0
votes

I am trying to create a Core Data-based Mac OS X application. I've used Core Data on iOS, but things are a little different with AppKit. I am also using Xcode 4.1 on 10.7, and I know some Core Data things have changed on Lion. The only up-to-date reference from Apple is a recent WWDC session, which promises/suggests updated references on Apple.com, but most of the Core Data stuff is very out of date.

To start, I used Xcode to generate a Document-based project using Core Data. I hollowed out some of the Main Menu bits (deleted some menu items) and got rid of the NSWindow object inside Main Menu.xib, creating my own MyDocument.xib with a NSWindow (and sub-views) instead.

Questions:

  1. I assume that a Persistent Store Controller always needs a persistent store, so if a new document is created, I'm just adding an in-memory store. When opening up from disk, I create it using the SQLite store. Is that correct?

  2. I (ostensibly) do a store migration in writeToURL:ofType:error: if the url is different. But if I set a breakpoint in writeToURL:ofType:error:, and then hit Save, my breakpoint is not hit. Why not?

  3. If I choose "Revert to Saved", the application crashes -- hard. I have to force-quit it (and quit Xcode). Why?

  4. (Very) old video tutorials suggest a way to easily bind data to a UIView like a table. Following along with Xcode is impossible since so much has changed. Any newer references out there for that?

Code:

MyDocument.h:

#import <Cocoa/Cocoa.h>

@interface MyDocument : NSPersistentDocument
{
@private
    NSManagedObjectModel* mom;
    NSPersistentStoreCoordinator* psc;
    NSManagedObjectContext* moc;
}

@end

MyDocument.m:

#import "MyDocument.h"

@interface MyDocument ()
- (void)setUpManagedObjectModel;
- (void)setUpPersistentStoreCoordinator;
- (void)setUpManagedObjectContext;
- (BOOL)addPersistentStore:(NSString*)type url:(NSURL*)url error:(NSError**)outError;
@end

@implementation MyDocument

- (void)dealloc
{
    [moc release];
    [psc release];
    [mom release];
    [super dealloc];
}

- (id)init
{
    self = [super init];
    if (self)
    {
        [self setUpManagedObjectModel];
        [self setUpPersistentStoreCoordinator];
        [self setUpManagedObjectContext];
    }
    return self;
}

- (id)initWithType:(NSString *)typeName error:(NSError **)outError
{
    self = [super initWithType:typeName error:outError];
    if (self)
    {
        [self addPersistentStore:NSInMemoryStoreType url:nil error:outError];
    }
    return self;
}

- (BOOL)readFromURL:(NSURL *)url ofType:(NSString *)typeName error:(NSError **)outError
{
    return [self addPersistentStore:NSSQLiteStoreType url:url error:outError];
}

- (BOOL)writeToURL:(NSURL *)url ofType:(NSString *)typeName error:(NSError **)outError
{
    NSPersistentStore* currentStore = [[psc persistentStores] lastObject];
    if (![[currentStore URL] isEqual:url])
    {
        NSPersistentStore* newStore = [psc migratePersistentStore:currentStore
                                                            toURL:url
                                                          options:nil
                                                         withType:typeName
                                                            error:outError];
        if (!newStore)
        {
            return NO;
        }
    }
    return [moc save:outError];
}

- (NSString *)windowNibName
{
    // Override returning the nib file name of the document
    // If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
    return @"MyDocument";
}

- (void)windowControllerDidLoadNib:(NSWindowController *)aController
{
    [super windowControllerDidLoadNib:aController];
    // Add any code here that needs to be executed once the windowController has loaded the document's window.
}

+ (BOOL)autosavesInPlace
{
    return YES;
}

- (BOOL)validateMenuItem:(NSMenuItem *)item
{
    NSLog(@"menu item: %@", [item title]);
    return YES;
}

#pragma mark Private Methods


- (void)setUpManagedObjectModel
{
    NSURL* modelUrl = [[NSBundle mainBundle] URLForResource:@"MyDatabase" withExtension:@"momd"];
    mom = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelUrl];
}

- (void)setUpPersistentStoreCoordinator
{
    psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
}

- (void)setUpManagedObjectContext
{
    moc = [[NSManagedObjectContext alloc] init];
    [moc setPersistentStoreCoordinator:psc];
}

- (BOOL)addPersistentStore:(NSString*)type url:(NSURL*)url error:(NSError**)outError
{
    NSDictionary* options = [NSDictionary dictionaryWithObjectsAndKeys:
                             [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                             [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
    NSPersistentStore* ps = [psc addPersistentStoreWithType:type
                                              configuration:nil
                                                        URL:url
                                                    options:options
                                                      error:outError];
    return (ps != nil);
}

@end
1

1 Answers

2
votes

If you're using NSPersistentDocument, by default all of this is handled for you, you don't need to do any Core Data setup. NSPersistentDocument will automatically handle creating, saving and migrating the persistent store.

If you want to control how the persistent store is configured, just override

-configurePersistentStoreCoordinatorForURL:ofType:modelConfiguration:storeOptions:error:

Then you can just ask the document for its managedObjectContext when you need it and code away.

I highly recommend you read Apple's NSPersistentDocument tutorial for more detail.