将@Observable添加到类中会导致不可变属性不会被解码......SwiftUI中的警告

问题描述 投票:0回答:1

我有一个非常简单的应用程序。它从 JSON 文件加载程序员数据,对其进行解码并在文本视图中显示程序员的姓名、地址和城市。

struct DecodeJson: View {   
    @State var decodedText: String = ""
    @State var encodedText: String = ""
    @State var programmers = Programmers()
    
    var body: some View {
        VStack {
            Button("Load JSON") {
                decodedText = ""
                programmers = load("programmers.json")
                for programmer in programmers.items {
                    decodedText.append("\(programmer.name), \(programmer.address.street), \(programmer.address.city)\n")
                }
            }
            Text(decodedText)
       }
    }
}

class Programmers: Codable, Identifiable {
    var items: [Programmer] = [
        Programmer(id: 1, name: "Programmer1", address: Address(street: "Street1", city: "City1"), changed: false),
        Programmer(id: 2, name: "Programmer2", address: Address(street: "Street2", city: "City2"), changed: false),
    ]
}

class Programmer: Codable, Identifiable {
    var id: Int
    var name: String
    var address: Address
    var changed: Bool
    
    init(id: Int, name: String, address: Address, changed: Bool) {
        self.id = id
        self.name = name
        self.address = address
        self.changed = changed
    }
}

class Address: Codable, Identifiable {
    var street: String
    var city: String
    
    init(street: String, city: String) {
        self.street = street
        self.city = city
    }
}

这效果很好,但我需要一个程序员编辑器视图,而不是在简单的文本框中显示程序员数据,因为我想编辑程序员并将更改保存回 JSON 文件。所以我需要将程序员的姓名和地址双向绑定到编辑器视图。我应该使用 @Observale 包装器包装 3 个类,并在编辑器视图中使用 @Bindable。

问题是,当我将 @Observale 包装器添加到 Address 类并构建应用程序时,我会收到一个模糊的警告:不可变属性将不会被解码,因为它是用无法覆盖的初始值声明的 .

// Added @Observable
@Observable class Address: Codable, Identifiable {
    var street: String
    var city: String
    
    init(street: String, city: String) {
        self.street = street
        self.city = city
    }
}
  1. 我没有不可变的财产。城市和街道都是可变的。
  2. 没有任何变量被初始化。 非常好的错误消息!

如果我使用 @Observale 包装类运行应用程序,那么它不会加载 JSON 文件,即使它报告 JSON 文件中的错误,该文件在没有 @Observale 包装器的情况下加载正常...

有人懂这个吗? 感谢您的帮助!

我重写了应用程序,至少10次,我尝试在Apple甚至stackoverflow上搜索错误消息,但我找不到任何解决方案。我需要帮助。谢谢大家!

swiftui binding observable
1个回答
0
投票

Codable
一致性的综合发生在宏扩展之后,因此错误消息实际上是在谈论由
@Observable
宏生成的代码,这就是它如此令人困惑的原因。也就是说,它正在谈论观察登记员:

@ObservationIgnored private let _$observationRegistrar = Observation.ObservationRegistrar()

@Observable
还将所有存储的属性转换为计算属性,并生成带有下划线前缀的新存储属性。这就是解码失败的原因,因为 JSON 中没有下划线前缀的键。

我会手动实现

init(from:)
encode(to:)
。 Xcode 15 实际上会在自动完成过程中为您生成这些内容。添加
@Observable
之前,请开始在班级中输入
init
encode
,然后选择末尾带有
{ ... }
的选项。

所有 3 个类的完整代码:

class Programmers: Codable, Identifiable {
    var items: [Programmer] = [
        Programmer(id: 1, name: "Programmer1", address: Address(street: "Street1", city: "City1"), changed: false),
        Programmer(id: 2, name: "Programmer2", address: Address(street: "Street2", city: "City2"), changed: false),
    ]
    
    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.items = try container.decode([Programmer].self, forKey: .items)
    }
    
    enum CodingKeys: CodingKey {
        case items
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(self.items, forKey: .items)
    }
}

@Observable
class Programmer: Codable, Identifiable {
    var id: Int
    var name: String
    var address: Address
    var changed: Bool
    
    init(id: Int, name: String, address: Address, changed: Bool) {
        self.id = id
        self.name = name
        self.address = address
        self.changed = changed
    }
    
    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.id = try container.decode(Int.self, forKey: .id)
        self.name = try container.decode(String.self, forKey: .name)
        self.address = try container.decode(Address.self, forKey: .address)
        self.changed = try container.decode(Bool.self, forKey: .changed)
    }
    
    enum CodingKeys: CodingKey {
        case id
        case name
        case address
        case changed
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(self.id, forKey: .id)
        try container.encode(self.name, forKey: .name)
        try container.encode(self.address, forKey: .address)
        try container.encode(self.changed, forKey: .changed)
    }
}

@Observable
class Address: Codable, Identifiable {
    var street: String
    var city: String
    
    init(street: String, city: String) {
        self.street = street
        self.city = city
    }
    
    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.street = try container.decode(String.self, forKey: .street)
        self.city = try container.decode(String.self, forKey: .city)
    }
    
    enum CodingKeys: CodingKey {
        case street
        case city
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(self.street, forKey: .street)
        try container.encode(self.city, forKey: .city)
    }
}

也就是说,我不明白为什么你需要它们作为类。这些类看起来代表了应用程序的简单数据,并且完全可以是结构。您应该尝试在 SwiftUI 中尽可能多地使用结构体。 SwiftUI 可以检测结构中的变化,而不需要任何额外的东西,比如

@Observable
。唯一需要类的地方是当你需要它持久地保留在内存中时,例如其他班级的代表,例如
CLLocationManager

© www.soinside.com 2019 - 2024. All rights reserved.