Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

DavidBlack

macrumors 6502a
Original poster
Jan 27, 2013
606
239
Somewhere In Apple's HQ ;)
Hi, I have this app which downloads YouTube videos it's currently not sandboxed, and I will like to be. So I tried to do that today it does not work properly my app has a feature called "simple downloading" it basically downloads the video to a defined folder and to choose your folder an NSSavePanel opens. So I did that with the app and downloading works fine but when you relaunch the app when you try to download the video it fails. I think this because PowerBox and failed to grant the app access to the file system. The app also has a feature that when the download button is click it opens an NSSavePanel this only works if the user has already clicked simple downloading and select a predefined and then uncheck it. (Another bug caused my app sandbox)
Can someone help me with this issue?

Thanks in advance
 

Attachments

  • Screen Shot 2013-10-24 at 6.48.05 PM.png
    Screen Shot 2013-10-24 at 6.48.05 PM.png
    27.7 KB · Views: 179
  • Screen Shot 2013-10-24 at 6.56.56 PM.png
    Screen Shot 2013-10-24 at 6.56.56 PM.png
    112.4 KB · Views: 144
  • Screen Shot 2013-10-24 at 7.12.49 PM.png
    Screen Shot 2013-10-24 at 7.12.49 PM.png
    50.3 KB · Views: 135
Last edited:
Without your code, probably not. Please share your relevant code. I haven't used this kind of feature before, but as I recall when I was reading the APIs when it was new, there was some sort of bookmark or something you're supposed to store that can be reused between launches to grant access to folders to which the user has previously given you access.
 
Without your code, probably not. Please share your relevant code. I haven't used this kind of feature before, but as I recall when I was reading the APIs when it was new, there was some sort of bookmark or something you're supposed to store that can be reused between launches to grant access to folders to which the user has previously given you access.

This is the NSSavPanel Method and the first part of the download method.

Code:
 self.extractedURL = [NSURL URLWithString: url];
                        NSString *downloadtitle = [youtubewebview stringByEvaluatingJavaScriptFromString:@"document.title"];
                        NSSavePanel *savePanel = [NSSavePanel savePanel];
                        [savePanel setTitle:NSLocalizedString(@"Choose Where You Want To Save Your Video...", @"Choose Where You Want To Save Your Video... comment")];
                        [savePanel setCanCreateDirectories:YES];
                        [savePanel setPrompt:NSLocalizedString(@"Download", @"Download Propmt comment")];
                        [savePanel setNameFieldStringValue:downloadtitle];
                        
                        NSInteger result = [savePanel runModal];
                        if(result != NSFileHandlingPanelOKButton)
                            return;
                        NSURL *directoryURL = [savePanel directoryURL];
                        
                        NSString *outputName = [savePanel nameFieldStringValue];
                        NSString *brain = [directoryURL path];
                        NSString *outputDic = brain;
                        
                        
                        // start fetching videos
                        [self.progressIndicator setIndeterminate: NO];
                        
                        [self.downloadButton setTitle:NSLocalizedString(@"Stop", @"Stop download")];
                        
                        
                     
                        
                        
                        r = [ASIHTTPRequest requestWithURL:self.extractedURL];
                        ASIHTTPRequest * __unsafe_unretained unretainedOperation = r;
                        
                        NSString *local_full_path = [NSString stringWithFormat: @"%@/%@.mp4", outputDic,outputName];
                        NSLog(@"Saving: %@", local_full_path);
                        //NSLog(@"Saving to the folder: %@", [local_full_path stringByDeletingLastPathComponent]);
                        [r setTimeOutSeconds: 10];
                        
                        [unretainedOperation setAllowResumeForFileDownloads: YES];
                        [unretainedOperation setNumberOfTimesToRetryOnTimeout:5];
                        [unretainedOperation setDownloadDestinationPath: local_full_path];
                        [unretainedOperation setTemporaryFileDownloadPath: [NSString stringWithFormat:@"%@/%@.temp", outputDic,outputName]];
                        
                        [unretainedOperation setDownloadProgressDelegate: self.progressIndicator];
                        
                        [unretainedOperation setShowAccurateProgress:YES];
                        [unretainedOperation setDelegate: self];
 
Last edited:
You need to save any URLs you want access to after relaunches using security scoped bookmarks, as explained here under "Security-Scoped Bookmarks and Persistent Resource Access".

Another option in your case would be to include access to the downloads folder in your sandbox entitlements, and allow users to select that as their location for simple downloading.
 
The whole idea of a sandbox is that your app cannot do whatever it likes, but is restricted to doing only things that are save. What you have is not a "Sandbox Bug", it's the sandbox doing exactly what it is supposed to do. One thing your app isn't supposed to be able to do is writing files anywhere it likes.

When you use the NSSavePanels from a sandbox, and the user picks a file or directory, the OS opens up access to that file. But only as long as the application is running. As JoshDC said, once your app has access to that file, even momentarily, you can make calls that give you access rights later.
 
You need to save any URLs you want access to after relaunches using security scoped bookmarks, as explained here under "Security-Scoped Bookmarks and Persistent Resource Access".

Another option in your case would be to include access to the downloads folder in your sandbox entitlements, and allow users to select that as their location for simple downloading.
Thanks I found these examples online. But they appear to need the NSSavePanel to run in a completion handler, which is not an option for me.

Code:
NSError *error = nil;
NSData *bookmarkData = nil;
 
bookmarkData = [openPanelFileURL 
bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
includingResourceValuesForKeys:nil
relativeToURL:nil
error:&error];

