syedfa
syedfa

Reputation: 2809

Trying to migrate nil attribute value when doing heavy weight migration in Core Data

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

Answers (1)

Marcus S. Zarra
Marcus S. Zarra

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 of NSNull.

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

Related Questions