简体   繁体   English

ForEach 在一系列协议实现上?

[英]ForEach over an array of protocol implementations?

I'm starting to learn SwiftUI, and so I decided to write a simple password manager as a learning exercise.我开始学习 SwiftUI,所以我决定编写一个简单的密码管理器作为学习练习。 I've defined a protocol with the common attributes in my passwords:我在我的密码中定义了一个具有公共属性的protocol

protocol Secret: Codable {
    
    var id: String { get set }
    var name: String { get set }
    var createdDate: Date { get set }
    
    var favorite: Bool { get set }
    
    var category: SecretCategory { get set }
    
    var notes: String { get set }
    
    init(_ id: String, name: String)
    init(_ id: String, name: String, favorite: Bool)

}

Then, I've defined a couple of Swift struct s that implement this protocol and define the properties of various types of secrets:然后,我定义了几个 Swift struct ,它们实现了这个protocol并定义了各种秘密的属性:

struct LoginSecret: Secret, Identifiable {
    
    var id: String
    var name: String
    var createdDate: Date = Date()
    
    var favorite: Bool = false
    
    var category: SecretCategory = .login
    
    var notes: String = ""
    
    var username: String = ""
    var encryptedPassword: String = ""
    
    var websiteURLs: [String] = []
    
    init(_ id: String, name: String) {
        self.id = id
        self.name = name
    }
    
    init(_ id: String, name: String, favorite: Bool) {
        self.id = id
        self.name = name
        self.favorite = favorite
    }
}

...and... ...和...

struct BankAccountSecret: Secret, Identifiable {
    
    var id: String
    var name: String
    var createdDate: Date = Date()
    
    var favorite: Bool = false
    
    var category: SecretCategory = .bankAccounts
    
    var notes: String = ""
    
    var bankName: String = ""
    var nameOnAccount: String = ""
    var routingNumber: String = ""
    var accountNumber: String = ""
    
    var swift: String = ""
    var iban: String = ""
    
    var encryptedPIN: String = ""
    
    var branchPhoneNumber: String = ""
    var branchAddress: Address = Address()
    
    init(_ id: String, name: String) {
        self.id = id
        self.name = name
    }
    
    init(_ id: String, name: String, favorite: Bool) {
        self.id = id
        self.name = name
        self.favorite = favorite
    }
}

These work just fine, and I'm able to Encode/Decode values in JSON for display later.这些工作正常,我可以对 JSON 中的值进行编码/解码,以便稍后显示。 However, now I'm working on my SwiftUI code.但是,现在我正在处理我的 SwiftUI 代码。 I want to display each item in a List , iterating over an Array of Secret objects using SwiftUI's ForEach .我想在List中显示每个项目,使用 SwiftUI 的ForEach遍历Secret对象Array My code looks like this:我的代码如下所示:

struct SecretsListView: View {

    @State    
    var secrets: [Secret]
    
    var body: some View {
        NavigationView {
            List {
               ForEach(secrets) { secret in
                  <snip>
               }
            }
        }
   }
}

However, when I compile I get an error saying...但是,当我编译时,我收到一条错误消息...

Value of protocol type 'Secret' cannot conform to 'Identifiable'; only struct/enum/class types can conform to protocols

Is there no way to iterate over an array of Secret objects?有没有办法遍历Secret对象数组? Is there a different way to accomplish this?有没有不同的方法来实现这一点? Is this issue specific to SwiftUI's ForEach ?这个问题是 SwiftUI 的ForEach特有的吗? Should I use the standard Swift Array forEach method instead?我应该改用标准的 Swift Array forEach方法吗? Any guidance you can provide is greatly appreciated.非常感谢您提供的任何指导。 I've worked with Swift and UIKit for a while, but SwiftUI is new and I want to make sure I'm developing for the framework correctly.我已经使用 Swift 和 UIKit 有一段时间了,但是 SwiftUI 是新的,我想确保我正在为框架正确开发。

Yes, only SwiftUI.ForEach requires the Identifiable conformance, Array.forEach does not require it.是的,只有SwiftUI.ForEach需要Identifiable一致性, Array.forEach不需要它。

There are several other initialisers of ForEach though that don't require your model type to conform to Identifiable . ForEach的其他几个初始化程序虽然不需要您的 model 类型符合Identifiable

You could use init(_ data: Data, id: KeyPath<Data.Element, ID>, content: @escaping (Data.Element) -> Content) for instance (assuming that ID conforms to Hashable , which Secret.id does, since it's a String ).例如,您可以使用init(_ data: Data, id: KeyPath<Data.Element, ID>, content: @escaping (Data.Element) -> Content) (假设ID符合HashableSecret.id确实如此,因为它是一个String )。

ForEach(secrets, id: \.id) { secret in
    <snip>
}

Another solution would be passing the indices of the array to ForEach , then you could use ForEach(secrets.indices) , but since you have a Hashable property on Secret that you can use as the id , that's a the previous one is a better solution.另一种解决方案是将数组的索引传递给ForEach ,然后您可以使用ForEach(secrets.indices) ,但是由于您在Secret上有一个可用作idHashable属性,因此前一个是更好的解决方案.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM