The first step in this project will be to create an ordering screen that takes the basic details of an order: how many cupcakes they want, what kind they want, and whether there are any special customizations.
Before we get into the UI, we need to start by defining the data model. Previously we’ve mixed structs and classes to get the right result, but here we’re going to take a different solution: we’re going to have a single class that stores all our data, which will be passed from screen to screen. This means all screens in our app share the same data, which will work really well as you’ll see.
For now this class won’t need many properties:
Each of those need to update the UI when changed, which means we need to make sure the class uses the @Observable
macro.
So, please make a new Swift file called Order.swift, change its Foundation import for SwiftUI, and give it this code:
@Observable
class Order {
static let types = ["Vanilla", "Strawberry", "Chocolate", "Rainbow"]
var type = 0
var quantity = 3
var specialRequestEnabled = false
var extraFrosting = false
var addSprinkles = false
}
We can now create a single instance of that inside ContentView
by adding this property:
@State private var order = Order()
That’s the only place the order will be created – every other screen in our app will be passed that property so they all work with the same data.
We’re going to build the UI for this screen in three sections, starting with cupcake type and quantity. This first section will show a picker letting users choose from Vanilla, Strawberry, Chocolate and Rainbow cakes, then a stepper with the range 3 through 20 to choose the amount. All that will be wrapped inside a form, which is itself inside a navigation stack so we can set a title.
There’s a small speed bump here: our cupcake topping list is an array of strings, but we’re storing the user’s selection as an integer – how can we match the two? One easy solution is to use the indices
property of the array, which gives us a position of each item that we can then use with as an array index. This is a bad idea for mutable arrays because the order of your array can change at any time, but here our array order won’t ever change so it’s safe.
Put this into the body of ContentView
now:
NavigationStack {
Form {
Section {
Picker("Select your cake type", selection: $order.type) {
ForEach(Order.types.indices, id: \.self) {
Text(Order.types[$0])
}
}
Stepper("Number of cakes: \(order.quantity)", value: $order.quantity, in: 3...20)
}
}
.navigationTitle("Cupcake Corner")
}
The second section of our form will hold three toggle switches bound to specialRequestEnabled
, extraFrosting
, and addSprinkles
respectively. However, the second and third switches should only be visible when the first one is enabled, so we’ll wrap then in a condition.
Add this second section now:
Section {
Toggle("Any special requests?", isOn: $order.specialRequestEnabled)
if order.specialRequestEnabled {
Toggle("Add extra frosting", isOn: $order.extraFrosting)
Toggle("Add extra sprinkles", isOn: $order.addSprinkles)
}
}
Go ahead and run the app again, and try it out.
However, there’s a bug, and it’s one of our own making: if we enable special requests then enable one or both of “extra frosting” and “extra sprinkles”, then disable the special requests, our previous special request selection stays active. This means if we re-enable special requests, the previous special requests are still active.
This kind of problem isn’t hard to work around if every layer of your code is aware of it – if the app, your server, your database, and so on are all programmed to ignore the values of extraFrosting
and addSprinkles
when specialRequestEnabled
is set to false. However, a better idea – a safer idea – is to make sure that both extraFrosting
and addSprinkles
are reset to false when specialRequestEnabled
is set to false.
We can make this happen by adding a didSet
property observer to specialRequestEnabled
. Add this now:
var specialRequestEnabled = false {
didSet {
if specialRequestEnabled == false {
extraFrosting = false
addSprinkles = false
}
}
}
Our third section is the easiest, because it’s just going to be a NavigationLink
pointing to the next screen. We don’t have a second screen, but we can add it quickly enough: create a new SwiftUI view called “AddressView”, and give it an order
property like this:
struct AddressView: View {
var order: Order
var body: some View {
Text("Hello World")
}
}
#Preview {
AddressView(order: Order())
}
We’ll make that more useful shortly, but for now it means we can return to ContentView.swift and add the final section for our form. This will create a NavigationLink
that points to an AddressView
, passing in the current order object.
Please add this final section now:
Section {
NavigationLink("Delivery details") {
AddressView(order: order)
}
}
That completes our first screen, so give it a try one last time before we move on – you should be able to select your cake type, choose a quantity, and toggle all the switches just fine.
SAVE 50% All our books and bundles are half price for Black Friday, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.
Link copied to your pasteboard.