I've been using a json decoding utility that works great. I'm wondering if that utility can be abstracted into a propertyWrapper that accepts the json file name as a string.
The call site in the ContentView looks like this:
struct ContentView: View {
@DataLoader("tracks.json") var tracks: [Tracks]
...
My rough sketch of the property wrapper looks like this:
@propertyWrapper
struct DataLoader<T: Decodable>: DynamicProperty {
private let fileName: String
var wrappedValue: T {
get {
return Bundle.main.decode(T.self, from: fileName)
}
set {
//not sure i need to set anything since i just want to get the array
}
}
init(_ fileName: String) {
self.fileName = fileName
wrappedValue = Bundle.main.decode(T.self, from: fileName)
}
}
Currently the body of the the ContentView shows this error:
Failed to produce diagnostic for expression; please file a bug report
I like the idea of removing some boilerplate code, but I think I'm missing something fundamental here.
In SwiftUI views are refreshed very often. When a view is refreshed then the @propertyWrapper
will be initialised again - this might or might not be desirable. But it's worth noting.
Here is a simple demo showing how to create a property wrapper for loading JSON files. For simplicity I used try?
and fatalError
but in the real code you'll probably want to add a proper error handling.
@propertyWrapper
struct DataLoader<T> where T: Decodable {
private let fileName: String
var wrappedValue: T {
guard let result = loadJson(fileName: fileName) else {
fatalError("Cannot load json data \(fileName)")
}
return result
}
init(_ fileName: String) {
self.fileName = fileName
}
func loadJson(fileName: String) -> T? {
guard let url = Bundle.main.url(forResource: fileName, withExtension: "json"),
let data = try? Data(contentsOf: url),
let result = try? JSONDecoder().decode(T.self, from: data)
else {
return nil
}
return result
}
}
Then, assuming you have a sample JSON file called items.json
:
[
{
"name": "Test1",
"count": 32
},
{
"name": "Test2",
"count": 15
}
]
with a corresponding struct:
struct Item: Codable {
let name: String
let count: Int
}
you can load your JSON file in your view:
struct ContentView: View {
@DataLoader("items") private var items: [Item]
var body: some View {
Text(items[0].name)
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.