Reputation: 2809
In my iOS application, I am doing a heavy weight migration of an Entity, where I convert the type of an attribute from Integer64 in the old data model to a type of String in the new data model. The conversion of this attribute appears to be working fine. However, the problem I have run into is that another attribute of the same Entity is null (which is what it is supposed to be), and when this attribute is being migrated to the same entity in the new schema, an error is being flagged:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of value for attribute: property = "date"; desired type = NSDate; given type = NSNull; value = <null>.'
I am not sure why this error is being flagged, because the attribute is marked as optional in the data model. I would like to simply migrate the nil value of the attribute "as is" to the same attribute in the new data model without any changes or modifications.
Here is the relevant code of my subclass of NSEntityMigrationPolicy that I am using:
- (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *)sInstance entityMapping:(NSEntityMapping *)mapping manager:(NSMigrationManager *)manager error:(NSError *__autoreleasing *)error {
NSManagedObject *newObject;
NSEntityDescription *sourceInstanceEntity = [sInstance entity];
//correct entity? just to be sure
if ([[sourceInstanceEntity name] isEqualToString:@"MyEntity"]) {
newObject = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" inManagedObjectContext:[manager destinationContext]];
//obtain the attributes
NSDictionary *keyValDict = [sInstance committedValuesForKeys:nil];
NSArray *allKeys = [[[sInstance entity] attributesByName] allKeys];
//loop over the attributes
for (NSString *key in allKeys) {
//get key and value
id value = [keyValDict objectForKey:key];
if ([key isEqualToString:@"integerType"]) {
//here retrieve old value
NSNumber *oldValue = [keyValDict objectForKey:key];
//here do conversion as needed
NSString *stringType = [oldValue stringValue];
//then store new value
[newObject setValue:stringType forKey:key];
} else { //no need to modify the value, Copy it across -- this is where I believe the problem is
[newObject setValue:value forKey:key];
}
}
[manager associateSourceInstance:sInstance withDestinationInstance:newObject forEntityMapping:mapping];
}
return YES;
}
Can anyone see what it is I'm doing wrong?
Upvotes: 0
Views: 359
Reputation: 46718
The issue is that you are getting back a NSNull
class in your dictionary which means you are trying to pass the wrong type of class to the new NSManagedObject
instance.
If you read the documentation on -committedValuesForKeys:
you will see that:
nil
values are represented by an instance ofNSNull
.
Which is your problem.
Personally I would not approach the values this way. Instead I would do something like:
NSDictionary *allAttributes = [[sInstance entity] attributesByName];
for (NString *key in allAttributes) {
id value = [sInstance valueForKey:key];
if ([key isEqualToString:@"integerType"]) {
//here retrieve old value
NSNumber *oldValue = [keyValDict objectForKey:key];
//here do conversion as needed
NSString *stringType = [oldValue stringValue];
//then store new value
[newObject setValue:stringType forKey:key];
} else { //no need to modify the value, Copy it across -- this is where I believe the problem is
[newObject setValue:value forKey:key];
}
}
Whereby you are grabbing the values directly from the object and you will get a proper nil
back.
Upvotes: 1