UIManagedDocument autosave troubleshooting

I’d been having some trouble with UIManagedDocument that turned out to be my fault, but it wasn’t easy to figure out. In the end there were a couple of things that helped me figure out what was going on, so I’m posting them here in the hopes that I’ll save someone else some time.

UIManagedDocument is a UIDocument subclass which stores document data in a Core Data database. UIDocument is a class that manages document storage for you, but provides some great features like iCloud integration and automatic saving.

It’s the automatic saving that I was having trouble with. I would create my UIManagedDocument subclass, create a new document, populate it, and all seemed well. But then later, I’d create a new instance of a managed object, add it to my document, expect it to automatically save.. but it wasn’t saving.

Core Data is fairly well documented, and the process of creating a UIManagedDocument is also well documented, so I’m not going to post a whole walkthrough here. To make autosave work, the key thing you need to do is either add something to the document’s undo manager, or use updateChangeCount to inform the document that you’ve made a change. I was doing the latter.

The first thing I found useful was to subclass UIManagedDocument, and add an override:

1
2
3
4
5
- (id)contentsForType:(NSString *)typeName error:(NSError *__autoreleasing *)outError
{
    NSLog(@"Auto-Saving Document");
    return [super contentsForType:typeName error:outError];
}

You don’t want to leave NSLog messages in a shipping release binary, so do something about that, but the goal here is to know when the save is happening. One reason for this is if you make a change to the document, and then kill the app before the change is autosaved, it doesn’t get saved. Seeing the save in the console is your sign that you can kill the debug session knowing your change has been committed.

The other thing you need to know is when errors happen. When UIManagedDocument runs into a Core Data error, your only opportunity to find out is through an override:

1
2
3
4
5
6
7
8
9
10
11
12
- (void)handleError:(NSError *)error userInteractionPermitted:(BOOL)userInteractionPermitted
{  
    NSLog(@"UIManagedDocument error: %@", error.localizedDescription);
    NSArray* errors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
    if(errors != nil && errors.count > 0) {
        for (NSError *error in errors) {
            NSLog(@"  Error: %@", error.userInfo);
        }
    } else {
        NSLog(@"  %@", error.userInfo);
    }
}

Without this override, your auto-save may fail, and you’ll never know about it. In my case, I was getting a 1570 error, which, when looked up here, turns out to be a missing mandatory property: I forgot to set a mandatory property on one of my objects, so the save was failing and I had no idea until adding this override.