WWDC22 SALE: Save 50% on all my Swift books and bundles! >>

Change in View does not update object/model

Forums > SwiftUI

Hi,

I have a problem where a change in the View is not updating the underlying object in the model. Idea here is to generate a dynamic list of attributes of all different types (string, date, bool) and in the GUI all looks fine but when hitting the Save button, I can see that the data is not updated. What am I missing here?

Full working demo project below:

//
//  ContentView.swift
//  AttributeDemo
//
//  Created by Max on 28.05.22.
//

import SwiftUI

public enum AttributeType: Codable, CaseIterable{
    case int
    case string
    case datetime
    case decimal
    case double
    case boolean

    var stringValue: String {
        switch self {
            case .int: return "Full numbers"
            case .string: return "Text"
        case .datetime: return "Date"
        case .decimal: return "Decimal"
        case .double: return "Double"
        case .boolean: return "Yes/No"
        }
    }
}

public class Attribute: Identifiable, Codable, ObservableObject {
    public var id: UUID = UUID()
    public var bkey: String = ""
    public var tmp_create: Date = Date()
    public var itemBkey: String = ""
    public var attrType: AttributeType = .string
    public var name: String = ""
    public var description: String = ""
    public var value_int: Int = 0
    public var value_string: String = ""
    public var value_datetime: Date = Date()
    public var value_decimal: Decimal = 0.0
    public var value_double: Double = 0.0
    public var value_boolean: Bool = false
    var userBkey: String = ""
    var userToken: String = ""
}

struct ContentView: View {
    @State private var attributes: [Attribute] = []
    @State private var showingAttributeTypes = false

    var body: some View {
        VStack{
            Button("Add attribute"){
                self.showingAttributeTypes.toggle()
            }
            .confirmationDialog("Select a color", isPresented: $showingAttributeTypes, titleVisibility: .visible) {
                            Button("Text") {
                                addAttribute(attributeType: .string)
                            }
                Button("Number") {
                    addAttribute(attributeType: .decimal)
                }
                Button("Date") {
                    addAttribute(attributeType: .datetime)
                }
                Button("Yes/No") {
                    addAttribute(attributeType: .boolean)
                }
                        }

            ForEach(self.attributes){value in
                AttributeView(attribute: value)
            }

            Button("Save"){
                self.attributes.forEach{value in
                    print(value.attrType.stringValue)
                    print(value.value_string)
                    print(value.value_datetime)
                    print(value.value_boolean)
                    print("--------------------------------")
                }
            }
        }
    }

    func addAttribute(attributeType: AttributeType){
        var attribute = Attribute()
        attribute.attrType = attributeType
        self.attributes.append(attribute)
    }
}

struct AttributeView: View {
    @ObservedObject var attribute: Attribute = Attribute()
    @State private var description: String = ""
    @State private var value_boolean: Bool = false
    @State private var value_string: String = ""
    @State private var value_decimal: Decimal = 0.0
    @State private var value_double: Double = 0.0
    @State private var value_datetime: Date = Date()

    var body: some View {
        HStack{
            FormField(fieldName: "Description", fieldValue: $description)
                .keyboardType(.default)
            Spacer()
            switch(attribute.attrType){
            case .boolean:
                Toggle(isOn: $value_boolean) {
                    Label("", image: "")
                }
            case .string:
                TextField("", text: $value_string)
                    .keyboardType(.default)
            case .datetime:
                DatePicker(selection: $value_datetime, displayedComponents: .date, label: { Text("") })
            case .decimal:
                TextField("", value: $value_decimal, format: .number)
                    .keyboardType(.decimalPad)
            case .double:
                TextField("", value: $value_double, format: .number)
                    .keyboardType(.decimalPad)
            default:
                EmptyView()
            }
        }
    }
}

struct FormField: View {
    var fieldName = ""
    @Binding var fieldValue: String

    var body: some View{
        TextField(fieldName, text: $fieldValue)

    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Max

   

What are you expecting to see and what are you actually seeing? From what I can tell, your code is functioning as intended.

   

Let's say I just add a single boolean attribute and set the toggle to On and hit Save, I get

Yes/No

2022-05-28 08:17:13 +0000
false
--------------------------------

in the console output. I would have expected to see

Yes/No

2022-05-28 08:17:13 +0000
true
--------------------------------

   

I am an idiot ... in my AttributeView I am actually not updating the item ...

struct AttributeView: View {
    @ObservedObject var attribute: Attribute = Attribute()
    @State private var description: String = ""
    @State private var value_boolean: Bool = false
    @State private var value_string: String = ""
    @State private var value_decimal: Decimal = 0.0
    @State private var value_double: Double = 0.0
    @State private var value_datetime: Date = Date()

    var body: some View {
        HStack{
            FormField(fieldName: "Description", fieldValue: $description)
                .keyboardType(.default)
            Spacer()
            switch(attribute.attrType){
            case .boolean:
                Toggle(isOn: $value_boolean) {
                    Label("", image: "")
                }
            case .string:
                TextField("", text: $value_string)
                    .keyboardType(.default)
            case .datetime:
                DatePicker(selection: $value_datetime, displayedComponents: .date, label: { Text("") })
            case .decimal:
                TextField("", value: $value_decimal, format: .number)
                    .keyboardType(.decimalPad)
            case .double:
                TextField("", value: $value_double, format: .number)
                    .keyboardType(.decimalPad)
            default:
                EmptyView()
            }
        }
    }
}

needs to change to

struct AttributeView: View {
    @ObservedObject var attribute: Attribute = Attribute()

    var body: some View {
        HStack{
            FormField(fieldName: "Description", fieldValue: $description)
                .keyboardType(.default)
            Spacer()
            switch(attribute.attrType){
            case .boolean:
                Toggle(isOn: $attribute.value_boolean) {
                    Label("", image: "")
                }
            case .string:
                TextField("", text: $attribute.value_string)
                    .keyboardType(.default)
            case .datetime:
                DatePicker(selection: $attribute.value_datetime, displayedComponents: .date, label: { Text("") })
            case .decimal:
                TextField("", value: $attribute.value_decimal, format: .number)
                    .keyboardType(.decimalPad)
            case .double:
                TextField("", value: $attribute.value_double, format: .number)
                    .keyboardType(.decimalPad)
            default:
                EmptyView()
            }
        }
    }
}

and then it works of course ...

   

Hacking with Swift is sponsored by Fernando Olivares

SPONSORED Fernando's book will guide you in fixing bugs in three real, open-source, downloadable apps from the App Store. Learn applied programming fundamentals by refactoring real code from published apps. Hacking with Swift readers get a $10 discount!

Read the book

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.