Skip to content

Using with an async function...

ConfusedVorlon edited this page Sep 3, 2021 · 1 revision

The standard functions perform the work in a block which is wrapped in the 'security dance' of startAccessingSecurityScopedResource, stopAccessingSecurityScopedResource

This example shows how you can still use SandboxFileAccess when you're going to be doing work in your own async function. You just need to do the security dance manually.

Additionally - it doesn't assume that access is provided through a bookmark (you might have powerbox access)

This case is a wallpaper app that needs to pick a file from folder, then do some async work with it before applying it to the system

func changeFromFileSystem(completion:@escaping WpAutoChangeCompletion) {

    //Get access info and check that we have read only access in one form or another
    let accessInfo = SandboxFileAccess().accessInfo(forFileURL: folder)
    guard accessInfo.permissions.meets(required: .anyReadOnly) else {
        completion(nil,.failure(Fail.unableToAccessFolder))
        return
    }
    
    //We'll need to release our security access when we clean up
    //We can't do this in a defer because we're going to do our work in an async function
    var releaseSecurityAccess:(()->Void)? = nil
    
    //we might not have a security scoped File URL
    //this will happen in the case where we don't have a stored bookmark
    //but we do have access to the folder anyway by normal powerbox permissions
    //e.g. a folder within our sandbox, or the pictures folder if we have that entitlement
    //if we do have a security scoped URL then we need to do the security access dance
    if let securityScopedFileURL = accessInfo.securityScopedURL {
        guard securityScopedFileURL.startAccessingSecurityScopedResource() == true else {
            completion(nil,.failure(Fail.unableToAccessFolder))
            return
        }
        
        //we'll need to release access after our async function has completed
        releaseSecurityAccess = {
            securityScopedFileURL.stopAccessingSecurityScopedResource()
        }
    }
    
    //Don't access the securityScopedFileURL - that might be a parent of our folder
    guard let folderPath = randomFile(url:folder) else {
        completion(nil,.failure(Fail.noPossibleWallpaper))
        return
    }

    guard let source = WpSource(path: folderPath) else {
        completion(nil,.failure(Fail.unableToLoadWallpaper))
        DDLogError("Unable to load wallpaper from \(folderPath)")
        return
    }

    source.apply(saveToHistory: true) {
                   result in
        completion(source, result)
        releaseSecurityAccess?()
               }
}
Clone this wiki locally