Home > Back-end >  Use an object to collect form data and set default value in Swift
Use an object to collect form data and set default value in Swift

Time:03-17

I'm brand new in Swift development, and I can't for the life of me figure this out. All I want to do is use an object to collect a forms data, save it to Realm, and then send it to the server via API.

In every example I've found, people are creating a new State variable for each element in their form. This seems unpractical for forms with many fields, so I tried to just create an object with properties that match the form fields I need. If I don't try to set any default values, this works as I expect. But when I try to set some default values for the form in the init(), I get errors that I don't know how to resolve. Here's some partial code:

The object that will be used to collect the form data:

class RecordObject: ObservableObject {
   var routeId: Int?
   var typeId: Int?
   var inDate: Date?
   var outDate: Date?
   var nextDate: Date?
   // .... more properties
}

And what I want to do is in the View, set some default values in the init() that need some logic to derive the value:

In the View:

struct AddLauncherView: View {
    var route: Route // this is passed from a previous view that lists all the routes
    @StateObject var record: RecordObject = RecordObject() // this is where I want to store all the form data
    
    init(){
        _record.routeId = self.route.id // I get the error: Referencing property 'routeId' requires wrapped value of type 'RecordObject'
        self.record.inDate = Date() // this gives the error: 'self' used before all stored properties are initialized
        //self.record.nextDate = here I need to do some date math to figure out the next date
    }

    var body: some View {
        Form{
            DatePicker("In Date", selection: $record.inDate, displayedComponents: .date)
            // .... more form elements
        }
    }
}

I know I could add default values in the RecordObject class, but there are some properties that will need some logic to assign the default value.

Can someone help out a Swift noob and give me some pointers for making this work? Do I really need to create a State var for each form field in the View?

CodePudding user response:

If you did use a class (ObservableObject), you'd want the properties to be annotated with @Published. However, it's probably a better idea to use a struct with a @State variable that contains all of the various properties you need. That may look like this:

struct Record {
    var routeId: Int?
    var typeId: Int?
    var inDate: Date?
    var outDate: Date?
    var nextDate: Date?
}

struct AddLauncherView: View {
    var route: Route
    @State var record: Record
    
    init(route: Route) {
        self.route = route
        _record = State(initialValue: Record(routeId: route.id,
                                             typeId: nil,
                                             inDate: Date(),
                                             outDate: nil,
                                             nextDate: nil))
    }

    var body: some View {
        Form{
            DatePicker("In Date",
                       selection: Binding<Date>(get: {record.inDate ?? Date()}, 
            //custom binding only necessary if inDate is Date? -- if you make it just Date, you can bind directly to $record.inDate
                                                set: {record.inDate = $0}),
                       displayedComponents: .date)
            // .... more form elements
        }
    }
}

CodePudding user response:

You are using the ObservableObject incorrectly. You data model should be a struct, and then the class publishes values that are of the type of the struct, so:

struct Record: Identifiable {
    // First, save yourself some grief later and make it conform to Identifiable
    let id = UUID()
    // The listed variables are probably non-optional in your data model.
    // If they are optional, mark them as optional, otherwise
    var routeId: Int
    var typeId: Int
    var inDate: Date
    // These are more likely optional
    var outDate: Date?
    var nextDate: Date?
    // .... more properties
}

class RecordObject: ObservableObject {
    // Make the source of truth @Published so things are updated in your view
    @Published var records: [Record] = []

    // OR use an init
    @Published var records: [Record]

    init(records: [Records] {
        self.records = records

        // or call some function that imports/creates the records...
    }
}
  • Related