Search code examples
iosobjective-ccore-datansmanagedobjectnsmanagedobjectcontext

Trying to initialize ManagedObjectContext from Singleton class in iOS


I created a Single View Application in iOS that also incorporates Core Data. I moved my .xcdatamodel file from another application and put in to the one I am working on now, and I am having issues. What I have done is cut and paste the code from the previous application and placed it in my AppDelegate.h/m files:

@interface DBAppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

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

- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;

@end

and my .m file:

    @implementation DBAppDelegate

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

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        // Override point for customization after application launch.
        UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
//the line below is what is causing an error
        DBViewController *controller = (DBViewController *)navigationController.topViewController;
        controller.managedObjectContext = self.managedObjectContext;


        return YES;
    }

Within my .m file, I also included the boiler plate code for Core Data that was also in my previous application which I have not posted. In my new application, what I am doing is that I have created an access layer, which also provides a Singleton instance to access this layer. It is in this class where I do my CRUD operations, and have declared the following properties in the .h file:

@property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;

At the moment, I am getting the following error:

"Property 'managedObjectContext' not found on object of type "DBViewController". What I would like to do is initialize the managedObjectContext inside my method that allows for the creation of a Singleton instance:

static DB *sharedSingleton = nil;

+ (DB *) sharedInstance {

    if (sharedSingleton == nil) {

        sharedSingleton = [[super alloc] init];

  }

    return sharedSingleton;
}

What am I doing wrong? I realize I don't declare a managedObjectContext object in my DBViewController, but what do I put in place of this line? I figure it would be something with respect to my Singleton class, but I honestly don't have a clue here.


Solution

  • What you are trying to do is often called a "data store" singleton, and it's a good design pattern to use. What I do in my applications is have a singleton class named DataStore, you can call it whatever you wish, with a class method:

    + (id)sharedStore{
        static DataStore *sharedStore = nil;
        if (!sharedStore) {
            sharedStore = [[self alloc] init];
        }
    
        return sharedStore;
    }
    

    Whenever I need access to the resources provided by the datastore, I do:

    Datastore *ds = [Datastore sharedStore];
    

    To provide access to Core Data, I have a data store method:

    + (NSManagedObjectContext*)managedObjectContext{
        static NSManagedObjectContext *context = nil;
    
        if(context){
            return context;
        }
    
        NSPersistentStoreCoordinator *coordinator = nil;
    
        NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Model" withExtension:@"momd"];
        NSManagedObjectModel *objectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    
        if (!coordinator) {
            coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:objectModel];
        }
    
        if(!coordinator){
            return nil;
        }
    
        NSString *storePath = [[self documentsDirectoryPath] stringByAppendingPathComponent:@"datastore.sqlite"];
        NSURL *storeURL = [NSURL URLWithString:storePath];
    
        NSError *error;
    
        if (![coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:NULL error:&error])
        {
            NSLog(@"Database error: %@", error);
            // if you make changes to your model and a database already exists in the app
            // you'll get a NSInternalInconsistencyException exception. When the model is updated
            // the databasefile must be removed. Remove the database here because it's easy.
            NSFileManager *fileManager = [NSFileManager defaultManager];
            [fileManager removeItemAtURL:storeURL error:nil];
    
            //try to add the persistant store one more time. If it still fails then abort
            if (![coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:NULL error:&error])
                return nil;
        }
    
        context = [[NSManagedObjectContext alloc] init];
        [context setPersistentStoreCoordinator:coordinator];
        [context setUndoManager:nil];
    
        return context;
    
    }
    

    This method returns nil if an NSManagedObjectContext couldn't be created. This way you only need to do:

    NSManagedObjectContext *context = [[DataStore sharedStore] managedObjectContext];

    whenever you need to use Core Data. This can be done once in viewDidLoad.

    Edit:

    The managedObjectContext method uses the method below to find the documents directory:

    + (NSString *)documentsDirectoryPath
    {
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
        return basePath;
    }