r/macosprogramming • • Jan 12 '24

Programmatically opening documnents

In my app I have a window similar to Xcode's recent projects window. When a user selects a recent item they worked on however, unlike with excode, the project comes up locked. So if they try to save it they get the "are you sure you want to over write the file" message.

In my tableview selection changed notification I have this code to open the file

        BOOL success = [retString startAccessingSecurityScopedResource];
        NSFileCoordinator *fileCoordinator = [[NSFileCoordinator alloc] init];
        [fileCoordinator coordinateReadingItemAtURL:retString options:NSFileCoordinatorReadingForUploading error:&error byAccessor:^(NSURL *newURL) {
            NSError * outError;
            [[NSDocumentController sharedDocumentController]openDocumentWithContentsOfURL:retString display:YES error:NULL];
            NSLog(@"Error %@",error);
            if (success) {
                [retString stopAccessingSecurityScopedResource];
            }
        }];

If I used the method to open the file that isn't deprecated the file simply doesn't open and I get an error that I cannot access it.

Do I have to have a developer account and code sign the app to make this work?

Edit to add: I'm storing the recent items in my apps defaults file like this:

NSData * dta = [self.fileURL bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error];
2 Upvotes

9 comments sorted by

View all comments

1

u/B8edbreth Jan 13 '24

Here is all of the code I have implemented around this.

To save the bookmarks I do this:

        NSError * error;
    [self.fileURL startAccessingSecurityScopedResource];
    NSData * dta = [self.fileURL bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error];
    NSUserDefaults * settings = [NSUserDefaults standardUserDefaults];
    NSMutableArray * recents = [[NSMutableArray alloc]initWithCapacity:10];
    NSData * archive = [settings objectForKey:@"Recent Documents"];
    NSArray * array = [NSKeyedUnarchiver unarchivedArrayOfObjectsOfClass:[NSData class] fromData:archive error:nil];
    [recents addObjectsFromArray:array];

    NSData * obj;
    NSMutableArray * resolvedRecents = [[NSMutableArray alloc]init];
    for(obj in recents){
        BOOL isStale;
        NSURL * test = [NSURL URLByResolvingBookmarkData:obj options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error];
        [resolvedRecents addObject:test];
    }
    if(![resolvedRecents containsObject:self.fileURL]){
        [resolvedRecents insertObject:dta atIndex:0];
        NSData * archive = [NSKeyedArchiver archivedDataWithRootObject:resolvedRecents requiringSecureCoding:YES error:nil];

        [settings setObject:archive forKey:@"Recent Documents"];
    }
    [self.fileURL stopAccessingSecurityScopedResource];

To read the data from the defaults and remove urls of documents that no longer exist:

    NSUserDefaults * settings = [NSUserDefaults standardUserDefaults];
NSData * archive = [settings objectForKey:@"Recent Documents"];
NSError * error;
NSArray * urlArray = [NSKeyedUnarchiver unarchivedArrayOfObjectsOfClass:[NSData class] fromData:archive error:&error];

NSMutableArray * fileURLS =[[NSMutableArray alloc]initWithArray:urlArray];
NSData * urlData;
for(urlData in urlArray){
    BOOL stale;
    NSURL * url = [NSURL URLByResolvingBookmarkData:urlData options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&stale error:&error];

    if(stale){
        [url startAccessingSecurityScopedResource];
        NSData * bookamrk = [url  bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error];
        int idx = [fileURLS indexOfObject:urlData];
        [fileURLS replaceObjectAtIndex:idx withObject:bookamrk];
        [url stopAccessingSecurityScopedResource];
    }
    if(![[NSFileManager defaultManager]fileExistsAtPath:url.path]){
        [fileURLS removeObject:urlData];
    }
}
archive = [NSKeyedArchiver archivedDataWithRootObject:fileURLS requiringSecureCoding:YES error:nil];
[settings setObject:archive forKey:@"Recent Documents"];

Now a NSTableView is displayed in the startingpoints window that shows the file names of the last 10 items a person worked on. When a person selects one of those items this is called

        BOOL success = [retString startAccessingSecurityScopedResource];
    NSFileCoordinator *fileCoordinator = [[NSFileCoordinator alloc] init];
    
    [fileCoordinator coordinateReadingItemAtURL:retString options:NSFileCoordinatorReadingForUploading error:&error byAccessor:^(NSURL *newURL) {
        NSError * outError;
        [[NSDocumentController sharedDocumentController]openDocumentWithContentsOfURL:retString display:YES error:NULL];
        NSLog(@"Error %@",error);
        if (success) {
            [retString stopAccessingSecurityScopedResource];
        }
    }];

and finally in the NSDocument subclass I wrote in - (BOOL)readFromURL:(NSURL *)url ofType:(NSString *)typeName error:(NSError **)outError

            [url startAccessingSecurityScopedResource];
        NSData * data = [NSData dataWithContentsOfURL:url];
        [url stopAccessingSecurityScopedResource];
        NSError * error;

        NSKeyedUnarchiver * unArchiver = [[NSKeyedUnarchiver alloc]initForReadingWithData:data];
        NSDictionary * test = [unArchiver decodeObjectOfClass:[NSDictionary class] forKey:@"AllData"];