Code:
NSError *error = nil;
BOOL bookmarkDataIsStale;
NSURL *bookmarkFileURL = nil;
 
bookmarkFileURL = [NSURL 
URLByResolvingBookmarkData:bookmarkData
options:NSURLBookmarkResolutionWithSecurityScope
relativeToURL:nil
bookmarkDataIsStale:&bookmarkDataIsStale
error:&error];
Can you help me?
 
You don't have to run it with a completion block. Provided the URL you are creating a security scoped bookmark from is accessible at the point you create it, it will work fine. You can replace openPanelFileURL with [savePanel URL] at the point your save panel ends.
 
You don't have to run it with a completion block. Provided the URL you are creating a security scoped bookmark from is accessible at the point you create it, it will work fine. You can replace openPanelFileURL with [savePanel URL] at the point your save panel ends.

This what I have but I getting a null value. Thanks for your help so far btw.

Code:
  NSString *downloadtitle = [youtubewebview stringByEvaluatingJavaScriptFromString:@"document.title"];
                        NSSavePanel *savePanel = [NSSavePanel savePanel];
                        [savePanel setTitle:NSLocalizedString(@"Choose Where You Want To Save Your Video...", @"Choose Where You Want To Save Your Video... comment")];
                        [savePanel setCanCreateDirectories:YES];
                        [savePanel setPrompt:NSLocalizedString(@"Download", @"Download Propmt comment")];
                        [savePanel setNameFieldStringValue:downloadtitle];
                        
                        NSInteger result = [savePanel runModal];
                        if(result != NSFileHandlingPanelOKButton)
                            return;
                        NSURL *directoryURL = [savePanel URL];
                        
                        NSString *outputURL = [savePanel nameFieldStringValue];
                        
                        NSError *error = nil;
                        NSData *bookmarkData = nil;
                        
                        bookmarkData = [directoryURL
                                        bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
                                        includingResourceValuesForKeys:nil
                                        relativeToURL:nil
                                        error:&error];
                        
                        BOOL bookmarkDataIsStale;
                        NSURL *bookmarkFileURL = nil;
                        
                        bookmarkFileURL = [NSURL
                                           URLByResolvingBookmarkData:bookmarkData
                                           options:NSURLBookmarkResolutionWithSecurityScope
                                           relativeToURL:nil
                                           bookmarkDataIsStale:&bookmarkDataIsStale
                                           error:&error];
                        
                        if (error) {
                            NSLog(@"Error");
                        }
                      
                        NSString *bookmarkFilePath = [bookmarkFileURL absoluteString];
                        
                        [bookmarkFileURL startAccessingSecurityScopedResource];


                        
                        // start fetching videos
                        [self.progressIndicator setIndeterminate: NO];
                        
                        [self.downloadButton setTitle:NSLocalizedString(@"Stop", @"Stop download")];
                        
                        
                        NSString *randomFilename = [NSString stringWithFormat: @"%@", outputURL];
                        
                       
                        r = [ASIHTTPRequest requestWithURL:self.extractedURL];
                        ASIHTTPRequest * __unsafe_unretained unretainedOperation = r;
                        
                        NSString *local_full_path = [NSString stringWithFormat: @"%@/%@.mp4", bookmarkFilePath,outputURL];
                        NSLog(@"Saving: %@", local_full_path);
                       
                        [r setTimeOutSeconds: 10];
                        
                        [unretainedOperation setAllowResumeForFileDownloads: YES];
                        [unretainedOperation setNumberOfTimesToRetryOnTimeout:5];
                        [unretainedOperation setDownloadDestinationPath: local_full_path];
                        [unretainedOperation setTemporaryFileDownloadPath: [NSString stringWithFormat:@"%@/%@.temp", bookmarkFilePath,outputURL]];

I get this in the debug area:

Code:
Error
Saving: (null)/Coming Soon - YouTube.mp4
 
You don't need to do that when the user selects an exact file path. From my understanding your app works in one of two ways:

  • Simple downlding: the user clicks "download", the file is saved to a folder chosen previously
  • Non-simple downloading: the user is asked each time they want to download a file

Sandboxing only affects you in the first case, in which you need to save the folder as a security scoped bookmark. Something like:

  • The user enables simple downloading and chooses a folder
  • You save that folder as a security scoped bookmark (for examaple in their preferences)
  • When the user wants to download a file, you resolve the URL from the data in their preferences and use the APIs available to work with it

Make sure to read and understand the sandbox guide, specifically how open and save panels work with powerbox.

For what it's worth I'd just add the downloads folder to your entitlements and tell users they can only save there if they want simple downloading.
 
You don't need to do that when the user selects an exact file path. From my understanding your app works in one of two ways:

  • Simple downlding: the user clicks "download", the file is saved to a folder chosen previously
  • Non-simple downloading: the user is asked each time they want to download a file

Sandboxing only affects you in the first case, in which you need to save the folder as a security scoped bookmark. Something like:

  • The user enables simple downloading and chooses a folder
  • You save that folder as a security scoped bookmark (for examaple in their preferences)
  • When the user wants to download a file, you resolve the URL from the data in their preferences and use the APIs available to work with it

Make sure to read and understand the sandbox guide, specifically how open and save panels work with powerbox.

For what it's worth I'd just add the downloads folder to your entitlements and tell users they can only save there if they want simple downloading.

Thanks, but I want to have the option for the use an NSSavePanel. Which currently does not work.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.