Search code examples
arraysswiftuiswiftdata

Why is ForEach/List working only for some Arrays?


I have a SwiftData Model and recently added a new Array which is holding "Sections" as Strings. No matter what I try I can't make Swift to go trough this Array in any way using ForEach, For or a List. I always get this error:

Generic parameter 'V' could not be inferred

If I use another Array tapas.asanas it works flawlessly.

Here is the Code Snippet, the error is shown at the Section("Sections") Line.

Section("Sections") {
    TextField("Add a new Section", text: $sectionName)
        .padding([.top, .bottom], 10)
    Button(action: {
        tapas.tapasSections.append(sectionName)
        sectionName = ""
    }, label: {
        Text("Add Section")
    })
    
    List(tapas.tapasSections) { name in
        Text("\(name)")
    }
}

And here is the SwiftData Model:

import Foundation
import SwiftData


@Model
class Tapas {
    var name: String
    
    /// Time Frame of the Tapas
    var currentDay: Int
    var lengthInDays: Int = 21
    var lastDay: Date
    var startDay: Date = Date()
    
    var intention: String
    var intentionEvoked: Bool = false
    var rules: String = ""
    var supportTime: [Date] = []
    var tapasSections: [String] = []
    
    var recuperated: Bool
    var tapasType: String
    var isActive: String
    
    var recupType: String                   /// NoRecoup, TimedRecoup, UnlimitedRecoup for No recouperations, Recouperations before Last Day, And even after
    var resetTime: Date
    
    var color: String
    
    /// Adding a relation to TapasAsana, as one Tapas has many Asanas
    @Relationship(deleteRule: .cascade) var asanas = [TapasAsana]()
    
    
    init(name: String = "", intention: String = "", currentDay: Int = 1, lengthInDays: Int = 49, lastDay: Date = .now, recuperated: Bool = false, tapasType: String = "hatha", isActive: String = "dormant", recupType: String = "NoRecoup", resetTime: Date = .now, color: String = "blue") {
        self.name = name
        self.intention = intention
        self.currentDay = currentDay
        self.lengthInDays = lengthInDays
        self.lastDay = lastDay
        self.recuperated = recuperated
        self.tapasType = tapasType
        self.isActive = isActive
        self.recupType = recupType
        self.resetTime = resetTime
        self.color = color
    }
}

As I said before, this relationship Array called asanas is working in any ForEach or List, also in this code above. Replacing tapas.tapasSections with tapas.asanas resolves the error.


Solution

  • The reason this is not compiling is the fact that String is not Identifiable but the initializer for the List you are using needs it to be.

    There are 2 possible solutions here:

    • Make String conform to Identifiable

        extension String: Identifiable{
            public var id: Self{ self }
        }
      
    • Use a different initializer for List

        List(tapas.tapasSections, id: \.self) { name in
            Text("\(name)")
        }
      

    Replacing tapas.tapasSections with tapas.asanas works because the @Model wrapper makes the TapasAsana class conform to PersistentModel which makes it conform to Identifiable.

    Documentation