skip to Main Content

I am trying to list all PDFs inside a folder that is stored in my Xcode project using SwiftUI, but after trying many different methods I found on here I cannot get it working.

Currently I am using file manager to list the items but when I try and print or list the items found it returns nil.

I have added the PDFs by dragging them into the Xcode project. I have also made sure that they are in my Copy Bundle Resource so I can use FileManager. See below:

enter image description here

Here is my Xcode structure, I am trying to list all items inside PDF. As you can see below the PDFs are stored outside the main folder structure in "/Swift/Products/Detail/Tab5/PDF", so when trying to list the files using Bundle.main.bundlePath, it looks at products.app.

enter image description here

Here is the code where I am trying to use FileManager to find all PDFs inside the folder:

struct ProductTab5View: View {
    
    @State var files = getFiles()

    var body: some View {
        VStack{
            ForEach(0..<files.count, id: .self) { item in
                 Text(files[item])
             }
        }
    }
}

func getFiles() -> Array<String>{
    // Get  document directory url
    let documentsUrl =  FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    var files: [String] = []
    
    do {
        // Get the directory contents urls (including subfolders urls)
        let directoryContents = try FileManager.default.contentsOfDirectory(at: documentsUrl, includingPropertiesForKeys: nil)
        print(directoryContents)
        // filter  directory contents:
        let pdfFiles = directoryContents.filter{ $0.pathExtension == "pdf" }
        let pdfFileNames = pdfFiles.map{ $0.deletingPathExtension().lastPathComponent }
        files.append(contentsOf: pdfFileNames)

    } catch {
        print(error)
    }
    return files
}

2

Answers


  1. There are a few potential problems with your current code.

    The first is this line:

    FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
    

    Unless you have gone through a process separately from what you’ve shown of adding all of these files to the app’s document directory, then there’s no reason to believe that these files will be there. In other words: your Xcode project is not the same as your app’s document directory. Think of the app’s document directory as a place where you may store user-created or perhaps downloaded content as managed by the app — not Xcode itself.

    Next thing to check is whether all of these files are truly added to your target. Check your target’s "Build Phases" -> "Copy Bundle Resources" section to see if they appear there.

    If they do, you can use FileManager, but you have to access the correct directory, which is inside the main bundle — not the app’s user document directory.

    The following answer goes into details about this (including making sure you create folder references): Getting a list of files in the Resources folder – iOS

    The gist will be doing something like this:

    if let files = try? FileManager.default.contentsOfDirectory(atPath: Bundle.main.bundlePath) { //may need to dig into subdirectories
       for file in files {
                //manipulate the file
            }
        }
    

    and then using FileManager to list the documents in that path.

    Note that you can also get all the files (including subdirectories) recursively by doing something like this:

    let url = URL(fileURLWithPath: Bundle.main.bundlePath)
    if let enumerator = FileManager.default.enumerator(at: url, includingPropertiesForKeys: [.isRegularFileKey], options: [.skipsHiddenFiles, .skipsPackageDescendants]) {
        for case let fileURL as URL in enumerator {
            print(fileURL)
        }
    }
    

    By using the above code during your debugging process, this should give you an insight into what your directory structure is really like inside your bundle. Once you’ve figured that out, adjust the previous code sample to be something like:

    if let files = try? FileManager.default.contentsOfDirectory(atPath: Bundle.main.bundlePath + mySubPath) {
       for file in files {
                //manipulate the file
            }
        }
    

    which will give you just the files in the one subdirectory you want once you fill in mySubPath. Note that if you want recursive search, you can use the code sample above.

    You may want to exclude non-file items eventually — see this answer for more details about recursive directory lists in Swift: listing all files in a folder recursively with swift

    Login or Signup to reply.
  2. In such a way resources are copied flat into root application bundle, not user ‘s Documents as it was tried in provided code snapshot (and no PDF sub-directory is created for you).

    So the solution would be just to iterate internal pdf files, like

    func getFiles() -> Array<String> {
        return Bundle.main.urls(forResourcesWithExtension: "pdf", subdirectory: nil)?
            .compactMap { $0.lastPathComponent } ?? []
    }
    

    Tested with Xcode 12.1 / iOS 14.1

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search