从SwiftUI ContentView中的@propertyWrapper加载json



我一直在使用一个运行良好的json解码实用程序。我想知道这个实用程序是否可以抽象成一个propertyWrapper,它接受json文件名作为字符串。

ContentView中的呼叫站点如下所示:

struct ContentView: View {
@DataLoader("tracks.json") var tracks: [Tracks]
...

我对属性包装器的粗略描述如下:

@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)
}
}

目前,ContentView的主体显示了这个错误:

无法为表达式生成诊断;请提交错误报告

我喜欢删除一些样板代码的想法,但我认为我在这里缺少了一些基本的东西。

在SwiftUI中,视图经常刷新。当视图被刷新时,@propertyWrapper将再次初始化——这可能是可取的,也可能不是可取的。但值得注意的是。

这里有一个简单的演示,展示了如何创建用于加载JSON文件的属性包装器。为了简单起见,我使用了try?fatalError,但在实际代码中,您可能希望添加适当的错误处理。

@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
}
}

然后,假设您有一个名为items.json:的示例JSON文件

[
{
"name": "Test1",
"count": 32
},
{
"name": "Test2",
"count": 15
}
]

具有相应的结构:

struct Item: Codable {
let name: String
let count: Int
}

您可以在视图中加载JSON文件:

struct ContentView: View {
@DataLoader("items") private var items: [Item]
var body: some View {
Text(items[0].name)
}
}

最新更新