Question

I have a UIDocument based app, without iCloud support. The user is able to create documents, and save them to app's Document directory.

I have also created few "sample" documents, and passing them with the app's bundle. I want the user to be able to open sample documents the same way as they can open their own documents:

NSArray *samplesContent = [[NSBundle mainBundle] URLsForResourcesWithExtension:@"doco" subdirectory:@"Samples"];

for (NSURL *sampleURL in samplesContent) {
    Doco *doco = [[Doco alloc] initWithFileURL:sampleURL];
    [doco disableEditing];

    [doco openWithCompletionHandler:^(BOOL success) {
        if (success) {
            [self.docos addObject:doco];

            if (self.docos.count >= samplesContent.count) {
                [self.tableView reloadData];
            }
        } else DLog(@"Failed to open %@", [sampleURL pathExtension]);
     }];
}

What I am seeing is this:

  1. On viewDidLoad, docs array gets populated with sample docs and displayed in the tableView the first time app launches. No issues here, all is smooth and nice.
  2. Then I back out of the view with the following code to close all open docs:

    int count = 0;
    
    for (Doco *doco in self.docos) {
        if (doco.documentState == UIDocumentStateNormal) {
            [doco closeWithCompletionHandler:nil];
            count++;
        }
    }
    
    DLog(@"Closed %i docs", count);
    
  3. When I open the view again, the array of docs should get populated again and tableView re-populated, but nothing happens.

The completion handler below never gets called, although the URL is pointing to the same file and it is valid:

[doco openWithCompletionHandler:^(BOOL success) {}

I do not have this issue for user generated docs stored in Documents, so my assumption is that it has something to do with auto-save, that gets called on read-only bundle and fails

But I am sort of stuck on this part, any help will be appreciated.

Was it helpful?

Solution 2

If UIDocument is updated it will try to save changes on close. Since UIDocument was loaded from read-only bundle, I had to make sure that it does not get updated, otherwise close block returns success=NO, and document is not closed...

OTHER TIPS

The problem has already been identified, but I think it's worth describing a couple of simple solutions since including sample documents in an app's bundle is not uncommon.

So the problem is that the sample document is trying to save changes when it is closed, but saving cannot succeed in a read-only app bundle.

I think there are two main solutions here:

  1. Copy the sample document into the Documents directory, where it can be treated like any other document and saved successfully (if you want user edits to the sample document to be saved, use this approach).

  2. Prevent the document from trying to save (for read-only sample documents).

So here are some simple examples...


1. Copy sample documents into Documents directory

On first launch (or indeed whenever you decide to 'refresh' the sample document), use NSFileManager to copy the file into place:

- (void)refreshSampleDocuments
{
    NSArray *sampleFromURLs = [[NSBundle mainBundle] URLsForResourcesWithExtension:@"doc" subdirectory:@"Samples"];

    for (NSURL *sampleFromURL in sampleFromURLs) {

        NSString *sampleFilename = [sampleFromURL lastPathComponent];
        NSURL *sampleToURL = [[self documentsDirectoryURL] URLByAppendingPathComponent:sampleFilename];

        // ...
        // Do some checks to make sure you won't write over any user documents!
        // ....

        NSError *error;
        BOOL copySuccessful = [[NSFileManager defaultManager] copyItemAtURL:sampleFromURL toURL:sampleToURL error:&error];

        if (!copySuccessful) {
            // Handle error...
        }
    }
}

2. Prevent sample documents from trying to save

This approach is much simpler (for read-only documents), and easier than trying to prevent updates wherever they might occur in the document.

When closeWithCompletionHandler: is called on UIDocument, autosaveWithCompletionHandler: is invoked to ensure the document file is saved before closing. This in turn invokes hasUnsavedChanges to decide whether a save is necessary. So if hasUnsavedChanges returns NO, then any invocation of the autosaving mechanism will result in no changes being written out.

(N.B. manually calling saveToURL:forSaveOperation:completionHandler: will still force a save, regardless of what hasUnsavedChanges returns.)

So in your UIDocument subclass, override hasUnsavedChanges to return NO if the document is read-only.

@interface MyDocument : UIDocument

@property(nonatomic, getter = isReadOnly) BOOL readOnly;

@end


@implementation MyDocument

- (BOOL)hasUnsavedChanges
{
    return [self isReadOnly] ? NO : [super hasUnsavedChanges];
}

@end

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top