Archive for the ‘iPhone’ Category.

Don’t forget to account for new lines in your iOS NSRegularExpression

The NSRegularExpression class is handy for searching and parsing strings. That is, it is handy until it starts to fail at seemingly random intervals.

I had a regex that was working fine on my small set of initial test data, but when I expanded it to work on a slightly larger and varied set of data, all of a sudden the weather turned a bit cloudy.

After some investigation on Stack Overflow and diving through the Apple documentation, I realized that I should probably add the NSRegularExpressionDotMatchesLineSeparators option to my NSRegularExpression, and the skies turned bright and sunny again.

Here is an example of what I am talking about. In this instance, I am looking to obtain the text in an NSString called theString that is in between the sentinel literals of =BeginTag= and =EndTag=:

NSString *pattern = @"=BeginTag=(.*?)=EndTag=";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern
    options:(NSRegularExpressionCaseInsensitive | NSRegularExpressionDotMatchesLineSeparators)
    error:nil];
[regex enumerateMatchesInString:theString options:0 range:NSMakeRange(0, [theString length])
    usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop)
    {
        NSString *s = [theString substringWithRange:[match rangeAtIndex:1]];
        NSLog(@"Enumerated matching string: %@", s);
    }];

If you leave off the NSRegularExpressionDotMatchesLineSeparators flag, and theString contains new line characters in between the sentinels, you end up with no text enumerated with the block.

BTW, Happy Birthday to Steven Spielberg, one of the true geniuses of our time.

How to build an app with iOS SDK 6.1 in Xcode 5

When you update to Xcode 5, you get all of the new Xcode and iOS 7 goodness, which is great if you are ready to handle that much goodness. If you have to maintain compatibility with older applications, it can be a problem as the iOS 7 SDK will wreak havoc with your application.

The easier path for now is to just set your project to specifically build against the iOS SDK 6.1 instead of just using the “Latest SDK” entry, and then to either copy or put a symbolic link in the Xcode 5 app bundle to the SDK living in the old Xcode 4.x bundle. Personally, I have found this Stack Overflow post to be handy:

How to point Xcode to my 10.6 SDK so it can be used as a “Base SDK”?

In addition, the answer by Rob Napier references a script that you can use to automatically put these symlinks back into the Xcode 5 bundle when you update the app:

Links Xcode SDKs from the /SDKs directory

EDIT on 12/21/2013: Keep in mind this all becomes a moot point when, on February 1, 2014, Apple will only allow apps to be published to the App Store that have been built with the iOS SDK 7.0 or newer (here’s the link to the article in the iOS Developer Center).

BTW, Happy Birthday to Jim Leyland, the baseball manager who I admired greatly from his time managing the Pittsburgh Pirates.

Getting a JSON string from an array of NSValue objects holding CGPoint structs

I am a big fan of SBJson, and let’s face it, who isn’t? (Thanks Stig!) It has served me well for a long time, and only occasionally do I have to do any real work when it comes to serializing and deserializing JSON.

My current needs required me to take an NSArray of NSValue objects that each hold a CGPoint value, and send that down the wire via JSON. Simply passing the array inside the proxyForJson method halts the processing, so as I looked into it further I discovered that I needed to set up a utility function to create an NSDictionary that could be handled by the SBJson framework. Here is the code that I am using:

+ (NSArray *)jsonCompatibleArrayOfCGPoints:(NSArray *)pts
{
    NSMutableArray *pointsArr = [NSMutableArray array];
    for (int idx = 0; idx < [pts count]; idx++)
    {
        CGPoint pt = [pts[idx] CGPointValue];
        [pointsArr addObject:@ { @"x" : [NSNumber numberWithFloat:pt.x], @"y" : [NSNumber numberWithFloat:pt.y] }];
    }
 
    return [NSArray arrayWithArray:pointsArr];
}

BTW, great sadness today as it is the anniversary of the untimely and far too early passing of Duane Allman, lost in a motorcycle accident this day in 1971.

Different Settings app entries for Debug vs. Release builds

