I'm starting to learn SwiftUI, and so I decided to write a simple password manager as a learning exercise. I've defined a protocol with the common attributes in my passwords:
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 structs that implement this protocol and define the properties of various types of secrets:
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. However, now I'm working on my SwiftUI code. I want to display each item in a List, iterating over an Array of Secret objects using SwiftUI's ForEach. 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? Is there a different way to accomplish this? Is this issue specific to SwiftUI's ForEach? Should I use the standard Swift Array forEach method instead? 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.
Yes, only SwiftUI.ForEach requires the Identifiable conformance, Array.forEach does not require it.
There are several other initialisers of ForEach though that don't require your model type to conform to 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).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With