3

I'm trying to do a lightweight migration. I made a new project following step by step this tutorial and I get things work. But now I'm in another app and everything seems go well but when I try to access to the new field then I get the following error.

[Promocion setEnlace:]: unrecognized selector sent to instance 0x7fafe40bceb0

I have selected the correct model. enter image description here

This is the model Promocion.h

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>

/*
@interface Promocion : NSManagedObject

@property (nonatomic, retain) NSNumber * id_promocion;
@property (nonatomic, retain) NSString * descripcion;
@property (nonatomic, retain) NSString * titulo;
@property (nonatomic, retain) NSString * url;
@property (nonatomic, retain) NSString * fecha_inicio;
@property (nonatomic, retain) NSString * fecha_fin;

@end
 */

NS_ASSUME_NONNULL_BEGIN

@interface Promocion : NSManagedObject

// Insert code here to declare functionality of your managed object subclass

//+(id)personaWithContext:(NSManagedObjectContext *)context;

@end

NS_ASSUME_NONNULL_END

#import "Promocion+CoreDataProperties.h"

Promocion.m

#import "Promocion.h"


@implementation Promocion


- (NSComparisonResult)compare:(Promocion *)otherObject {

    NSDateFormatter *dateformat = [[NSDateFormatter alloc] init];
    [dateformat setDateFormat:@"MMMM dd,eeee HH:mm a z"];
    NSDate *dateA = [dateformat dateFromString:self.fecha_inicio];
    NSDate *dateB = [dateformat dateFromString:otherObject.fecha_inicio];

    return [dateA compare:dateB];
}

@end

And the following were auto-generated files Promocion+CoreDataProperties.h

#import "Promocion.h"

NS_ASSUME_NONNULL_BEGIN

@interface Promocion (CoreDataProperties)

@property (nullable, nonatomic, retain) NSString *descripcion;
@property (nullable, nonatomic, retain) NSString *fecha_fin;
@property (nullable, nonatomic, retain) NSString *fecha_inicio;
@property (nullable, nonatomic, retain) NSNumber *id_promocion;
@property (nullable, nonatomic, retain) NSString *titulo;
@property (nullable, nonatomic, retain) NSString *url;
@property (nullable, nonatomic, retain) NSString *enlace;

@end

NS_ASSUME_NONNULL_END

Promocion+CoreDataProperties.m #import "Promocion+CoreDataProperties.h"

@implementation Promocion (CoreDataProperties)

@dynamic descripcion;
@dynamic fecha_fin;
@dynamic fecha_inicio;
@dynamic id_promocion;
@dynamic titulo;
@dynamic url;
@dynamic enlace;

@end

AppDelegate.m

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }

    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"data.sqlite"];

    //Migration
    NSDictionary *options = @{
                              NSMigratePersistentStoresAutomaticallyOption  :@YES,
                              NSInferMappingModelAutomaticallyOption        :@YES
                              };

    NSError *error = nil;
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                   configuration:nil
                                                             URL:storeURL
                                                        //options:nil
                                                         options:options//automatic migration
                                                           error:&error]) {
        /*
         Replace this implementation with code to handle the error appropriately.

         abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

         Typical reasons for an error here include:
         * The persistent store is not accessible;
         * The schema for the persistent store is incompatible with current managed object model.
         Check the error message to determine what the actual problem was.


         If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.

         If you encounter schema incompatibility errors during development, you can reduce their frequency by:
         * Simply deleting the existing store:
         [[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]

         * Performing automatic lightweight migration by passing the following dictionary as the options parameter:
         @{NSMigratePersistentStoresAutomaticallyOption:@YES, NSInferMappingModelAutomaticallyOption:@YES}

         Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.

         */
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        //abort();
    }

    return _persistentStoreCoordinator;
}

Additional info

I have updated the version of the project.

Where I access the field called "enlace"