So compiler directives can only take you so far when building your Objective-C iOS apps, especially when you have testers who want to keep switching things up. Rebuilding and redeploying is not the end of the world, but it gets old quick.

Luckily I figured out how to switch up the Settings.bundle file so that I can have one version for the release app, and a different version with some extra options for the debug build. This way, they don’t have to drop by every hour when they want to change something that would previously require me to comment in/out some compiler directive and rebuild and redeploy.

The theory behind this is to create a new Settings_Debug.bundle file in the same location as the current Settings.bundle file, change up the plist for your additional settings, and then in the build process, just replace the plist file in Settings.bundle with the one from the Settings_Debug.bundle file. Sounds easy, right?

Here is basically what you need to do step by step:

  • Create a new Settings_Debug.bundle file
  • Copy the plist from Settings.bundle and make the necessary modifications in the Settings_Debug.bundle
  • Click the project in file explorer, select target, select Build Phases
  • Leave release version Settings.bundle in the Copy Bundle Resources, but remove Settings_Debug.bundle from Copy Bundle Resources
  • Click Add Build Phase > Add Run Script
  • Copy following into text view below Shell
if [ "${CONFIGURATION}" == "Debug" ]; then
cp -r "${PROJECT_DIR}/Shared/Settings_Debug.bundle/Root.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/Settings.bundle/Root.plist"
echo "Debug settings bundle copied"
else
cp -r "${PROJECT_DIR}/Shared/Settings.bundle/Root.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/Settings.bundle/Root.plist"
echo "Release settings bundle copied"
fi

Here are a few caveats to keep in mind…

  • In my example above, note that my Settings.bundle file is in a folder called Shared off the project directory
  • I am not using the Root.strings file
  • It was done this way because just copying the .bundle file threw errors trying to copy the .svn files and folders
  • For me, it seems way easier to work with Root.plist in source code mode instead of property list mode

BTW, it was pretty slim pickings (not Slim Pickens, one of the great characters of all time) on the births, deaths, and events for September 21, so let me just wish everyone a Happy International Day of Peace.

Objective-C preprocessor paranoia

I am always deathly afraid of test code working its way into a release app. Usually for me this happens when I am trying to optimize a portion of the code by modifying something that is already there and working, which usually leads to something like this in my code during development:

//#define SAFE_OPTION_1
//#define RISKY_OPTION_2
#define DANGEROUS_OPTION_3

So finally I started to set it up so that I can verify that, at least for the archival build of the app, that the wrong option is not selected by using something like this:

#ifndef DEBUG_VERSION
 #if !defined(SAFE_OPTION_1)
  #error The release build of MyApp must have the SAFE_OPTION_1 option selected.
 #endif
#endif

This code will give me a build error if I forget to set the option back to the safe option, which is good.

Also, I wanted to make sure that one of the three options was selected, as commenting out all 3 options is not good, and may not necessarily generate any build errors on their own. So I then figured out that this would do the trick for me:

#if !defined(SAFE_OPTION_1) && !defined(RISKY_OPTION_2) && !defined(DANGEROUS_OPTION_3)
 #error Please select an option.
#endif

Now I get a build error when I accidentally have all 3 options commented out.

BTW, Happy 20th Birthday to the X-Files, which first aired on this date in 1993. I think it is about time for me to go back through and binge watch all 9 seasons of the show.

Centering the toolbarItems in a UINavigationController

When you add to the toolbarItems array of your UINavigationController, it puts the items left justified in the toolbar at the bottom of the screen. If you instead want the items to be centered in the toolbar, here is a way to do it…

NSArray *itemArray = @ [ @"Item 1", @"Item 2" ];
UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems:itemArray];
segmentedControl.frame = CGRectMake(0.0, 0.0, 240.0, 30.0);
segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;
[segmentedControl addTarget:self action:@selector(segmentedControlChanged:) forControlEvents:UIControlEventValueChanged];
UIBarButtonItem *item = [[[UIBarButtonItem alloc] initWithCustomView:segmentedControl] autorelease];
UIBarButtonItem *flex1 = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:NULL] autorelease];
UIBarButtonItem *flex2 = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:NULL] autorelease];
self.toolbarItems = @ [ flex1, item, flex2 ];
[segmentedControl release];

