ForEach requires that '[String : String]' conform to 'Identifiable' - ios

I don't know how to write ForEach in Array in struct by SwiftUI.
struct Group: Identifiable,Decodable{
#DocumentID var id: String?
var name: String
var members: [[String:String]]
}
I want to display member's array by using ForEach.
#ObservedObject var groupVM: GroupViewModel
var body: some View {
NavigationView {
List{
ScrollView{
LazyVStack(alignment: .leading){
ForEach(groupVM.group.members){ member in
groupMemberCell()
}
}
}
}
}
}
I got this error Referencing initializer 'init(_:content:)' on 'ForEach' requires that '[String : String]' conform to 'Identifiable'.
Please tell me how to write the right way.

This is because the value for members (of type [[String: String]]) does not automatically conform to Identifiable.
It depends on your model as to how each individual member (with data [String: String]) needs to be identified, as it's not clear from this dictionary how you would do this (a dictionary is a bunch of keys and values for each member, so how do we know which member is which based off this data?).
I'd suggest modelling each member as its own object and include a field that allows you to uniquely identify each member (such as having an id for each member, which could be their user id in your application for example).
struct Member: Identifiable, Decodable {
var id: String
var data: [String: String]
}
Your group would then look like:
struct Group: Identifiable, Decodable {
#DocumentID var id: String?
var name: String
var members: [Member]
}

Related

Nested Firestore Data Model

I am working on a classified ads app (something like craigslist) I am looking at how best to structure the data model using Firestore (NoSQL Database).
The issue I'm facing is structuring items that have additional fields. For instance, electronic items will have additional fields such as RAM, Storage, etc.
I currently have the following model
enum Category: Codable {
case standard
case phone(Phone)
case tv(TV)
}
struct Item: Codable {
var document_ID: String? = nil
var title: String
var created_at: Int
var category: Category
}
struct Phone: Codable {
var brand: String
var model: String
var RAM: String
var storage: String
}
struct TV: Codable {
var size: Int
var screen_technology: String
}
Although I am not sure this is the best way as the category object is being mapped too many times.

Show sheet with String Binding

Can I show a sheet using a String?
Please let me know if there's a way around it.
#State var selectedContactInfo: String?
.sheet(item: $selectedContactInfo){ item in MessageView(recipient: selectedContactInfo!) }
I'm receiving this error:
Instance method 'sheet(item:onDismiss:content:)' requires that
'String' conform to 'Identifiable'
String could become Identifiable
Its id could be self because String is Hashable
extension String: Identifiable {
public var id: String {self}
}

Conforming an array to Identifiable in SwiftUI

I have a little question about conforming to Identifiable in SwiftUI.
There are situations where we are required to have a given type MyType to conform to Identifiable.
But I am facing a case where I am required to have [MyType] (an array of MyType) to conform to Identifiable.
I have MyType already conforming to Identifiable. What should I do to also make [MyType] to conform to Identifiable?
I suggest embedding [MyType] in a struct, then having the struct conform to Identifiable. Something like this:
struct MyType: Identifiable {
let id = UUID()
}
struct Container: Identifiable {
let id = UUID()
var myTypes = [MyType]()
}
Usage:
struct ContentView: View {
let containers = [
Container(myTypes: [
MyType(),
MyType()
]),
Container(myTypes: [
MyType(),
MyType(),
MyType()
])
]
var body: some View {
/// no need for `id: \.self`
ForEach(containers) { container in
...
}
}
}
You can write an extension to conform an Array to Identifiable.
Since extensions can't contain stored properties, and also because it makes sense that two arrays that are the "same" to also have the same id, you'd need to compute the id based on the contents of an array.
The simplest approach here is if you can conform your type to Hashable:
extension MyType: Hashable {}
This also makes [MyType] conform to Hashable, and since id could be any Hashable, you could use the array itself as its own id:
extension Array: Identifiable where Element: Hashable {
public var id: Self { self }
}
Or, if you want, the id could be an Int:
extension Array: Identifiable where Element: Hashable {
public var id: Int { self.hashValue }
}
Of course, you can do this just for your own type where Element == MyType, but that type would need to be public.

Usage of LazyMapSequence when an array of Strings is excepted

I have a User object
#objc(User)
public class User: NSManagedObject {
#NSManaged public var firstname: String
#NSManaged public var lastname: String
#NSManaged public var country: String
#NSManaged public var friends: NSSet // of User objects
var full: String {
firstname + " " + lastname
}
var friendsArray: [User] {
friends.allObjects as? [User] ?? []
}
}
and at some point I want to map a large array of users (80k objects) to an array of View models
struct ItemViewModel: Hashable {
let id: UUID
let friendsName: [String]
}
Without lazy it takes a long time, so I have opted for the usag of lazy:
func prepareViewModel(users: [User]) -> [ItemViewModel] {
users.map { user in
let friendsName = user.friendsArray.lazy.filter{["en", "fr"].contains($0.country)}.map(\.full)
return ItemViewModel(id: UUID(), friendsName: friendsName)
}
}
But I get an error:
Cannot convert value of type 'LazyMapSequence<LazyFilterSequence<LazySequence<[User]>.Elements>.Elements, String>'
(aka 'LazyMapSequence<LazyFilterSequence<Array<User>>, String>') to expected argument type '[String]'
It makes sense because now the friends names array will be processed lazily later. I have tried to convert the view model struct to hold:
struct ItemViewModel: Hashable {
let id: UUID
let friendsName: LazyMapSequence<LazyFilterSequence<[User]>, String>
}
But now it's not Hashable is there a way to keep the auto-conformance to Hashable when using LazyMapSequence<LazyFilterSequence<[User]>, String> as type for ItemViewModel and any tips on how to improve performance of logic

Does not conform to protocol Decodable

I have the following code which represents a Hockey Stick and some information about it. I have an issue where the stick isn't conforming to Decodable. I understand that every type used in the struct needs to also be codeable, and they are. For some reason however the "var conditions" line causes the error that I am unsure how to fix. Thank you!
enum StickLocation: Int, Codable, Hashable, CaseIterable {
case handle, mid, bottom
}
enum StickCondition: Int, Codable, Hashable, CaseIterable {
case pristine, scuffed, damaged, broken
}
struct HockeyStick: Identifiable, Codable {
var barcode: Int
var brand: String
var conditions: [StickLocation:(condition:StickCondition, note:String?)] // Offending line
var checkouts: [CheckoutInfo]
var dateAdded: Date
var dateRemoved: Date?
// Conform to Identifiable.
var id: Int {
return self.barcode
}
// If the stick was never removed then its in service.
var inService: Bool {
return self.dateRemoved == nil
}
}
The value type of your conditions dictionary is (StickCondition, String?), which is a tuple. Tuples are not Decodable/Encodable, and you cannot make them conform to protocols, so to fix this I recommend you make a new struct to replace the tuple like this:
enum StickLocation: Int, Codable, Hashable, CaseIterable {
case handle, mid, bottom
}
enum StickCondition: Int, Codable, Hashable, CaseIterable {
case pristine, scuffed, damaged, broken
}
struct StickConditionWithNote: Codable, Hashable {
var condition: StickCondition
var note: String?
}
struct HockeyStick: Identifiable, Codable {
var barcode: Int
var brand: String
var conditions: [StickLocation: StickConditionWithNote]
var checkouts: [CheckoutInfo]
var dateAdded: Date
var dateRemoved: Date?
// Conform to Identifiable.
var id: Int {
return self.barcode
}
// If the stick was never removed then its in service.
var inService: Bool {
return self.dateRemoved == nil
}
}

Resources