NSString* className = NSStringFromClass([Promocion class]);
NSEntityDescription *entity = [NSEntityDescription entityForName:className inManagedObjectContext:context];
Promocion* obj = (Promocion*)[[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:context];

obj.id_promocion = [NSNumber numberWithInt:[[centro objectForKey:@"id_promo"] intValue]];
obj.titulo = [centro objectForKey:@"titulo"];
obj.descripcion = [centro objectForKey:@"descripcion"];
obj.url = [centro objectForKey:@"imagen"];
obj.fecha_inicio = [centro objectForKey:@"fecha_ini"];
obj.fecha_fin = [centro objectForKey:@"fecha_fin"];
obj.enlace = [centro objectForKey:@"enlace"];

What I've tried

I have changed Promocion.h to avoid use Promocion+CoreDataProperties.h but I still have the same error.

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>

@interface Promocion : NSManagedObject

@property (nonatomic, retain) NSNumber * id_promocion;
@property (nonatomic, retain) NSString * descripcion;
@property (nonatomic, retain) NSString * titulo;
@property (nonatomic, retain) NSString * url;
@property (nonatomic, retain) NSString * fecha_inicio;
@property (nonatomic, retain) NSString * fecha_fin;
@property (nonatomic, retain) NSString * enlace;

@end

Another attempt

I recreate Promocion.h deleting the old one and creating a new one using Editor->Create NSOjecteManaged subclass. It looks like is working. But when I rollback using git to a last not updated core data commit and install it on a device and then upgrading the app with the last commit with new coredata model then start failing again. Damn I feel lot of frustration.

Partial Appdelegate.m

#pragma mark - Core Data stack

// Returns the managed object context for the application.
// If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
- (NSManagedObjectContext *)managedObjectContext
{
    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        _managedObjectContext = [[NSManagedObjectContext alloc] init];
        [_managedObjectContext setPersistentStoreCoordinator:coordinator];
    }
    return _managedObjectContext;
}

// Returns the managed object model for the application.
// If the model doesn't already exist, it is created from the application's model.
- (NSManagedObjectModel *)managedObjectModel
{
    if (_managedObjectModel != nil) {
        return _managedObjectModel;
    }
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"DataModel" withExtension:@"momd"];
    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return _managedObjectModel;
}

// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the application's store added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }

    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"data.sqlite"];

    //Migration
    NSDictionary *options = @{
                              NSMigratePersistentStoresAutomaticallyOption  :@YES,
                              NSInferMappingModelAutomaticallyOption        :@YES
                              };

    NSError *error = nil;
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                   configuration:nil
                                                             URL:storeURL
                                                        //options:nil
                                                         options:options//automatic migration
                                                           error:&error]) {
        /*
         Replace this implementation with code to handle the error appropriately.

         abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

         Typical reasons for an error here include:
         * The persistent store is not accessible;
         * The schema for the persistent store is incompatible with current managed object model.
         Check the error message to determine what the actual problem was.


         If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.

         If you encounter schema incompatibility errors during development, you can reduce their frequency by:
         * Simply deleting the existing store:
         [[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]

         * Performing automatic lightweight migration by passing the following dictionary as the options parameter:
         @{NSMigratePersistentStoresAutomaticallyOption:@YES, NSInferMappingModelAutomaticallyOption:@YES}

         Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.

         */
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        //abort();
    }

    return _persistentStoreCoordinator;
}

Solution

What finally was going wrong is that I had two references(dunno why) of Coredata file. One of them was still on first version. I have changed that version and everything works as expected.

Ricardo
  • 7,921
  • 14
  • 64
  • 111

4 Answers4

3

I think that your bug is not related to lightweight-migration, since your application does't crash-OnLaunch (as for any structure changes without migration) but it provide you with a runtime error mainly related to your model-class.

