CoreData migration issues
Asked Answered
A

3

6

I am currently writing the next version of an app.

In the Old version, there was no CoreData sqlite

In the New version, we have a local CoreData sqlite database.

When I install the new version of the application from scratch, there is no problems, the store is there and I can query.

However, when I install the app on phone that has the previous version on it, my queries come back with no results.

When I look at the logs, there is nothing in the console, and no errors are returned.

   #import "CoreDataHelper.h"

@implementation CoreDataHelper
@synthesize store = _store;
@synthesize coordinator = _coordinator;


#pragma mark - 
#pragma mark - FILES
NSString *storeFileName = @"Reporting.sqlite";

#pragma mark - 
#pragma mark - PATHS
- (NSString *)applicationDocumentsDirectory {

    return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

}

- (NSURL *)applicationStoresDirectory {

    NSURL *storesDirectory = [[NSURL fileURLWithPath:[self applicationDocumentsDirectory]]URLByAppendingPathComponent:@"Stores"];

    NSFileManager *fileManager = [NSFileManager defaultManager];

    if (![fileManager fileExistsAtPath:[storesDirectory path]]) {
        NSError *error = nil;
        if ([fileManager createDirectoryAtURL:storesDirectory
                   withIntermediateDirectories:YES
                                    attributes:nil
                                         error:&error]) {
            //File created
        } else {

            //Error
        }
    }

    return storesDirectory;

}

- (NSURL *)storeURL {

    return [[self applicationStoresDirectory] URLByAppendingPathComponent:storeFileName];

}

#pragma mark - 
#pragma mark - SETUP
- (id)init {

    if (self = [super init]) {
        _model = [NSManagedObjectModel mergedModelFromBundles:nil];
        _coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:_model];
        _context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        [_context setPersistentStoreCoordinator:_coordinator];
    }

    return self;
}

- (void)loadStore {

    if (_store) return; // Don't load store if it is already loaded

    // to generate the database in the app launching comment next lines...

    if(![self getFileExistence:storeFileName]){
        // file URL in our bundle
        NSURL *fileFromBundle = [[NSBundle mainBundle]URLForResource:@"FaultReporting" withExtension:@"sqlite"];

        // Destination URL
        NSURL *destinationURL = [[self applicationStoresDirectory] URLByAppendingPathComponent:@"FaultReporting.sqlite"];

        // copy it over
        [[NSFileManager defaultManager]copyItemAtURL:fileFromBundle toURL:destinationURL error:nil];
    }

    // end of comments

    NSError *error = nil;

    @try {
        _store = [_coordinator addPersistentStoreWithType:NSSQLiteStoreType
                                            configuration:nil
                                                      URL:[self storeURL]
                                                  options:@{                                                         NSMigratePersistentStoresAutomaticallyOption: @(YES),
                                                            NSInferMappingModelAutomaticallyOption: @(YES)}
                                                    error:&error];
    }
    @catch (NSException *exception) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:[NSString stringWithFormat:@"Error: %@, %@",error, [error userInfo]] delegate:self
                                              cancelButtonTitle:@"OK"
                                              otherButtonTitles:nil, nil];
        [alert show];
    }
    @finally {
        //
    }

}
Aldos answered 7/11, 2014 at 17:24 Comment(2)
What calls loadStore? What else can modify the instance variable _store? Is loadStore getting past the first line, if (_store)?Beatabeaten
You have a couple of places where you're passing a &error parameter to a method and not checking to see if an error occurred, and there's another with error:nil. You should start by at least verifying that the code above is doing what you expect, and by looking to see what error messages, if any, the APIs are trying to send you.Accomplished
Z
2

Su. Not sure if you did this but try the follwoing:

    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                             [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                             [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
[[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
    NSError* error;
    [managedObjectContext.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
                                                                  configuration:nil
                                                                            URL:self.storeUrl
                                                                        options:options // this is required to migrate some core data model attributes
                                                                          error:&error];

Basically have to tell it to use this migration thing in the options when you're setting it up.

Zimmer answered 7/11, 2014 at 18:32 Comment(1)
I already do (by passing directly the arguments in options) tried your way, didn't work, thanks for the suggestionAldos
G
1

I am not sure if this would apply to you or not, but I had a similar situation when upgrading from iOS 7 to iOS 8. In iOS 8 NSManagedObject added a property for deleted. I happened to have a property called deleted on my managed object models. I had to change the name of my deleted column to something that didn't conflict with the new deleted property on NSManagedObject.

Grandiloquence answered 12/11, 2014 at 0:3 Comment(1)
The isDeleted method was always there. As part of the Swift refactor for iOS 8 this was redeclared as a property.Beatabeaten
E
1

Could you confirm that you have executed all of these steps?

Step 1: Add the Framework

Click on your app target (on the left pane its the top icon with the name of your app) then go to the 'Build Phases' tab then on 'Link Binary With Libraries', click the little '+' at the bottom then find 'CoreData.framework' and add it to your project

Then either import coredata on all the objects that need it:

#import <CoreData/CoreData.h>

or add the import below the common imports in your .pch file:

#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
    #import <CoreData/CoreData.h>
#endif

Step 2: Add the Data Model

To add the .xcdatamodel file right click/control-click on your files in the right pane (like in a Resources folder for safe keeping) and select to Add a New File, Click the Core Data tab when selecting your file type then Click 'Data Model', give it a name and click Next and Finish and it will add it to your project. When you click on this Model object you will see the interface to add the Entities to your project with any relationships you want.


Step 3: Update App Delegate

Add these objects to AppDelegate.h

 @property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;
 @property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
 @property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;

 - (NSURL *)applicationDocumentsDirectory; // nice to have to reference files for core data

Synthesize the previous objects in AppDelegate.m like this:

@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;

Then add these methods to AppDelegate.m (make sure to put the name of the model that you added in the spots shown):

- (void)saveContext{
    NSError *error = nil;
    NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
    if (managedObjectContext != nil) {
        if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }
}

- (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;
    }
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"NAMEOFYOURMODELHERE" withExtension:@"momd"];
    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return _managedObjectModel;
}

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

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

    NSError *error = nil;
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {

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

    return _persistentStoreCoordinator;
}

 #pragma mark - Application's Documents directory

// Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory{
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}

Step 4: Get the Data Objects to the ViewControllers Where You Need the Data

in the ViewController.h

@property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;

In the ViewController.m

@synthesize managedObjectContext = _managedObjectContext;

In the AppDelegate, or class where the ViewController is created set the managedObjectContext to be the same as the AppDelegate one

ViewController.managedObjectContext = self.managedObjectContext;

If you want the viewcontroller using Core Data to be a FetchedResultsController then you'll need to make sure this stuff is in your ViewController.h

@interface ViewController : UIViewController <NSFetchedResultsControllerDelegate> {
  NSFetchedResultsController *fetchedResultsController;
  NSManagedObjectContext *managedObjectContext;
}

 @property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController;

And this is in ViewController.m

@synthesize fetchedResultsController, managedObjectContext;

Reference: How to add Core Data to an existing project in Xcode

Hope it sheds some lights on your issue.

Emplace answered 14/11, 2014 at 19:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.