Home > database >  Model is hashable and Identifiable but still can't use ForEach
Model is hashable and Identifiable but still can't use ForEach

Time:01-08

I've been at it for more than 8h refactoring the code over and over again and I can't get it to work, I have this in my view:

ForEach(allWalletsData.walletList) { walletKey in
    WalletStackView(user: user, walletAddress: walletKey)
}

and it complains saying: value of type 'String' has no member 'walletList' if I try another combination like:

ForEach(allWalletsData.walletList) { walletKey in
    WalletStackView(user: user, walletAddress: walletKey)
}

it says: referencing initializer 'init(_:content:)' on 'ForEach' requires that 'String' conform to 'Identifiable'

The data comes from a Publisher:

@Published var allWalletsData: AllWalletsData = AllWalletsData(walletList: [], walletsData: [:], combinedWalletTokensQuotesFormatted: "")

In my model I've wrote it 1000 different ways and still... doesn't work.

The information in it cannot be static, as I refresh the API, the data will change.

This is the model I've tried:

struct AllWalletsData: Decodable, Encodable {
    init(walletList: [String], walletsData: OrderedDictionary<String, WalletInformation>, combinedWalletTokensQuotesFormatted: String) {
        self.walletList = walletList
        self.walletsData = walletsData
        self.combinedWalletTokensQuotesFormatted = combinedWalletTokensQuotesFormatted
    }

    let walletList: [String]
    let walletsData: OrderedDictionary<String, WalletInformation>
    let combinedWalletTokensQuotesFormatted: String
}

extension AllWalletsData: Hashable, Identifiable {
    var id: [String] { walletList }

    func hash(into hasher: inout Hasher) {
        hasher.combine(combinedWalletTokensQuotesFormatted)
    }

    static func ==(lhs: AllWalletsData, rhs: AllWalletsData) -> Bool {
        if lhs.combinedWalletTokensQuotesFormatted != rhs.combinedWalletTokensQuotesFormatted {
            return false
        }
        return true
    }
}

I've burned out at this point trying to find out what's happening, does anyone know what's going on?

EDIT:

Here's the second part of the model that contains the data using in the ForEach:

struct WalletInformation: Decodable, Encodable {
    init(wallet: Wallet, chainsInfo: [ChainReCalculatedData], allChainsTotalBalanceValue: String) {
        self.wallet = wallet
        self.chainsInfo = chainsInfo
        self.combinedChainsTokensQuotesFormatted = allChainsTotalBalanceValue
    }

    let wallet: Wallet
    let chainsInfo: [ChainReCalculatedData]

    // Combined chain balances
    let combinedChainsTokensQuotesFormatted: String
}

extension WalletInformation: Hashable, Identifiable {
    var id: String { wallet.address }

    public func hash(into hasher: inout Hasher) {
        hasher.combine(wallet)
        hasher.combine(chainsInfo)
        hasher.combine(combinedChainsTokensQuotesFormatted)
    }

    public static func ==(lhs: WalletInformation, rhs: WalletInformation) -> Bool {
        if lhs.wallet != rhs.wallet {
            return false
        }
        if lhs.chainsInfo != rhs.chainsInfo {
            return false
        }
        if lhs.combinedChainsTokensQuotesFormatted != rhs.combinedChainsTokensQuotesFormatted {
            return false
        }
        return true
    }
}

CodePudding user response:

Instead of making AllWalletsData Identifiable, you need to make the individual items of your collection Identifiable or Hashable, or provide an ID.

Your collection is allWalletsData.walletList -- according to your model, this is of type [String]. So, one option is:

ForEach(allWalletsData.walletList, id: \.self) { walletKey in
    WalletStackView(user: user, walletAddress: walletKey)
}

That tells SwiftUI to use the String as the id for each item (which looks like what you were trying to accomplish by doing var id: [String] { walletList }.

Beware that if walletAddress doesn't contain truly unique elements, this (using id: \.self) can lead to unpredictable results. The other route to go would be defining a model for your wallet that had truly unique ids (like a UUID).


Update, based on comments:

It sounds like instead of trying to store separate arrays of keys and dictionaries of your data, you should just have an array of your models. Something like:

struct WalletInformation : Identifiable, Codable {
 var id = //...
 //other properties
} 

struct AllWalletsData: Decodable, Encodable {
  var walletList : [WalletInformation]   
}

//and your ForEach:

ForEach(allWalletsData.walletList) { wallet in
    WalletStackView(user: user, wallet: wallet)
}

  •  Tags:  
  • Related