iPhone - Beware of case sensitivity when enabling data model versioning

In an app I'm building, I decided it was about time to start thinking about data model versioning as a way to protect user data from database schema changes. Without versioning, every time I updated the app's schema, the user's data would have to be wiped out. Not good.

Any way, I consulted various sources on how to do this, including my beloved More iPhone 3 Development and Apple's Introduction to Core Data Model Versioning and Data Migration Programming Guide.

I made the minor changes to my code in the persistentStoreCoordinator and managedObjectModel, and tested in the Simulator. Great, nothing was broken, the changes worked just as advertised.
I continued coding, and a few hours later decided it was time to test on the device.
Bummer, I got this error message:

2011-02-10 14:23:12.470 myApp[9241:307] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSURL initFileURLWithPath:]: nil string parameter' 
*** Call stack at first throw: 

0 CoreFoundation 0x33ac0987 __exceptionPreprocess + 114 
1 libobjc.A.dylib 0x3347b49d objc_exception_throw + 24 
2 CoreFoundation 0x33ac07c9 +[NSException raise:format:arguments:] + 68 
3 CoreFoundation 0x33ac0803 +[NSException raise:format:] + 34 
4 Foundation 0x3362b54f -[NSURL(NSURL) initFileURLWithPath:] + 70 
5 Foundation 0x33650157 +[NSURL(NSURL) fileURLWithPath:] + 30 
6 myApp 0x00003537 -[myAppAppDelegate managedObjectModel] + 138 
7 myApp 0x000036fb -[myAppAppDelegate persistentStoreCoordinator] + 302

8 myApp 0x00003407 -[myApp AppDelegate managedObjectContext] + 62 

9 myApp 0x0000302f -[iNspectorAppDelegate application:didFinishLaunchingWithOptions:] + 78 
10 UIKit 0x3209ebc5 -[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 772 
11 UIKit 0x3209a259 -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 272 
12 UIKit 0x3206648b -[UIApplication handleEvent:withNewEvent:] + 1114 
13 UIKit 0x32065ec9 -[UIApplication sendEvent:] + 44 
14 UIKit 0x32065907 _UIApplicationHandleEvent + 5090 
15 GraphicsServices 0x33b0ef03 PurpleEventCallback + 666 
16 CoreFoundation 0x33a556ff __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 26 
17 CoreFoundation 0x33a556c3 __CFRunLoopDoSource1 + 166 
18 CoreFoundation 0x33a47f7d __CFRunLoopRun + 520 
19 CoreFoundation 0x33a47c87 CFRunLoopRunSpecific + 230 
20 CoreFoundation 0x33a47b8f CFRunLoopRunInMode + 58 
21 UIKit 0x32099309 -[UIApplication _run] + 380 
22 UIKit 0x32096e93 UIApplicationMain + 670 
23 myApp 0x00002fb3 main + 70 
24 myApp 0x00002f68 start + 40 

Tried again in the simulator, it worked.
Looking at the error trace, something was happening in the managedObjectModel function, and the error message in the first line indicated that the NSURL string was nil. The offending block of code was this:

NSString *path = [[NSBundle mainBundle] pathForResource:@"Datamodel" ofType:@"momd"];
NSLog(@"path: %@", path);
NSURL *momURL = [NSURL fileURLWithPath:path];

I added the NSLog line to print out the contents of the `path` variable, and discovered that it was not nil when running in the simulator, but nil when running on the device.

I went to have a look at the application assets in

/Users/peter/Library/Application Support/iPhone Simulator/4.2/Applications/F2854B27-4750-4DC8-8CB6-A22B036F5DC9\myApp.app

by right-clicking and selecting "Show Package Contents", and noticed that the data model versioning directory was named `DataModel.momd`.
However in my code, the path variable was `Datamodel` (loading a resource of type `momd`), whereas the directory containing the model versions was `DataModel`.
Yeap, this was another case of the simulator and the device behaving differently, this time in how they handle file name cases.

Once I changed my code to
NSString *path = [[NSBundle mainBundle] pathForResource:@"DataModel" ofType:@"momd"];

and ran on the device, it worked.

About

Twitter