Why isn't NSManagedObjectModel getting loaded? - core-data

I can load my model with mergedModelFromBundles: just fine. But when I try to initialize them manually with initWithContentsOfURL:, they are nil.
I share them in 2 projects. This load code is in one file shared in the projects as well. Everything works as expect in the first project but the second project is giving me issues.
NSMutableArray *models = [NSMutableArray array];
NSString *path = [[NSBundle mainBundle] pathForResource:#"MyModel" ofType:#"momd"];
if(![[NSFileManager defaultManager] fileExistsAtPath:path]){
NSLog(#"Compiled MyModel.momd file not found.");//never gets called, file is there
}
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL: [NSURL URLWithString:path] ];
if(!model){
//this is getting called which indicates there is something wrong with the coredata file...?
NSLog(#"Could not load compiled MyModel.momd file.");
}else{
[models addObject:toolKitModel];
}
//works in project 1 where initWithContentsOfURL: returns nil but not in project 2
_managedObjectModel = [NSManagedObjectModel mergedModelFromBundles: NULL ];
//works in project 2 where initWithContentsOfURL: returns the NSManagedObjectModel but not in project 1
_managedObjectModel = [NSManagedObjectModel modelByMergingModels:models ];the models
Any ideas why the NSManagedObjectModel won't load with initWithContentsOfURL: and modelByMergingModels:?

Related

Migrate core data to add new attribute

I have added new attribute and I need to migrate core data. As a result, I do like this.
Bus.xcDataModel >> Add model version
Then, I add new attribute. I change persistent store coordinator option as well like this.
#{NSMigratePersistentStoresAutomaticallyOption:#YES, NSInferMappingModelAutomaticallyOption:#YES}
But when I add that new attribute, it show me like this. How shall I do?
-[BusService setBus_wap:]: unrecognized selector sent to instance 0x1742a7320
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator_busservice
{
if (_persistentStoreCoordinator_busservice != nil)
return _persistentStoreCoordinator_busservice;
NSURL *applicationDocumentsDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *storeURL = [applicationDocumentsDirectory URLByAppendingPathComponent:#"Busservice_new.sqlite"];
if (![[NSFileManager defaultManager] fileExistsAtPath:[storeURL path]] &&
!self.preloadEnabled)
{
NSURL *preloadURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"Busservice_new" ofType:#"sqlite"]];
NSError* err = nil;
if (![[NSFileManager defaultManager] copyItemAtURL:preloadURL toURL:storeURL error:&err])
DLog(#"Oops, could not copy preloaded data");
}
NSError *error = nil;
_persistentStoreCoordinator_busservice = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator_busservice addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:#{NSMigratePersistentStoresAutomaticallyOption:#YES, NSInferMappingModelAutomaticallyOption:#YES} error:&error])
{
DLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return _persistentStoreCoordinator_busservice;
}
From that error it looks like you added a new attribute to your BusService entity in Core Data, but did not add the attribute to your BusService class. If you want to use accessor methods for this new attribute, you need to update the class as well. Or you can leave the class alone but use setValue:forKey: to assign values to the new attribute.

Automatic lightweight migration works for local storage but iCloud storage "loses" all legacy data

I'm tearing my hair out with this one.
I've got an App on iTunes which I added iCloud support to end of last year (Oct '13) on iOS7.0
This week I decided to write a new functional for the App which requires a new entity in the xcdatamodel. A very simple change/addition. Should have no impact on the current data set.
I create a new v2 xcdatamodel and set it to Current Model version, compile and run and it works fine if I've got iCloud switch off on my iPad. I see previous saved data.
Run it again with iCloud switch on and I get a blank table with no data.
No error messages, nothing.
Hoping someone can throw some light on what I've done wrong here:
- (NSManagedObjectModel *)managedObjectModel {
if (__managedObjectModel != nil) {
return __managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"UserData" withExtension:#"momd"];
__managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return __managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
NSError *error = nil;
BOOL success = NO;
if((__persistentStoreCoordinator != nil)) {
return __persistentStoreCoordinator;
}
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
NSPersistentStoreCoordinator *psc = __persistentStoreCoordinator;
NSString *iCloudEnabledAppID = #"C3FUPX46ZG~com~software~App";
NSString *dataFileName = #"UserData.sqlite";
NSString *iCloudDataDirectoryName = #"CoreData.nosync";
NSString *iCloudLogsDirectoryName = #"CoreDataLogs";
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *localStore = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:dataFileName];
NSURL *iCloud = [fileManager URLForUbiquityContainerIdentifier:nil];
if (iCloud && ([UserDefaults getIsiCloudOn])) {
// This iCloud storage fails to migrate.
NSURL *iCloudLogsPath = [NSURL fileURLWithPath:[[iCloud path] stringByAppendingPathComponent:iCloudLogsDirectoryName]];
if([fileManager fileExistsAtPath:[[iCloud path] stringByAppendingPathComponent:iCloudDataDirectoryName]] == NO) {
NSError *fileSystemError;
[fileManager createDirectoryAtPath:[[iCloud path] stringByAppendingPathComponent:iCloudDataDirectoryName]
withIntermediateDirectories:YES
attributes:nil
error:&fileSystemError];
if(fileSystemError != nil) {
NSLog(#"Error creating database directory %#", fileSystemError);
}
}
NSString *iCloudData = [[[iCloud path]
stringByAppendingPathComponent:iCloudDataDirectoryName]
stringByAppendingPathComponent:dataFileName];
NSDictionary *options = #{NSMigratePersistentStoresAutomaticallyOption : #YES,
NSInferMappingModelAutomaticallyOption : #YES,
NSPersistentStoreUbiquitousContentNameKey : iCloudEnabledAppID,
NSPersistentStoreUbiquitousContentURLKey : iCloudLogsPath
};
success = [psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:[NSURL fileURLWithPath:iCloudData]
options:options
error:&error];
if (!success) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
} else {
// This local storage migrates automatically just fine.
NSDictionary *options = #{NSMigratePersistentStoresAutomaticallyOption : #YES,
NSInferMappingModelAutomaticallyOption : #YES
};
success = [psc addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:localStore
options:options
error:&error];
if (!success) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:kCoreDataChangeNotification object:self userInfo:nil];
});
return __persistentStoreCoordinator;
}
UPDATE: Switched on core data debugging & iCloud debugging/logging.
Migration works for both local & iCloud. Logs are the same, ending with:
CoreData: annotation: (migration) inferring a mapping model between data models with...
CoreData: annotation: (migration) in-place migration completed succeessfully in 0.03 seconds
-PFUbiquitySwitchboardEntryMetadata setUseLocalStorage:: CoreData: Ubiquity: mobile~F9AC6EB1
Using local storage: 1
With iCloud storage & debugging on it seems to cause a delay and I briefly see my saved data for about 10seconds when it then disappears.
Just before it disappears the debugs spit out:
CoreData: annotation: (migration) inferring a mapping model between data models with...
Using local storage: 0
The iCloud logs are enormous which is why I'm not posting them here. From what I can see I have over 400 log files and iCloud seems to be doing some sort of syncing. If I leave the App and iPad open and on for a few hours I still see an empty data set. So it's not a case of waiting for a sync catch up. I'm still at a loss even with the debugs on....

Load a previous model version

I am loading a NSManagedObjectModel model with the initWithContentsOfURL: constructor like this:
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"MyDocument" withExtension:#"momd"];
NSManagedObjectModel *mom = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
However this only gives me access to the latest/current version of a model. Is it posible to load previous versions with the same momd file? how?
Actually you can load an older version with:
- (NSManagedObjectModel *)managedObjectModelForVersion:(NSString *)version
{
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:[NSString stringWithFormat:#"AppModel.momd/AppModel %#",version] withExtension:#"mom"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return model;
}
Just replace AppModel with your model name.
I'm using this to get myself out of a sticky manual migration situation involving iCloud. Searched high and low and couldn't find this anywhere.
If you just want to load the version of the model that's compatible with a particular existing store try:
NSError *error = nil;
NSDictionary *storeMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType
URL:storeURL
error:&error];
NSManagedObjectModel *oldManagedObjectModel = [NSManagedObjectModel mergedModelFromBundles:[NSArray arrayWithObject:[NSBundle mainBundle]]
forStoreMetadata:storeMetadata];
Note that if you use XCode version identifiers for your data model versions, the persistent store's current version identifiers are accessible through the NSStoreModelVersionIdentifiersKey entry in the store metadata dictionary.
As far as loading a particular arbitrary version is concerned, the mom files are typically located under the momd directory in your app's bundle, so you could enumerate them using NSFileManager. I believe to find one with a particular version identifier you would have to use NSManagedObjectModel's initWithContentsOfURL: initializer and then inspect the versionIdentifiers property, or use the isConfiguration:compatibleWithStoreMetadata: instance method to determine compatibility.
Made the solution offered by #Schoob into a category, because they rock.
#interface NSManagedObjectModel (version)
+ (NSManagedObjectModel *)modelFromBundle:(NSBundle *)bundle name:(NSString *)modelName version:(NSString *)version;
#end
#implementation NSManagedObjectModel (version)
+ (NSManagedObjectModel *)modelFromBundle:(NSBundle *)bundle name:(NSString *)modelName version:(NSString *)version
{
if(!bundle)
bundle = [NSBundle mainBundle];
NSString *resource = [[modelName stringByAppendingPathExtension:#"momd"] stringByAppendingPathComponent:version];
NSURL *modelURL = [bundle URLForResource:resource withExtension:#"mom"];
NSAssert(modelURL,#"Unable to find MOM - %#",resource);
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
NSAssert(model,#"Unable to load MOM at URL- %#",modelURL);
return model;
}
#end
Swift version. Replace file name.
import CoreData
extension NSManagedObjectModel
{
class func model(forVersion version: Int) -> NSManagedObjectModel?
{
if let fileUrl = Bundle.main.url(forResource: "Model.momd/Model \(version)", withExtension: "mom")
{
return NSManagedObjectModel(contentsOf: fileUrl)
}
return .none
}
}
No, it is not foreseen that this is possible. I deduce that from the NSManagedObjectModel documentation, where it says discussing the property versionIdentifiers:
This value is meant to be used as a debugging hint to help you determine the models that were combined to create a merged model.
So it does not seem you are supposed to use previous model versions for your program logic.

Strange Core Data Error During Migration

I'm absolutely pulling my hair out with Core Data after yet another bizarre error that I can't seem to solve.
This will be the fourth version of the Data model, the previous migrations have worked (albeit with some headaches).
All I'm trying to do is add a property of String type to an 'Engines' entity. I create a new version of the model (version 4) based on the current version (v3). I select the newly created version 4 as the 'Current model' and add the string property to the Engines entity. I then create a new mapping model, using v3 as the source and v4 as the target. I delete the previous Engines NSManagedObject subclass, and create a new one using the new, modified Engines entity, checking to make sure that the new String property is in the header file. I clean build the app and run it, and boom! I get this error, about 18 times:
{NSDetailedErrors=(
"Error Domain=NSCocoaErrorDomain Code=1570 \"The operation couldn\U2019t be completed. (Cocoa error 1570.)\" UserInfo=0x6015820 {NSValidationErrorObject= (entity: BodySet; id: 0x60ba670 ; data: ), NSValidationErrorKey=availableCar, NSLocalizedDescription=The operation couldn\U2019t be completed. (Cocoa error 1570.)}",
BodySet is another entity in the model, but I haven't touched it during this migration, so why is it causing all these errors?
I'm not sure if this is a help or not, but here's my Core Data code:
- (NSManagedObjectContext *) managedObjectContext {
if (managedObjectContext != nil) {
return managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
managedObjectContext = [[NSManagedObjectContext alloc] init];
[managedObjectContext setPersistentStoreCoordinator: coordinator];
}
return managedObjectContext;
}
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel != nil) {
return managedObjectModel;
}
NSString *path = [[NSBundle mainBundle] pathForResource:#"CoreDataTest" ofType:#"momd"];
NSURL *momURL = [NSURL fileURLWithPath:path];
managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:momURL];
return managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
stringByAppendingPathComponent:#"<Project Name>.sqlite"]];
//get the DB from the Documents directory:
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory]
stringByAppendingPathComponent: #"<Project Name>.sqlite"]];
NSLog(#"Loading DB at path: %#", [storeUrl path]);
NSError *error = nil;
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel:[self managedObjectModel]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if(![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil URL:storeUrl options:options error:&error]) {
/*Error for store creation should be handled in here*/
NSLog(#"Something went wrong....%#", [error description]);
}
return persistentStoreCoordinator;
}
Any help with this is very much appreciated.
i have fixed it ,maybe you can change "availableCar“ item to optional

CoreData "Error validating url for store"

I'm having a problem in my application, CoreData works as it should in he simulator - but not on the device.
I receive an
2010-09-30 12:45:07.500 CoreDataTutorial_iOS[130:307] Unresolved error Error Domain=NSCocoaErrorDomain Code=513 "The operation couldn’t be completed. (Cocoa error 513.)" UserInfo=0x1412a0 {NSUnderlyingException=Error validating url for store}, {
NSUnderlyingException = "Error validating url for store";
I'm calling for the PersistentStoreCoordinator in this function (which throws the error above):
-(NSPersistentStoreCoordinator*)persistentStoreCoordinator
{
if(persistentStoreCoordinator_ != nil)
return persistentStoreCoordinator_;
NSURL *aStoreURL = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingFormat:#"corebase.sqlite"]];
NSError *anError = nil;
persistentStoreCoordinator_ = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if(![persistentStoreCoordinator_ addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:aStoreURL options:nil error:&anError])
{
NSLog(#"Unresolved error %#, %#", anError, [anError userInfo]);
abort();
}
return persistentStoreCoordinator;
}
I'm setting a break point, on "objc_exception_throw", to see what the aStoreURL is, and it is:
file://localhost/var/mobile/Applications/BE9A2982-BDC3-405D-A201-FB78E9E0790B/Documentscorebase.sqlite
I notice it's not itself adding the final "/" after "/Documents".
When I created the URL this way
NSURL *aStoreURL = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingFormat:#"/corebase.sqlite"]];
It seems to have worked, or at least got passed that part.
Shouldn't this function be appending that part itself?
-(NSString*) applicationDocumentsDirectory
{
return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
}
It works fine in the simulator, what is the right practice?
NSURL *aStoreURL = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory]
stringByAppendingFormat: #"corebase.sqlite"]];
should be
NSURL *aStoreURL = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory]
stringByAppendingPathComponent: #"corebase.sqlite"]];
stringByAppending*PathComponent* instead of stringByAppending*Format*.
This nice little bug was brought to you by autocomplete :-)
Why it worked in the simulator? I guess because you are allowed to create files everywhere on the harddisk. So the Simulator created Documentscorebase.sqlite in your Apps Directory. You should check if it's there.
On the iphone you are limited to the Documents directory and are not allowed to create files everywhere.

Resources