Projects People Resources Semesters Blog About
Essential Techniques
🧶

Essential Techniques

Last Updated: 22 September, 2022

This presentation provides explanations for basic building blocks and how to apply this to creating a simple Shopping List app.

Click above to view the slides. Presentation given by Frank Anderson on 25 September, 2022 during Fall 2022 Hack Session 2.

MyFirstApp.zip3086.7KB

Download the sample code here. Please play with and manipulate it to see how different parts of the app work.

The Goal

These are the screens we want to make using SwiftUI
These are the screens we want to make using SwiftUI

Data in Swift

To build an interface, we need some data to show first. Let’s define some examples.

We’ll start with defining one item at Wollaston’s, a WollyItem. SwiftUI likes to use Structs, so let’s define a struct SwiftUI can identify with some properties to denote what type of WollyItem we have.

We’re representing a real item, so let’s think about the attributes we need to include for our code as well such that we accurately represent the real world.

// Represents an item Frank would get at Wollaston's
struct WollyItem: Identifiable { // marked Identifiable, requirement for List in SwiftUI
	var id = UUID() // UUID is an easy way to make unique identifiers that satisfy the `var id` requirement
	var title: String // Use : to require definition when instantiated
	var imageName: String = "tag.fill" // use = to define a default
	var price: Double // Use : to require definition when instantiated
}
Note Swift is strongly typed, just like Java, but (not shown here) uses optionals if an object can be nil.

Now that we have the ability to define an item, let’s define an example list of these items.

// Represents Examples of WollyItems
struct Examples {

	// static instances of each item 
  // Great if we want to preivew a single item (DetailView, ListRowView, etc.)
  static let muffin = WollyItem(
      title: "French Toast Muffin", price: 2.5)
  static let coffee = WollyItem(
      title: "Iced Coffee", price: 4.49)
  static let bagel = WollyItem(
      title: "Bagel", price: 1.65)
  static let cookie = WollyItem(
      title: "Cookie", price: 1.99)
  static let brownie = WollyItem(
      title: "Brownie", price: 1.8)
  
  // static list of many items
	// great for sampling a whole group (cart, list, etc.)
  static let wollyItems = [muffin, coffee,
                           bagel, cookie, 
													 brownie]
}
We are using this as our model to start. In the future, we could create a model that calls to an API to get this list of items.

Thinking about Layout

We’ve got some data, so now let’s build an interface. Rule #1 of SwiftUI is that everything is a View. You can combine and manipulate Views to build up an interface. Rule #2 is that each View structure can only return one view.

Because of the limitation of Rule #2, we use Stacks, Groups, and other Layouts to combine views.

⌨️
Examples of each Stack
VStack
image
image

🔑
Image above the Text
HStack
image
image
🔑
Image next to Text
ZStack
image
image
🔑
Images stacked on top of each other.

Using Stacks, you can build up different Views to build interfaces. By default, Stacks center views inside of them, and views take up only the space they require to render. You can just adjust how much space views take up using .frame() and .padding(). You can adjust the amount of space between views in a stack using SomeStack(spacing:) and you can adjust the alignment of views in stacks using SomeStack(alignment:).

The different colored rectangles show how different sections of code correspond to different outputs.
The different colored rectangles show how different sections of code correspond to different outputs.

Styling with Modifiers

You can style individual views and groups of views using Modifiers. Modifiers build up in order from top to bottom, so if you want some buffer space between a view and its background modifier, you’d apply padding and then a background. If you want more space between the background and the next element, you’d apply another padding. It’s hard to explain with words, so play around with it a bit on your own and see how switching the order changes the view. (Keyboard Shortcut tip: cmd + option + [ or ] lets you move code up and down lines.)

Fun thing: SFSymbols are a set of built-in icons you can use. Use the Image(systemName:) initializer to access them, or use Cmd + Shift + L and click the Star to see a library of them.
Fun thing: SFSymbols are a set of built-in icons you can use. Use the Image(systemName:) initializer to access them, or use Cmd + Shift + L and click the Star to see a library of them.

Abstraction (to remove Duplication)

Using reusable components to simplify our work and reduce duplication let’s us be more efficient programmers. It also maintains cohesion across you app since you don’t have to define every modifier and detail manually every time.

To create reusable components, you extract complex views into their own subviews.

Here’s an example of subviews at work:
image

To extract subviews, you can follow three quick steps:

  1. Create a new SwiftUI file which will have a struct of type View and a body which returns some View.
  2. Cut the parts from the main view and paste them into the body of the subview.
  3. Wait for Xcode to produce errors, pointing to missing variables that were in the main view which are not accessible in the subview. Create the necessary receivers (normally @Binding variables, @ObservedObject variables, or let constants) and pass in the values from the main view.
Example of extracted subview:
image
image

Something else you can do to remove duplication is to simplify modifiers into custom ViewModifiers/Extensions. There are some examples of this in the demo project which you can download. Look to DemoGuide.md to see where each of the examples are linked.

Data + Design = …

Now it’s time to connect our views and data together to build a responsive application. A key thing to remember about declarative UI (like SwiftUI or React) is that the views are a product of their states. States are the values of the instance variables in a given View’s structure. States that the views can respond to (not just display) are marked using property wrappers in SwiftUI. Examples of these are @State, @Binding, @ObservedObject, @Environment, @StateObject, etc.

When passing data around, it’s important to remember there should be one source of truth, and the other views should merely refer to the source, not recreate it. The two most common property wrappers to definen sources of truth are @State and @StateObject.

To refer to sources of truth, use @Binding or @ObservedObject when you want to make changes. If you just want to display immutable data, use let.

Here’s an example from our code where we define sources of truth:

The
The List displays values in items; A sheet is/is not presented based on the value of showCart

And here’s how we pass those sources of truth to sub-views that can modify and view the state:

•
@Binding lets us change the state of the cart and have those changes reflected across the app.

Using these principles, you can pass data around your app. If you want more global data, do some research into .environmentObject() and @StateObject.

Next Steps

  1. Look at the code for the sample project, linked at the top of this block.
  2. Check out the Next Steps block at the bottom of this page for a list of resources.