SwiftUI & ChatGPT. The world is changing. Again.
Tags: swift, ios, mobile, programming
Published: 2023-02-07 17:21:03

## Fast app prototyping with SwiftUI & ChatGPT

Everything that follows from this point forward is my input prompts, followed by ChatCGP’s responses, complete with sample code in Swift.

> Hey ChatGPT, can you make a SwiftUI registration form with name, address and city fields?

```swift
import SwiftUI

struct RegistrationForm: View {
    @State private var name: String = ""
    @State private var address: String = ""
    @State private var city: String = ""

    var body: some View {
        Form {
            TextField("Name", text: $name)
            TextField("Address", text: $address)
            TextField("City", text: $city)
        }
    }
}
```

The `RegistrationForm` struct conforms to the `View` protocol and defines a `body` property that returns a `Form` view. The `Form` view contains `TextField` views for each of the form fields, and the `@State` property wrapper is used to manage the state of each field.

> Can you move the fields to a view model?

Move the form fields to a separate view model in order to better manage the state and logic of the form.

```swift
import SwiftUI

struct RegistrationForm: View {
    @ObservedObject var viewModel: RegistrationFormViewModel

    var body: some View {
        Form {
            TextField("Name", text: $viewModel.name)
            TextField("Address", text: $viewModel.address)
            TextField("City", text: $viewModel.city)
        }
    }
}

class RegistrationFormViewModel: ObservableObject {
    @Published var name: String = ""
    @Published var address: String = ""
    @Published var city: String = ""
}
```

The `RegistrationForm` struct has an `@ObservedObject` property that references an instance of the `RegistrationFormViewModel` class. This allows the `RegistrationForm` view to bind to the properties of the view model using the `$` prefix, just as it did with the `@State` properties in the previous example.

The `RegistrationFormViewModel` class defines `@Published` properties for each of the form fields, which allows the view to be notified when the values of these properties change. This allows the view to update automatically when the user changes the form fields.

This approach helps to keep the view logic and state management separate from the view itself, making the code easier to understand and maintain.


> What if I need to validate that none of the fields are empty?

If you need to validate that none of the fields in the form are empty, you can add a method to the `RegistrationFormViewModel` class that checks the values of the form fields and returns a `Bool` indicating whether the form is valid or not.

```swift
class RegistrationFormViewModel: ObservableObject {
    @Published var name: String = ""
    @Published var address: String = ""
    @Published var city: String = ""

    var isValid: Bool {
        return !name.isEmpty && !address.isEmpty && !city.isEmpty
    }
}
```

The `isValid` property uses the `isEmpty` property of each of the form fields to determine whether the form is valid or not. If all of the fields are not empty, the `isValid` property will return `true`, otherwise it will return `false`.

You can then use this property in the view to enable or disable a submit button or to display an error message if the form is not valid.

```swift
struct RegistrationForm: View {
    @ObservedObject var viewModel: RegistrationFormViewModel

    var body: some View {
        Form {
            TextField("Name", text: $viewModel.name)
            TextField("Address", text: $viewModel.address)
            TextField("City", text: $viewModel.city)

            if !viewModel.isValid {
                Text("All fields are required")
                    .foregroundColor(.red)
            }
        }
    }
}
```

The `RegistrationForm` view checks the value of the `isValid` property and, if the form is not valid, it displays an error message using a `Text` view. This allows the user to know which fields need to be filled out in order to make the form valid.

> Can we add a Save button to that form?

Add a Save button to the form by using a `Button` view in the `RegistrationForm` struct.

```swift
struct RegistrationForm: View {
    @ObservedObject var viewModel: RegistrationFormViewModel

    var body: some View {
        Form {
            TextField("Name", text: $viewModel.name)
            TextField("Address", text: $viewModel.address)
            TextField("City", text: $viewModel.city)

            if !viewModel.isValid {
                Text("All fields are required")
                    .foregroundColor(.red)
            }

            Button(action: {
                // Save the form data here
            }) {
                Text("Save")
            }
            .disabled(!viewModel.isValid)
        }
    }
}
```

The `RegistrationForm` view includes a `Button` view with an action closure that is called when the button is tapped. This closure can be used to save the form data to a database or other storage location.

The `Button` view also has a `disabled` modifier that disables the button if the `isValid` property of the `RegistrationFormViewModel` is `false`. This ensures that the form must be filled out correctly before the user can save the data.

> Can you create a User struct based on the form information?

A `User` struct that could be used to represent the data from the form:

```swift
struct User {
    var name: String
    var address: String
    var city: String
}
```

This User struct defines properties for each of the form fields, such as `name`, `address` and `city`. These properties can be used to store the values entered by the user in the form.

Create an instance of the `User` struct using the values from the form:

```swift
let user = User(
    name: viewModel.name,
    address: viewModel.address,
    city: viewModel.city
)
```

The `user` constant is initialized with the values from the `RegistrationFormViewModel` instance. You can then use this `User` instance to save the data to a database or other storage location, or to pass the data to another part of your app for further processing.

> And how do I populate the fields on the view model from the struct?

```swift
class RegistrationFormViewModel: ObservableObject {
    @Published var name: String = ""
    @Published var address: String = ""
    @Published var city: String = ""

    var isValid: Bool {
        return !name.isEmpty && !address.isEmpty && !city.isEmpty
    }

    func populate(with user: User) {
        name = user.name
        address = user.address
        city = user.city
    }
}
```

Use this method to populate the form fields:

```swift
let user = User(
    name: "Oliver Doe",
    address: "100 Main St",
    city: "Anytown"
)

viewModel.populate(with: user)
```

The `user` constant is initialized with some sample data, and then the `populate(with:)` method is called on the `viewModel` instance, passing in the `user` instance as a parameter. This will set the values of the view model's properties to match the values of the `User` instance, which will in turn populate the form fields with the data from the `User` instance.

---

**Contacts**
I have a clear focus on time-to-market and don't prioritize technical debt. And I took part in the Pre-Sale/RFX activity as a System Architect, assessment efforts for Mobile (iOS-Swift, Android-Kotlin), Frontend (React-TypeScript) and Backend (NodeJS-.NET-PHP-Kafka-SQL-NoSQL). And I also formed the work of Pre-Sale as a CTO from Opportunity to Proposal via knowledge transfer to Successful Delivery.

🛩️ #startups #management #cto #swift #typescript #database
📧 Email: sergey.leschev@gmail.com
👋 LinkedIn: https://linkedin.com/in/sergeyleschev/
👋 LeetCode: https://leetcode.com/sergeyleschev/
👋 Twitter: https://twitter.com/sergeyleschev
👋 Github: https://github.com/sergeyleschev
🌎 Website: https://sergeyleschev.github.io
🌎 Reddit: https://reddit.com/user/sergeyleschev
🌎 Quora: https://quora.com/sergey-leschev
🌎 Medium: https://medium.com/@sergeyleschev