UIManagedDocument Creation in ViewDidLoad

756 views Asked by At

After some research I am still unsure on when / how I should create my UIManagedDocument.

@interface ViewController ()
@property (strong, nonatomic) UIManagedDocument *document;
@end

@implementation ViewController

- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:YES];

NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *documentsDirectory = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] firstObject];
NSString *documentName = @"MyDocument";
NSURL *url = [documentsDirectory URLByAppendingPathComponent:documentName];

BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:[url path]];

if (fileExists) {
    [self.document openWithCompletionHandler:^(BOOL success) {
        NSLog(@"Exsits dont create again");
        if (success) [self documentIsReadyCreateAndObject];
        if (!success) NSLog(@"cound not open document at %@", url);
    }];
}
else {
    // create it
    [self.document saveToURL:url forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
        NSLog(@"Create it");
        if (success) [self documentIsReadyCreateAndObject];
        if (!success) NSLog(@"cound not create document at %@", url);
    }];
}
}

- (void) documentIsReadyCreateAndObject
{
if (self.document.documentState == UIDocumentStateNormal) {
    NSLog(@"document is ready - create obj");
    Car *demo = [NSEntityDescription insertNewObjectForEntityForName:@"Car" inManagedObjectContext:self.document.managedObjectContext];
    demo.carName = @"xxxxx";
}
else {
    NSLog(@"Not ready to go");
}
}

- (void) fetchAndPrint
{
if (self.document.documentState == UIDocumentStateNormal) {
    NSLog(@"document is ready - print objs");
    NSFetchRequest *request = [[NSFetchRequest alloc]initWithEntityName:@"Car"];
    NSArray *results = [self.document.managedObjectContext executeFetchRequest:request error:nil];

    for (Car *aLog in results) {
        NSLog(@"\nNAME: %@", aLog.carName);
    }
}
else {
    NSLog(@"Not ready to go");
}
}

@end
2

There are 2 answers

10
Scott Berrevoets On BEST ANSWER

View Controller vs. App Delegate

The best time to create your UIManagedDocument depends a little on the situation, but I find that in most cases the app's delegate is the best place for it. A view controller is really not the place to create a database. Most of the time, you'll be using that database in multiple places in your app, so it wouldn't really be the task of one specific view controller to create it. If the design of your app changes such that that view controller is not the first controller on screen, you have to take all that code and put it in some other view controller.

Since a Core Data backed model usually needs to be accessed from many different view controllers, I use the app delegate for that. As soon as your app is launched, it will create or open a database that view controllers can use pretty much immediately.

That doesn't mean that you have to access your document from the app delegate as well. There's a lot of debate about whether that's good practice or not, but I prefer to pass the document to whatever object needs so that it doesn't depend on the app delegate for its model.

Most of the time, the only reason you pass a UIManagedDocument is for its NSManagedObjectContext, so you could consider just passing that if you don't need any other document-related properties. Also, if you just pass one NSManagedObject, you automatically have access to its context, so that could reduce the number of times you have to pass your document.

Migration

I'll answer your last question first: no, using the traditional Core Data stack or UIManagedDocument does not matter with regards to migration (lightweight or using mapping models). UIManagedDocument has a persistentStoreOptions property that you can use to handle most migrations for you. Even if you need a mapping model, you can create that, add it to your bundle and if you made the right mappings, it will do it all for you.

0
Duncan Groenewald On

Create the UIManagedDocument in the AppDelegate but because creation is asynchronous you can't rely on it being ready when your view loads. So you should not try and access anything in your viewDidLoad method. Rather have a method on your viewController called displayData{} or something and call this from the UIManagedDocument open completion handler, or better still post a notification once the file has been successfully created and/or opened. Better get used to this async stuff because iCloud makes extensive use of it. Check this link out for more information on how to achieve UIManagedDocument & iCloud Integration.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
   ...

    if (fileExists) {
        [self.document openWithCompletionHandler:^(BOOL success) {

            NSLog(@"Exsits dont create again");
            if (success) {
               [[NSNotificationCenter defaultCenter] postNotificationName:@"DatabaseReady"
                                                        object:self];
            } else {
               NSLog(@"cound not open document at %@", url);
            }
        }];
    }
    else {
        // create it
        [self.document saveToURL:url forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
            NSLog(@"Create it");
            if (success) {
               [[NSNotificationCenter defaultCenter] postNotificationName:@"DatabaseReady"
                                                        object:self];
            } else {
               NSLog(@"cound not create document at %@", url);
            }
        }];
    }
}

Add an observer in your viewControllers viewDidLoad method to receive the notification

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(displayData) name:@"DatabaseReady" object:nil];

In your viewControllers displayData method get the moc and retrieve your data and you should be good to go.

- (void) displayData
{
self.document = [(AppDelegate *)[[UIApplication sharedApplication] delegate] document];

self.managedObjectContext = self.document.managedObjectContext;

Car *demo = [NSEntityDescription insertNewObjectForEntityForName:@"Car" inManagedObjectContext:self.managedObjectContext];
demo.carName = @"xxxxx ";
}

I would usually also post a (different) message if the creation failed so you can display a message to the user from the viewController.