The flex1 and flex2 items are just like adding the springs in Interface Builder, which space things out equally across the view. This code is not ARC approved as you can see from the autorelease and release methods, but if you need to use it in an ARC project you should be able to figure it out.

BTW, it looks to be a thin day for actor or musician birthdays, deaths, events, or holidays, so Happy Birthday to President Obama.

External full screen UIViewController

While trying to write an app that drives an external monitor hooked up to my iPad, I found that you need to set the wantsFullScreenLayout to your view controller in order for it to go edge to edge. No combination of constraints or struts and springs in Interface Builder or playing with the frame and bounds in code does the job.

As an aside, if you get the message “Could not change executable permissions on the application.” in Xcode when you try to run an app, it is most likely because there is already an app on the device or simulator that has the same bundle identifier that you have specified in your code. To solve, just remove the offending app from the device or simulator and run again, and it should work.

BTW, Happy 30th Birthday to the Nintendo Entertainment System, released 30 years ago today in Japan as the Famicom. Ice Hockey is still one of my all time favorite games.

UISearchBar gotcha

I sort of discovered something quite by accident today.

I have had a persistent crash problem in my app for a long time, so I put in some code from the QuincyKit so that I could get the crash logs from devices when they encountered a problem. Unfortunately, the crash logs that I have been getting have been 99% unhelpful, as the only mention of my app is the main.m line shown at the bottom of the thread 0 stack trace.

In diagnosing a problem in another app today, it occurred to me that the problem in one of my apps could be related. My users have a UISearchBar on top of a table view of items, and they can enter some text to narrow down the item list. I of course am hooking into the textDidChange method in the UISearchBarDelegate protocol. However, at the direction of a specific customer with specific instructions, I discovered that if the auto correction parameter is turned on for the search bar, and some text is entered that has a suggestion below it, then the act of selecting a row from the filtered table view will cause the text in the search bar to be replaced with the suggested text, and then hilarity ensues as the textDidChange is fired again and the program returns a nil record instead of the one I just selected from the table view, since the table view switched in the textDidChange method.

So I corrected this issue in the original app, and it got the wheels turning. So I tried doing much the same thing on a device in my other app, and sure enough, my exception breakpoint in Xcode fires! (I think I heard some angels singing right about this time.) And just for good measure, I set it up so that it does not crash anymore in that instance, it just sits there and does nothing, which apparently is better than a crash.

Needless to say, from now on I am going to set all of my UISearchBar items to have the auto correction parameter set to No.

BTW, happy birthday to Michael Anthony of Chickenfoot fame. Oh, and he was in some band called Van Halen for a while too.

EDIT: I knew that my Spidey sense was detecting something familiar when I was putting together this post. Maybe I should read my own blog every once in a while…

Search bar and correction don’t play nice together

WWDC 2013 wrap up

Well I have been back home for a few days now from the WWDC in San Francisco, and I have to say that it was a super busy week. The content was very solid, and the lines were massive, but all in all worth the time and expense.

BTW, Happy Birthday to Mia Sara, IMHO the real star of Ferris Bueller’s Day Off.

UIImage categories (part 4 of 4)

And the final step of the process is to take the scaled image and center it in a rectangle. Here is the code for that:

- (UIImage *)imageCenteredInSize:(CGSize)newSize
{
    CGSize imageSize = self.size;
    CGFloat x = (newSize.width - imageSize.width) / 2.0;
    CGFloat y = (newSize.height - imageSize.height) / 2.0;
    CGFloat w = imageSize.width;
    CGFloat h = imageSize.height;
    UIGraphicsBeginImageContext(newSize);
    [self drawInRect:CGRectMake(x, y, w, h)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}

I believe that I found this code on Stack Overflow.

BTW, Happy Doughnut Day to everyone.