Save DocumentPicker URL for further use

Hello, I'm having trouble saving the value of an NSURL object.

The code bellow is used in a c++ project, however, this issue shouldn't be exclusive to this scenario.

I want to use document picker, only to choose a file and get its url, which I would use on c++ side to read/write data. That I've managed to do successfully, by passing the url to c++ side by calling FileWasPicked(std::string) c++ method.

However, since document picker allows us to choose files only from public folders, I need to call start/stopAccessingSecurityScopedResource, and to call them I need to pass an NSURL object, that I get from the document picker. I call startAccessingSecurityScopedResource method before leaving didPickDocumentsAtURLs method, however I can't do that with the stopAccessingSecurityScopedResource.

I've tried to save the NSURL we get from document picker in a class variable @property NSURL *m_LastUsedURL;, however it's value changes after leaving didPickDocumentsAtURLs method. My guess, is that after leaving the before mention method, the value of the object that we save a pointer to gets deinitialized. Passing the string value back from c++ and converting it to an NSURL object doesn't work either.

Is there any way to save NSURL object value in this case, or call the stopAccessingSecurityScopedResource method in a different manner?

I attach my current code:

@implementation DocumentPicker //In a DocumentPicker.mm file

//We call this method from c++, to open document picker
- (void)openDocumentPicker:(NSString*)pickerType //We pass the picker type from c++
{
    //Find the current app window, and its view controller object
    UIApplication* app = [UIApplication sharedApplication];
    UIWindow* rootWindow = app.windows[0];
    UIViewController* rootViewController = rootWindow.rootViewController;
    
    UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[pickerType] inMode:UIDocumentPickerModeOpen];
    
    documentPicker.delegate = self;

    documentPicker.modalPresentationStyle = UIModalPresentationFormSheet;

    [rootViewController presentViewController:documentPicker animated:YES completion:nil];
}

//Callback, that gets called after user successfully chooses a file
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls
{
    //Start accessing file, that the user have picked
    if ( [urls[0] startAccessingSecurityScopedResource] )
    {
        _m_LastUsedURL = urls[0];
        //Pass the url and exit this method, to do actions in c++
        GlobalCppClass->FileWasPicked(urls[0].absoluteString.UTF8String);
    }
    else
    {
        NSLog(@"startAccessingSecurityScopedResource failed");
    }
}

//This would be called, after we finish reading the file in c++
-(void)stopAccesingFile
{
    [_m_LastUsedURL stopAccessingSecurityScopedResource];
}

@end
Answered by Dielektrik in 683469022

Hello,

This issue does not exists on native XCode generated projects, only on Qt generated XCode projects.

So the closest conclusion that I've came to is different memory management in Qt generated XCode project and native XCode projects. On a native XCode project, if you assign an NSURL object that we get at didPickDocumentsAtURLs method, to a class variable, the object is being kept alive and we can use it even if we leave the didPickDocumentsAtURLs method. However on a Qt generated XCode project, as soon as we leave didPickDocumentsAtURLs method, the NSURL object value is deinitialized.

ARC (Automatic Reference Counting) is disabled on Qt (according to: https://stackoverflow.com/questions/55181370/arc-enabled-for-qt-project-on-macos), and XCode has it enabled by default. My guess that this is the cause for the memory management difference, and there is no way to solve this issue. Or atleast if there is a solution, it's too much of a hassle.

Passing the string value back from c++ and converting it to an NSURL object doesn't work either.

Right. Security-scoped URLs have state that round tripping via a string does not preserve.

I've tried to save the NSURL we get from document picker in a class variable @property NSURL *m_LastUsedURL;,

That will only work if the DocumentPicker object itself persists throughout your access to the security-scoped resource. If not, the the URL will be deallocated when the DocumentPicker object is deallocated.

The rules of the road here are that you must:

  • Ensure that any C++ file access is within a -startAccessingSecurityScopedResource / -stopAccessingSecurityScopedResource pair.

  • Maintain the security-scoped URL for the duration (so that you can call -stopAccessingSecurityScopedResource).

How you do this very much depends on the structure of the rest of your code. For example, you could:

  • Create a heap-allocated Objective-C++ class that stores the NSURL in a data member.

  • Have it export a method that takes a lambda parameter and calls within a -startAccessingSecurityScopedResource / -stopAccessingSecurityScopedResource pair. If you use C++ exceptions, make sure to catch any exceptions and call -stopAccessingSecurityScopedResource in that case as well.

  • Have your document picker code return an instance of this object to your C++ so that it can maintain the object (and hence the NSURL) while it needs access to the file.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Accepted Answer

Hello,

This issue does not exists on native XCode generated projects, only on Qt generated XCode projects.

So the closest conclusion that I've came to is different memory management in Qt generated XCode project and native XCode projects. On a native XCode project, if you assign an NSURL object that we get at didPickDocumentsAtURLs method, to a class variable, the object is being kept alive and we can use it even if we leave the didPickDocumentsAtURLs method. However on a Qt generated XCode project, as soon as we leave didPickDocumentsAtURLs method, the NSURL object value is deinitialized.

ARC (Automatic Reference Counting) is disabled on Qt (according to: https://stackoverflow.com/questions/55181370/arc-enabled-for-qt-project-on-macos), and XCode has it enabled by default. My guess that this is the cause for the memory management difference, and there is no way to solve this issue. Or atleast if there is a solution, it's too much of a hassle.

Save DocumentPicker URL for further use
 
 
Q