Projects People Resources Semesters Blog About
Building Your First App (Wolly’s Mobile)
👣

Building Your First App (Wolly’s Mobile)

Last Updated: 22 September, 2022

Getting Started

To get started, click Create a new Xcode project. With App selected, click Next. Name your first app, and make sure your interface is SwiftUI. Click Next and then choose a place to store your project. Click Create and your project should open up.

image

When you first open Xcode you should see something like this:

image
  • The left sidebar is your File Browser where you can see files associated with your project.
  • The second column is the Code Editor where you can edit the code in each file.
  • The third column is your Previews. After loading for a bit, you’ll see an iPhone with your project being run inside. If this section never stops loading, it’s worth quitting and reopenning Xcode (get used to doing this when things stop working).
  • The rightmost column shows the Attributes Inspector. You’ll use this different tabs in this column to edit specific attributes of certain files.

Writing your first Swift code

  • Xcode should open your project to the ContentView.swift file. Inside, you’ll see a struct named ContentView of type View. Inside, is a computed property named body of type some View; meaning the body can be any type of view (Text, Image, VStack, etc.). Right now, the body is a VStack.
  • All objects in SwiftUI are eventually Views. Keep this in mind if you ever are trying to return a String or an Integer instead of a Text view for example.
  • struct ContentView: View {
        var body: some View {
            VStack {
                Image(systemName: "globe")
                    .imageScale(.large)
                    .foregroundColor(.accentColor)
                Text("Hello, world!")
            }
            .padding()
        }
    }
  • Let’s start by modifying the Text view. You can add .font(.title) and .foregroundColor(.green) below it, just like .imageScale(...) and .foregroundColor(...) on the Image view above it. Your text should now be larger and in green. You’ve written your first SwiftUI code!

Combining Views Using Stacks

How body works

Let’s clear our ContentView’s body property and start building the foundations of a user interface. We’re going to build one row of a List that we’ll create later. Your ContentView should look like this (and Xcode might have throw a ton of errors):

struct ContentView: View {
    var body: some View {
        
    }
}

If Xcode is throwing errors, it’s because the body property needs to return a view, but at the moment it’s empty so it’s not returning anything. Let’s fix this.

We want to display more than one item at a time so we’ll need to use some structures which let us combine more than one view into a single view. The most common form of these are Stacks. SwiftUI has three types of Stack, the HStack, VStack, and ZStack. They let you layout content in Horizontal, Vertical, and Z-direction space.

Since we’re building a row for a list, let’s layout our content horizontally using a HStack. Add HStack {} to your body property.

Populating body

Now, let’s populate our layout with some content. We’re going to build a Wollaston’s Bakery catalogue, so let’s think through what this row might need. It’s going to represent an item they offer, and when we tap on the row it will show a detali view with more information about the item.

Helpful information to put on this list row might be an Image to show the type of item, some Text to show the name of the item, and on the far right edge we can show the price.

To our body property let’s add an Image(systemName: “”), Text(""), Spacer(), and another Text(""). Then we’ll walk through each item one-by-one.

Image and SFSymbols

Starting with the Image. We used the systemName initializer because we want to access the large library of SFSymbols. These are built in images you can use in your apps which scale with fonts and are easy to add.

Type cmd shift L and navigate to the ⭐ section to see the entire list of SFSymbols. To represent an item, let’s use “tag.fill”.

Your image property should look like this: Image(systemName: "tag.fill") and your Preview might show an image of a tag.

Text and basic styling

Text is one of the most common Views you’ll use in SwiftUI. In its most basic form, it accepts a String and displays it using attributes you describe through modifiers.

Let’s represent Wollaston’s best muffin, the French Toast muffin as text: Text("French Toast Muffin").

This item’s important, so let’s modify the font a little bit using the .font(...) modifier. Typing . between the parentheses will display a list of autocomplete font options. Let’s choose .headline.

Your Text should look like this now:

Text("French Toast Muffin")
	.font(.headline)

Spacers, Padding, and Frame

Between your two text views we’ve added a Spacer(). Most views in SwiftUI take up only the space they need, but spacers are special because they work in the opposite way. Spacers fill up all the available space given to them. In this case, the spacer pushes our Text and Image views towards each edge. You can modify how much space items take up using the .frame(...) modifier.

If you want to add a little space around an item, you can use .padding(). Let’s place a padding modifier on our HStack to give it a little breathing room from the edges of the screen.

More text styling and Foreground Color

Before we move on, let’s add a price too. Muffins are “$2.50” at Wolly’s.

