(This blog post's cover photo has been designed using resources from Freepik.com)
By bringing together SwiftUI and Combine, we can create a simple watch timer app that will add a little amount of pressure to any user. Its implementation is simple, so let's get started.
This is what our app will look like at the end of this short tutorial:
Step 1: Setting Up a New watchOS Project
To create a new watchOS project:
- In Xcode, choose File > New > Project.
- Go to the watchOS tab.
- Choose Watch App as the template of your new project and click Next.
In the project options, enter the name for your project (I named mine "SimpleTimerApp" π). Ensure that the Language is set to Swift.
Once you click Next, you will be asked to select a location to save your project. Click Create.
Step 2: Declaring our State variables
For this, we can use the default ContentView.swift file. Let's first create our two state variables:
- 'count' (represents our time in seconds)
- 'timer' (represents our countdown timer)
@State private var count = 0
@State private var timer: Timer?
When we use @State (a property wrapper), weβre asking SwiftUI to watch a property for changes. So if we add 1 to our count variable, for example, our State will see this and re-render the view. If there is a change in the state of this property, we will see the number on our screen update.
To make a countdown timer or similar, we will use Timer. For our needs, we'll use an optional variable to set the timer to nil at the start. Our timer will be set at a later time.
Step 3: Defining our SwiftUI Stacks
Using stacks in SwiftUI allows us to arrange our UI elements. We will need two type of stacks in this project:
- VStack - arranges UI elements in a vertical line, i.e above or below each other.
- HStack - arranges elements in a horizontal line, i.e. beside each other.
var body: some View {
VStack(alignment: .center, spacing: 8){
Text("Hello, World!")
.padding()
HStack(alignment: .center, spacing: 8){
//Increment button
//'Go' button
}
}
}
We will further customize our stacks with alignment and spacing in order to modify their appearance. For example, our VStack elements will be aligned horizontally in the middle and 8 points apart. Our _HStack _elements will be aligned vertically in the middle and 8 points apart as well.
Step 4: Preparing our UI elements π¨
Replace the string "Hello Word" with "0" as a placeholder text for our count. We will also increase the font size and font weight of this string.
Text("\(count)")
.font(.system(size:90))
.fontWeight(.bold)
Now, let's set up our two buttons in our HStack:
Increment button: Adds 1 to the count variable every time the button is pressed. As a styling preference, we are rendering the plus icon from SF Symbols using Image.
'Go' button: This is where we will start our countdown timer. For styling, we will make the text a green colour.
//Increment button
Button {
count = count + 1
} label: {
Image(systemName: "plus")
.font(.system(size:34))
}
//'Go' button
Button {
//set our countdown timer
} label: {
Text("Go!")
.font(.system(size:34))
.foregroundColor(.green)
}
Step 5: Starting our Countdown Timer
Once a user clicks on the 'Go!' button, we want our timer to start counting (in seconds) all the way down to 0. However, we want to ensure that our count is neither equal to nor less than 0. We will only start our timer if our count value is greater than 0.
Button {
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) {time in
if count > 0 {
count -= 1
}else {
timer?.invalidate()
}
}
}label: {
Text("Go!")
.font(.system(size:34))
.foregroundColor(.green)
}
You can destroy an existing timer by calling its **invalidate() **method. In this case, the timer is destroyed until it is restarted. If the count value was increased a second time, the timer will not begin until the user presses the 'Go' button.
Overview
And that's it! Below, you will see the finished code structure of this simple app. Thank you for reading!
import SwiftUI
import Combine
struct ContentView: View {
@State private var count = 0
@State private var timer: Timer?
var body: some View {
VStack(alignment: .center, spacing: 8){
Text("\(count)")
.font(.system(size:90))
.fontWeight(.bold)
HStack(alignment: .center, spacing: 8){
//Increment button
Button {
count = count + 1 //add 1 to the count variable each time the button is pressed
} label: {
Image(systemName: "plus") //rendering an icon from SF Symbols
.font(.system(size:34))
}
Button {
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) {time in
if count > 0 {
count -= 1
}else {
timer?.invalidate()
}
}
}label: {
Text("Go!")
.font(.system(size:34))
.foregroundColor(.green)
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Top comments (1)
Wow - what a breath of fresh air to find a clean understandable example to follow - and thoughtfully explained!
Thank you!