Dom Bryan
Dom Bryan

Reputation: 1268

Get all URLs for resources in sub-directory in Swift

I am attempting to create an array of URLs for all of the resources in a sub-directory in my iOS app. I can not seem to get to the correct path, I want to be able to retrieve the URLs even if I do not know the names (i.e. I don't want to hard code the file names into the code).

Below is a screen shot of the hierarchy, I am attempting to get all the files in the 'test' folder: enter image description here

Any help is greatly appreciated, I have attempted to use file manager and bundle main path but to no joy so far.

This is the only code I have currently:

let fileManager = FileManager.default
let path = Bundle.main.urls(forResourcesWithExtension: "pdf", subdirectory: "Files/test")

print(path)

I have also tried this code but this prints all resources, I can't seem to specify a sub-directory:

let fm = FileManager.default
let path = Bundle.main.resourcePath!

do {
    let items = try fm.contentsOfDirectory(atPath: path)

    for item in items {
        print("Found \(item)")
    }
} catch {
    // failed to read directory – bad permissions, perhaps?
}

Based on an answer from @vadian , The folders were changed from virtual groups to real folders. Using the following code I was able to get a list of resources:

let fileManager = FileManager.default
    let path = Bundle.main.resourcePath

    let enumerator:FileManager.DirectoryEnumerator = fileManager.enumerator(atPath: "\(path!)/Files/test")!
    while let element = enumerator.nextObject() as? String {
        if element.hasSuffix("pdf") || element.hasSuffix("jpg") { // checks the extension
            print(element)
        }
    }

Upvotes: 12

Views: 9436

Answers (3)

Steve Perry
Steve Perry

Reputation: 1

I came across a similar issue today. I needed to retrieve the URL of a resource file in a bundle ignoring its path. I wrote the following:

public extension Bundle {
/// Returns the file URL for the resource identified by the specified name, searching all bundle resources.
/// - Parameter resource: The name of the resource file, including the extension.
/// - Returns: The file URL for the resource file or nil if the file could not be located.
func recurseUrl(forResource resource: String) -> URL? {
    let contents = FileManager.default.allContentsOfDirectory(atPath: self.bundlePath)
    for content in contents {
        let fileNameWithPath = NSString(string: content)
        if let fileName = fileNameWithPath.pathComponents.last {
            if resource == fileName {
                return URL(fileURLWithPath: content)
            }
        }
    }
    return nil
}

Based on this:

public extension FileManager {
/// Performs a deep search of the specified directory and returns the paths of any contained items.
/// - Parameter path: The path to the directory whose contents you want to enumerate.
/// - Returns: An array of String objects, each of which identifies a file or symbolic link contained in path. Returns an empty array if the directory does not exists or has no contents.
func allContentsOfDirectory(atPath path: String) -> [String] {
    var paths = [String]()
    do {
        let url = URL(fileURLWithPath: path)
        let contents = try FileManager.default.contentsOfDirectory(atPath: path)
        
        for content in contents {
            let contentUrl = url.appendingPathComponent(content)
            if contentUrl.hasDirectoryPath {
                paths.append(contentsOf: allContentsOfDirectory(atPath: contentUrl.path))
            }
            else {
                paths.append(contentUrl.path)
            }
        }
    }
    catch {}
    return paths
}

}

Which achieves the goal of retrieving the URL for the first match of a given resource filename in a bundle's resources, all directories wide. I tend to think that Swift's func url(forResource name: String?, withExtension ext: String?) -> URL? should behave this way in the first place.

Upvotes: 0

Quang Tran
Quang Tran

Reputation: 1309

You can follow the following steps to get them:

  1. Create a new folder inside your project folder with the extension is .bundle (for example: Images.bundle).
  2. Copy resource files into that new folder.
  3. Drag that new folder into the project that opening in Xcode.
  4. Retrieve the URLs by using the following code snippet:

    let urls = Bundle.main.urls(forResourcesWithExtension: nil, subdirectory: "Images.bundle")
    

You can also view the guide video here: https://youtu.be/SpMaZp0ReEo

Upvotes: 4

vadian
vadian

Reputation: 285270

Consider that the yellow folders enter image description here are virtual groups, not real folders (although Xcode creates real folders in the project directory). All files in the yellow folders are moved into the Resources directory in the bundle when the app is built.

Real folders in the bundle are enter image description here in the project navigator.

Upvotes: 19

Related Questions