skip to Main Content

Hi everyone !

I’m using SwiftData with a View designed to update the @Model.

Here is my Data :

@Model
final class Pet {
    var name: String
    var gender: Gender
    var type: String
    var race: String
    var birthdate: Date
    var color: String
    var eyeColor: String
    var photo: Data?
    var identification: Identification?

    init(
        name: String,
        gender: Gender,
        type: String,
        race: String,
        birthdate: Date,
        color: String,
        eyeColor: String,
        photo: Data?
    ) {
        self.name = name
        self.gender = gender
        self.type = type
        self.race = race
        self.birthdate = birthdate
        self.color = color
        self.eyeColor = eyeColor
        self.photo = photo
    }
}
@Model
final class Identification {
    var chip: Int?
    var chipLocation: String?
    var tatoo: String?
    var tatooLocation: String?

    init(chip: Int?, chipLocation: String?, tatoo: String?, tatooLocation: String?) {
        self.chip = chip
        self.chipLocation = chipLocation
        self.tatoo = tatoo
        self.tatooLocation = tatooLocation
    }
}

As you can see, I have a type called Identification, nested in the Pet model, and it is optional.
Furthermore, all of its property are optional too.

I’m trying to edit the chip property in the following way :

struct EditInformationView: View {
    @Environment(Pet.self) var pet

    var body: some View {
        @Bindable var pet = pet

        Form {
            Section("Identification") {
                TextField("Chip", value: $pet.identification?.chip)
            }
        }
    }
}

My question is really simple, how can I do to edit the chip parameter with my TextField ?

I’m a bit lost here and would appreciate a little help, thanks a lot 🙂

I tried to to optional chaining, like Xcode suggested me but it didn’t worked.

2

Answers


  1. You could try this simple approach using an
    intermediate variable and .onSubmit,
    as shown in the example code:

    struct EditInformationView: View {
        @Environment(Pet.self) var pet
    
        @State private var chipVal = 0  // <--- here
    
        var body: some View {
            @Bindable var pet = pet
    
            Form {
                Section("Identification") {
                    TextField("Chip", value: $chipVal, format: .number)  // <--- here
                        .onSubmit {
                            pet.identification?.chip = chipVal  // <--- here
                        }
                }
            }
            .onAppear {
                if let chipv = pet.identification?.chip {  // <--- here
                    chipVal = chipv
                }
            }
        }
    }
    

    Note, you can also use .onChange(of: chipVal)

    Login or Signup to reply.
  2. You can use any of the "Optional Binding solutions"

    Option 1

    struct EditInformationView: View {
        @Environment(Pet.self) var pet
    
        var body: some View {
            @Bindable var pet = pet
    
            Form {
                Section("Identification") {
                    if let identification = Binding($pet.identification) {
                        TextField("Chip", value: identification.chip, format: .number)
                    } else {
                        ProgressView()
                            .task {
                                pet.identification = Identification(chip: nil, chipLocation: nil, tatoo: nil, tatooLocation: nil)
                            }
                    }
                }
            }
        }
    }
    

    Option 2

    struct EditInformationView: View {
        @Environment(Pet.self) var pet
        
        var body: some View {
            @Bindable var pet = pet
            
            Form {
                Section("Identification") {
                    TextField("Chip", value: ($pet.identification ?? Identification(chip: nil, chipLocation: nil, tatoo: nil, tatooLocation: nil)).chip, format: .number)
                    
                }
            }
        }
    }
    
    @MainActor func ?? <Element> (lhs: Binding<Element?>, rhs: Element) -> Binding<Element> {
        Binding {
            lhs.wrappedValue ?? rhs
        } set: { newValue in
            lhs.wrappedValue = newValue
        }
        
    }
    

    There are several others if you search SO.

    The essence is dealing with identification before you deal with its properties. There are infinite ways of dealing with this.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search