I suspect that your code still use a old generated model-class or a old category on that old class without that missing attribute: ( #import "Promocion.h" or #import "Promocion+creation.h").

This is potentially true if your coreData & managedObjects-subclass had been created using a previous version of Xcode before Xcode 7. An easy check is to cmd+tap on any #import "Promocion.h" to see contents.

If is it the case here is a check list to fix the problem (else this may still help other facing similar issue) :

-Search and delete(or rename!) Promocion.h :old generated file that contains only dynamic attributes (generated using a version previous to Xcode7 version see details below).

-Check if a category had been created on that class.."Promocion+Xyz.h" and change all reference #import "Promocion+Xyz.h" to #import "Promocion.h"

-Recreate your managedObject subclass.

Context

Before Xcode7 whenever we create a managedObject subclass the .h contains dynamic attributes and we used to create a category on that class to add methods and meanwhile to be able to regenerate our managedObject subclass without losing our created methods since they are in a category.

From Xocde7 Apple reviewed that and now dynamic attibutes are in ClassName+CoreDataProperties.h to make it easy to work/add methods on the main class without the need to add a category, and meanwhile to be able to regenerate managedObject since only ClassName+CoreDataProperties.h is recreated.

So now whenver we recreate a managedObject subclass that previously was created by the old way we need to make sure that we reference #import "Promocion.h" the right file!.

Idali
  • 1,023
  • 7
  • 10
  • I'm start this development with Xcode 6 but now I'm with Xcode 7. However I tried ´cmd+tap´ and I discover a circular reference between Promocion.h and Promocion+CoreDataProperties.h . But i still don´t get how solve it. – Ricardo Jun 05 '16 at 11:12
  • Anyway, I only have one Promocion.h file so I don't understand how can I mess references. – Ricardo Jun 05 '16 at 11:23
  • Please show your [self managedObjectModel] method. in case you are using mergedModelFromBundles try to changed with nitWithContentsOfURL: NSString *path = [[NSBundle mainBundle] pathForResource:@"Model" ofType:@"momd"]; NSURL *url = [NSURL fileURLWithPath:path]; NSManagedObjectModel *managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:url]; – Idali Jun 07 '16 at 16:28
  • I have update the question with snippet from AppDelegate. Could you update the answer? – Ricardo Jun 08 '16 at 13:50
  • It seems you are already using initWithContentsOfURL:url instead of mergedModelFromBundles... – Idali Jun 08 '16 at 14:01
  • Since migration from DataModel.xcdatamodel to DataModel2.xcdatamodel works using your current code version, but fails with a old code version Make sure that DataModel.xcdatamodel are exact same. you can use git diff to check http://blog.chaitanyagupta.com/2012/05/git-diff-driver-for-core-data-model.html – Idali Jun 08 '16 at 15:04
  • I tried mergedModelFromBundles an I get the same error `(+[DataParser fetchPromos]) (DataParser.m:65) exception = -[Promocion setEnlace:]: unrecognized selector sent to instance 0x7fd2438b4b90` – Ricardo Jun 14 '16 at 10:48
  • I have checked the differences between datamodel (first version) from premigration and the postmigration and they are identicals. – Ricardo Jun 14 '16 at 11:25
  • Please test the migration on device and provide the full error description. you can create another SO question specific to that crash log to have more audience. and give us a link t it. you can also try adding NSSQLitePragmasOption – Idali Jun 14 '16 at 12:45
0

It reads like the migration is not occurring and while the class files are probably correct, they are all relying on the underlying NSManagedObject to have the property.

Can you get a sqlite file from a device that has been updated to Model 2 and check the structure of the sqlite file and see if the new property is in place? If it isn't then this would point to the migration not firing and perhaps the wrong version of the model being defined as current.

Marcus S. Zarra
  • 46,571
  • 9
  • 101
  • 182
0

What finally was going wrong is that I had two references(dunno why) of Coredata file. One of them was still on first version. I have changed that version and everything works as expected.

Ricardo
  • 7,921
  • 14
  • 64
  • 111
-1

Try changing the line in your AppDelegate's datamodel function:

NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"DataModel" withExtension:@"momd"];

with

NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"DataModel 2" withExtension:@"mom"];
quarac
  • 3,454
  • 3
  • 21
  • 25
  • This is not working, I got this message `CoreData: Cannot load NSManagedObjectModel. nil is an illegal URL parameter` – Ricardo Jun 12 '16 at 18:58