Search code examples
macossandboxnsmetadataquery

Accessing the desktop in a Sandboxed app


I'm building an application that has a feature to open newly taken screenshots. I would like to distribute it using the Mac App Store. Unfortunately, it needs to be sandboxed.

To find the new screenshots I run a NSMetaDataQuery. It returns a few entries but unfortunately I can't get their URL since they are on the Desktop (out of my app's sandbox).

How can I fix this ?

Here is some of the code

query = [[NSMetadataQuery alloc] init];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryUpdated:) name:NSMetadataQueryDidStartGatheringNotification object:query];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryUpdated:) name:NSMetadataQueryDidUpdateNotification object:query];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryUpdated:) name:NSMetadataQueryDidFinishGatheringNotification object:query];

[query setDelegate:self];
[query setPredicate:[NSPredicate predicateWithFormat:@"kMDItemIsScreenCapture = 1"]];
[query startQuery];

numberOfScreenshots = [query resultCount];
[self uploadToAmazonS3:[[[query results]objectAtIndex:([query resultCount]-1)]valueForAttribute:NSMetadataItemURLKey]];

Thanks


Solution

  • Without asking user permission, you can access only Music, Movies, Pictures and Download folders. Entitlements

    You have to ask user, to grant you access to Desktop Folder. Then use mechanism called Security-Scoped Bookmarks, read more about it in AppSandboxDesignGuide.

    1. Use NSOpenPanel to choose directory.
    2. Save bookmark for future use, for example in NSUserDefaults.
    3. Get access

    1 and 2

        NSOpenPanel *openPanel = [[NSOpenPanel alloc] init];
        [openPanel setCanChooseFiles:NO];
        [openPanel setCanChooseDirectories:YES];
        [openPanel setCanCreateDirectories:YES];
    
        [openPanel beginWithCompletionHandler:^(NSInteger result){
            if (result == NSFileHandlingPanelOKButton) {
                for (NSURL *fileURL in [openPanel URLs]) {
                    NSString *filename = [fileURL path];
                    [[NSUserDefaults standardUserDefaults] setObject:filename forKey:@"PathToFolder"];
    
                    NSError *error = nil;
                    NSData *bookmark = [fileURL bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
                         includingResourceValuesForKeys:nil
                                          relativeToURL:nil
                                                  error:&error];
                    if (error) {
                        NSLog(@"Error creating bookmark for URL (%@): %@", fileURL, error);
                        [NSApp presentError:error];
                    } else {
                        [[NSUserDefaults standardUserDefaults] setObject:bookmark forKey:@"PathToFolder"];
                        [[NSUserDefaults standardUserDefaults] synchronize];
                    }
                    break;
                }
            }        
    }];
    

    3.

        NSError *error = nil;
        NSData *bookmark = [[NSUserDefaults standardUserDefaults] objectForKey:@"PathToFolder"];
        bookmarkedURL = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:nil error:&error];
        BOOL ok = [bookmarkedURL startAccessingSecurityScopedResource];
        NSLog(@"Accessed ok: %d %@", ok, [bookmarkedURL relativePath]);
    

    So, that sould be it.