Update UIAlertView to UIAlertController

UIAlertView and UIActionSheet have been deprecated in iOS8. They still work and I submitted all of my apps without changing anything, but you’ll need to do it sometime. Now that my apps are all updated for the new phones, I have some time to do these type of nice to have’s. At first glance it appears difficult to update, but it really isn’t that hard.

The first place I updated was in the AppDelegate. I have an alert that asks the user to either start a new session (with new scoring and word choice) or resume the current session (keeping all of the current settings). The old code runs in the applicationWillEnterForeground method.

 
- (void)applicationWillEnterForeground:(UIApplication *)application {
    
    if (self.window.rootViewController) {
        NSString *messageWithTitle = [NSString stringWithFormat:@"Do you want to resume playing %@ or start a new session?", GAME_NAME_TITLE];
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Welcome Back"
                                                        message:messageWithTitle
                                                       delegate:self
                                              cancelButtonTitle:@"Resume"
                                              otherButtonTitles: @"Start New Session",nil];
        [alert show];
    }
}

Because I want to continue to run the app in versions prior to iOS8, I need to change this to a conditional. The code makes use of a #define from my Project-Prefix.pch


#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)

Then I created two methods, one just has the code from the original alert and the other is new.
 
- (void)applicationWillEnterForeground:(UIApplication *)application {
    
    if (self.window.rootViewController) {

        if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) {
            [self displayUIAlertController];
        } else {
            [self displayUIAlertView];
        }
    }

This is the new code. The title is the first line of the alert. The message is the second and additional lines. What’s different about this from the old way is that you add actions using methods with completion blocks. You can do anything in a block that you normally would. I kept mine simple and just called a method. If was writing this just for iOS8, I’d probably put the contents of the method in the block. Note: I could have used UIAlertActionStyleCancel instead of UIAlertActionStyleDefault for the Resume button. If you use the UIAlertActionStyleCancel style, it always appears at the end and is bolded
 
- (void)displayUIAlertController {

    NSString *alertMessage = [NSString stringWithFormat:@"Do you want to resume playing %@ or start a new session?", GAME_NAME_TITLE];
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Welcome Back"
                                                                   message:alertMessage
                                                            preferredStyle:UIAlertControllerStyleAlert];

    // You can add as many actions as you want
    UIAlertAction *startNewSession = [UIAlertAction actionWithTitle:@"Start New Session" 
                                                              style:UIAlertActionStyleDefault
                                                            handler:^(UIAlertAction *action) {
       [self startNewSession];
    }];

    UIAlertAction *doNothingAction = [UIAlertAction actionWithTitle:@"Resume"
                                                              style:UIAlertActionStyleDefault
                                                            handler:^(UIAlertAction *action) {
                    // Do nothing
    }];

    // Add actions to the controller. The order here determines the order in the alert. The last one is bolded.
    [alert addAction:doNothingAction];
    [alert addAction:startNewSession];

    // Finally present the action
    [self.window.rootViewController presentViewController:alert animated:true completion:nil];
}

Normally you’d display the alert with

[self presentViewController:alert animated:YES completion:nil];

but since this is the app delegate you need to do it slightly different.

This is the old code, just put into a method.

 
- (void)displayUIAlertView {
    NSString *messageWithTitle = [NSString stringWithFormat:@"Do you want to resume playing %@ or start a new session?", GAME_NAME_TITLE];
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Welcome Back"
                                                    message:messageWithTitle
                                                   delegate:self
                                          cancelButtonTitle:@"Resume"
                                          otherButtonTitles: @"Start New Session",nil];
    [alert show];
}

I pulled the code out of the buttonIndex1 section and created a new method that is called by both versions when a new session is started.

 
#pragma mark - Alert on restart
// buttonIndex 0 is cancel and the game continues
// buttonIndex 1 is Start New Session and the old results are saved and new session started
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    if (buttonIndex == 1) {
        [self startNewSession];
    }
}

- (void)startNewSession {

    // Delete current results
    [Utilities copyCachedResultsToFile];
    [Utilities removeFileFromCache:@"Results.txt"];
    // When deciding whether to start a new table, compares the current scoringType to previousScoringType
    [Globals sharedInstance].previousScoringType = @"";
    [self.navigationController popToRootViewControllerAnimated:YES];
}

And this is what it looks like.

UIAlertControler example

Initializing Booleans

I have a globals method that I use to keep track of global values in my apps. It is mostly for options. I override the getter like this. In this example, if trials per round is not set, I initialize it to the #defined value for that app.