If you want a little more control over your text styling, it might be worth using the .font(.system(style:,design:,weight:) modifier. This modifier lets you define the size (style), design (rounded, monospaced, serif, or sans-serif [default]), and the weight (regular, bold, heavy, thin, etc.)

Since we want the price to have less significance than the name, let’s modify that text view using: .font(.system(.caption, design: .monospaced, weight: .regular)).

We also want to change the color so that the color reflects its importance too. To do this we can use .foregroundColor(.secondary). Secondary is a built in color that’s a gray which adjusts based on light and dark mode. This is what your price Text view should look like:

Text("$2.50")
	.font(.system(.caption, design: .monospaced, weight: .regular))
  .foregroundColor(.secondary)

After populating our body, here’s what we should have in ContentView. I’ve added a .foregroundColor(.pink) to the Image to add a small pop of color.

struct ContentView: View {
    var body: some View {
        HStack {
            Image(systemName: "tag.fill")
                .foregroundColor(.pink)
            
            Text("French Toast Muffin")
                .font(.headline)
            
            Spacer()
            
            Text("$2.50")
                .font(.system(.caption, design: .monospaced, weight: .regular))
                .foregroundColor(.secondary)
        }
        .padding()
    }
}

Extracting Subviews & List

Now it’s great that we have this list row, but if we have to define everything in one body property that will get messy really fast. Luckily, there’s a method for dealing with this.

You can place any view inside another view, meaning not only the ContentView struct can have a body property. Any struct which adopts the View protocol can have a body.

Let’s create a new SwiftUI View file (cmd n) called ListRowView. Let’s replace the contents of the body in this view, with the current contents of our ContentView’s body. (Your ContentView body will be empty and throwing errors again.

Now, add a List() {} to your ContentView.

What’s List

List let’s us define how to deal with a list of views. We do this by iterating over some set of data and describing what view to output for each datum.

Let’s start by iterating over a range and displaying our row for each iteration:

List(0..<20) { _ in
	ListRowView()
}

We should see our row has been replicated 10 times. We can scroll up and down this list too.

Now what if we want to iterate over different data for each entry? We’ll have to start by modifying our list row.

Abstracting our ListRowView()

Right now our ListRowView only display the single best item at Wollastons. But what if we want to give people more options? We’ll need to abstract the view’s properties.

Let’s define some constants to use in our view instead. Add this above the body property in ListRowView:

let imageName: String
let title: String
let price: Double

Now, let’s use these properties in our view, replacing the hard-coded content with the property names. For Strings, we’ll need to interpolate using “\(propertyName)”. You can also specify a rounding format using String(format: "$%.02f", propertyName) to round to 2 decimal places with a preceding $ as an example.

HStack {
  Image(systemName: imageName)
    .foregroundColor(.pink)
  
  Text(title)
    .font(.headline)
  
  Spacer()
  
  Text(String(format: "$%.02f", price))
	  .font(.system(.caption, design: .monospaced, weight: .regular))
	  .foregroundColor(.secondary)
}
.padding()
Updated body property.

Your Preview and the ContentView will throw errors. Respond to them accordingly, adding the required properties to their initializers.

// Updated Preview
struct ListRowView_Previews: PreviewProvider {
    static var previews: some View {
        ListRowView(imageName: "tag.fill", title: "French Toast Muffin", price: 2.5)
    }
}

// Updated ContentView
struct ContentView: View {
    var body: some View {
        List(0..<20) { _ in
            ListRowView(imageName: <#String#>, title: <#String#>, price: <#Double#>)
        }
    }
}
You’ll note we didn’t actually solve the ContentView yet. That’s because we’ll need to iterate over data.

Iterating over data

Example Data

We’ll need some data to iterate over first. I’ve defined a struct to represent one WollyItem and then some examples in a static struct.

struct WollyItem: Identifiable {
  var id = UUID()
  var title: String
  var imageName: String = "tag.fill"
  var price: Double
}

struct Examples {
  static let wollyItems = [
    WollyItem(title: "French Toast Muffin", price: 2.5),
    WollyItem(title: "Iced Coffee", price: 4.49),
    WollyItem(title: "Bagel", price: 1.65),
    WollyItem(title: "Cookie", price: 1.99),
    WollyItem(title: "Brownie", price: 1.8)
  ]
}

Note the WollyItem has adopts the Identifiable protocol, which is required for items that List will iterate over (which lets list keep track of which item is which).

I’ve also defined a default value for id and imageName because they won’t be changing for now. This makes it easier to define examples.

Iterating

In List, let’s replace our range with some data. Turn List(0..<20) { _ in into List(Examples.wollyItems) { item in. We’ve replaced the data with some specific data of a new type instead of a numbered range, and we’ve replaced the _ with a named item.

In Swift, _ means we want to ignore the given item, but we acknowledge it exits. Naming that value item means we can interact with it now.

Let’s populate the parameters of our ListRowView too: ListRowView(imageName: item.imageName, title: item.title, price: item.price).

List(Examples.wollyItems) { item in
  ListRowView(imageName: item.imageName, title: item.title, price: item.price)
}
Our List should look like this.

If you reset the preview you should see a list with our defined example data.

Navigation

Let’s add some simple Navigation. This section will be in broader strokes and less of a follow along.

To add Navigation, Views need to be contained in some sort of Navigation layout. NavigationStack is a great place to start. We’ll wrap our list in one. At first this will appear to not have done anything.

We want to display some details for each item in our catalogue. To do this, we’ll wrap the ListRowView in a NavigationLink to link to the detail view. This will add arrows to the right side of each list item, and tapping on the items will bring us to the detail views (which are just a Text view for now).

	NavigationStack {
	List(Examples.wollyItems) { item in
    NavigationLink {
      Text("Detail view")
    } label: {
      ListRowView(imageName: item.imageName, title: item.title, price: item.price)
    }
	}
}
New version of the body property; now with Navigation.

SwiftUI makes it really easy to add a title too! Let’s add a .navigationTitle("Wolly's Mobile") inside our NavigationStack.

We can modify the accentColor on the NavigationStack too to change the color of the Back buttons, and we’ve got ourselves a handy little app with very little work.

Finished Version Sample Code

MyFirstApp.zip33.8KB

This is the version built using the above tutorial.