Reading files from external storage in iOS 13
Asked Answered
R

1

11

I have an iOS app that is trying to read files from an external storage device without importing them into the App's sandbox.

I have followed Apple's documentations outlined here to do this --

Providing Access to Directories

I'm able to retrieve the selected directory ( which is on an external storage device connected via the Lightning port ) and enumerate the files inside the directory.

However, when I try to do something with those files as per the recommended pattern, I get a failure and basically get permission errors on the file.

        let shouldStopAccessing = pickedFolderURL.startAccessingSecurityScopedResource()
        defer {
          if shouldStopAccessing {
            pickedFolderURL.stopAccessingSecurityScopedResource()
          }
       }
       var coordinatedError:NSError?
       NSFileCoordinator().coordinate(readingItemAt: pickedFolderURL, error: &coordinatedError) { (folderURL) in
        let keys : [URLResourceKey] = [.isDirectoryKey]
        let fileList = FileManager.default.enumerator(at: pickedFolderURL, includingPropertiesForKeys: keys)!
        for case let file as URL in fileList {
            if !file.hasDirectoryPath {
                do {
                    // Start accessing a security-scoped resource.
                    guard file.startAccessingSecurityScopedResource() else {
                        // Handle the failure here.
                        //THIS ALWAYS FAILS!!
                        return
                    }

                    // Make sure you release the security-scoped resource when you are done.
                    defer { file.stopAccessingSecurityScopedResource() }

I should add that this works JUST FINE if the files are on iCloud Drive via Simulator. It fails both on external devices and iCloud Drive on a real device.

Here is a full working project that demonstrates the failure.

  1. Running on simulator accesses iCloud Drive files just fine. But running on device fails.
  2. Running on device to access USB drive fails.
Retarded answered 3/3, 2020 at 0:54 Comment(5)
I do see one major difference between your code and the example code Apple gives; you're saying guard file.startAccessingSecurityScopedResource() else { return }, but Apple says else { continue }. Could that make a difference? You're giving up if the first file fails, but maybe there's something funny about that one file.Cohe
@Cohe -- thanks for the comment. I have tried many different files to be certain.Retarded
I am curious, what is it exactly you want to do with the selected directory? From your code it looks like you want to make mutable writes to all the contents right?Incipient
@DanielGalasko -- no . I just want to read the files there. the directory contains a bunch of mp3 files which I want to read and play.Retarded
Did you tried to copy your files to temp directory at first then read from it?Drench
R
15

So, this seems like a documentation issue with the link posted above. When the user selects a folder, all files and folders are recursively granted access and automatically security scoped. The line guard file.startAccessingSecurityScopedResource() always returns false.

The trick to getting this work is NOT to try to security scope individual files, but to ensure that this code snippet does not run BEFORE you access files.

   defer {
      if shouldStopAccessing {
        pickedFolderURL.stopAccessingSecurityScopedResource()
      }
   }

As long as you continue to access files while the pickedFolderURL is inside security scope, you will be successful.

Hope this helps somebody.

Retarded answered 9/3, 2020 at 0:45 Comment(1)
This was very helpful, thank you. Kept thinking something was broken because startAccessingSecurityScopedResource() would fail on the individual files but nothing was wrong and I already had access to the files.Euratom

© 2022 - 2024 — McMap. All rights reserved.