- (NSUInteger)trialsPerRound {
    
    if ( !_trialsPerRound ) _trialsPerRound = TRIALS_PER_ROUND;
    return _trialsPerRound;
}

I tried to do the same thing for some Booleans, but ran into a problem.


- (BOOL)reviewIgnored {
    
    if ( !_reviewIgnored ) _reviewIgnored = YES;
    return _reviewIgnored;
}

I wanted the reviewIgnored value to start at YES if it was not set. But what happened is that when I would change it to NO in the options page it would be fine. But when I called it in my if statement


  if (![Globals sharedInstance].reviewIgnored) {

what happened is that the getter checks its value, sees that it is NO. The if statement says to change reviewIgnored to YES and I get the wrong behaviour.

What I did was put all of my Boolean initialization in th AppDelegate.m file. Problem solved.

Things I can’t remember: More GREP goodness

When cleaning up mailing lists, I often need to remove the zip+4 info. It is usually at the end of a line so this grep works in BBEdit to find them.


-[0-9]{4}$

Look for a dash followed by the numbers 0-9 four times and then an end of line.

Working on a word list, we needed to find and delete all of the words with 1, 2, or 3 letters. So we use this code switching out the 1 for 2 and 3. Note the ^ and $. This means that we start at the beginning of the line look for the pattern and nothing else until the end of the line.


^[a-zA-Z]{1}$

We could use the same technique to find all of the words with more than 5 letters, but then we’d have to do a bunch of searches. Instead we used this.


[a-zA-Z]{5}[a-zA-Z]

Here we look for pattern of five letters and then look for one more. Not to belabor the point, but suppose together is in the word list. It has five letters, toget and one more h. It also happens to have some more letters after that, but we don’t particularly care. We just care that there are more than 5.

ON DUPLICATE KEY UPDATE

When processing addresses for postcard mailings we get a lot of addresses that don’t pass the VistaPrint address validation process. Fortunately, VistaPrint has a button that lets you download the addresses. What makes it easy for me to mark the bad addresses in my database is that I don’t use a salutation and instead put the customer number in the salutation field. I strip out all of the address information and keep just the customer ID. I then massage the IDs so that they look like this.


INSERT INTO `customers` (id, BadVistaPrint) VALUES 
(15771, 1), 
...
(16248, 1), 
(16249, 1)
ON DUPLICATE KEY UPDATE BadVistaPrint=VALUES(BadVistaPrint)

What this code does is look for customer ID and then update the BadVistaPrint value. Not sure why it uses the ON DUPLICATE KEY syntax. Note that the last row before the ON DUPLICATE KEY does not have a comma.

I paste this into the SQL section of phpMyAdmin and I my database is now updated with bad addresses. I can manually look for easy to fix addresses e.g. missing numbers, zip codes, etc. and look them up. There are lots of addresses that are mailable but not in the VistaPrint database e.g. University departments and rural route addresses and I mail them separately.

I also use the USPS zip code finder to validate addresses.

Improving legibility of OSX Yosemite

The latest update to OSX incorporates design elements from iOS7. If you hate iOS7, you’re going to really hate Yosemite.

However, there are a couple of things you can do to restore screen legibility. Open up the Accessibility preference pane in System Preferences. Click on the checkbox labelled increase contrast. It automatically checks the box for Reduce Transparency. This does two things. First, it stops background colors from bleeding through menu bars and option panels. Second, it darkens the text so it is more legible.

That still isn’t enough for my non-retina displays, so I also move the Display Contrast up to the first tick mark. Finally, on the physical monitor controls I reduced the brightness a bit. I’ve been using it all morning and the text is readable. Unfortunately, there is no way to fix the contrast on buttons so that they are more readily identifiable, but I suppose I’ll get used to it.

Safari:
I like to see the entire URL when maintaining my websites, so in the Advanced Safari preferences I check, “Show full website address”. In the security section I turned off “Autofill web forms using: Credit cards”. Then under the View menu item, I turned on “Show Tab Bar” and “Show Status Bar”.

Dock:
In the Dock preference pane, I disable “Double-click a window’s title bar to minimize”. Prevents you from accidentally disappearing windows. This isn’t a new feature, but I just found out how to disable it.

My usual method for pinning the dock the bottom RHS corner doesn’t work, so if anyone figures out how to do it, pls let me know.

Finder:
In the General preference pane, change “Sidebar icon size” to large. Improves legibility.

Lots of other annoying things I’ll probably get used to, though it would be nice if someone figures out how to change the folder color in the Finder from the garish blue. The good news is that everything I’ve tried so far works just as before—including all the software I wrote.