Loading resources

Do I really have to use Bundle to use resources? Or can it be done easier?

I have this JSON file resource in my Playground and am wondering about the least amount of code necessary to load its data into a dictionary.

I’m currently using this Bundle extension:

extension Bundle {
    func decode(_ file: String) -> [String: MyType] {
        guard let url = self.url(forResource: file, withExtension: nil) else {
            fatalError("Failed to locate \(file) in bundle.")
        }
        guard let data = try? Data(contentsOf: url) else {
            fatalError("Failed to load \(file) form bundle.")
        }
        let decoder = JSONDecoder()
        guard let loaded = try? decoder.decode([String: MyType].self, from: data) else {
            fatalError("Failed to decode \(file) from bundle.")
        }
        return loaded
    }
}

This type:

struct MyType: Codable, Identifiable {
    let id: String
    let name: String
    let description: String
}

Using this to load it into a struct:

let myData = Bundle.main.decode("MyData.json")

There must be an easier way to access resources in Playgrounds… like image assets for example, it’s just Image(“My Image File Name Here”) and done…

Answered by DTS Engineer in 709405022

like image assets for example

The image subsystem has specific support for loading named assets. Similarly for the text subsystem and so on. There is no specific support like this for codable types. Adding an extension on Bundle, like you’ve shown, is a perfectly reasonable way to do that.

One thing you might do is make this generic:

extension Bundle {

    func decode<Value: Decodable>(_ type: Decodable.Protocol, from name: String) -> Value? {
        let decoder = JSONDecoder()
        guard
            let u = self.url(forResource: name, withExtension: "json"),
            let d = try? Data(contentsOf: u),
            let v = try? decoder.decode(Value.self, from: d)
        else {
            return nil
        }
        return v
    }
}

That way you can load any Decodable value with the same code.

Share and Enjoy

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

Accepted Answer

like image assets for example

The image subsystem has specific support for loading named assets. Similarly for the text subsystem and so on. There is no specific support like this for codable types. Adding an extension on Bundle, like you’ve shown, is a perfectly reasonable way to do that.

One thing you might do is make this generic:

extension Bundle {

    func decode<Value: Decodable>(_ type: Decodable.Protocol, from name: String) -> Value? {
        let decoder = JSONDecoder()
        guard
            let u = self.url(forResource: name, withExtension: "json"),
            let d = try? Data(contentsOf: u),
            let v = try? decoder.decode(Value.self, from: d)
        else {
            return nil
        }
        return v
    }
}

That way you can load any Decodable value with the same code.

Share and Enjoy

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

Loading resources
 
 
Q