Building a Simple Time Tracker With SwiftUI — Part 1
In this series of articles I will be walking you through my process of building a time tracker app for iOS and macOS using SwiftUI
As I started to write more apps as an independent developer, I quickly realised this independence needed to be balanced around my full time job. I needed a way to track how much time I was spending working on different projects while still accomplishing a full 40 hour work week. After all, independent development isn’t paying my bills yet. This led me to search for simple time tracker, yet after googling I couldn’t seem to find one that met my needs. So what does an indie dev do at a time like this? Build one.
This article is PART ONE of it’s series. I will be posting a new article every week, at the same time as I send out my newsletter. Follow my newsletter to stay up to date. You can also follow along with the code and/or pull the source code from the GitHub repo. Code made in this article can be found on branch post/initial-setup.
These are my minimum viable product (MVP) goals:
- Simple UI
- Ability to track multiple projects
- Menu Bar functionality in macOS with the same main UI as with the iPad
- Stand alone iPhone app
- Cross platform with single SwiftUI codebase
There are also have two additional features I want to explore implementing:
- Printing of PDF reports
- Subscriptions for syncing the data between devices
The First Iteration
For me to be able to test the basic functionality of the app, there needs to be a simple view to see the projects and then a button for each project to start tracking time for that specific project. Since we are working on a cross platform app, supporting both iOS and macOS, we can test our code on either the macOS or on the simulator. When we are finishing up functionality and getting the UI dialled in, we should of course test all the platforms.
To get started: Open Xcode > Create new Xcode project > Choose
app > Choose whatever name you want and choose where to save it.
Note: Since the app being built is a time tracker app, some of the terms might be a bit confusing so:
App = the time tracker app being built
Project = a single object that time is being tracked for
ContentView() is the default place where the app UI is built, let’s start there. For the basic view we need a
Text() for the name of the project and for the time to be shown. Then we need a
Button to toggle the timer on and off. That would look something like this:
Since there can be multiple of these projects, I’m going to make a
ProjectView which we can reuse for each project. I’ll also make a struct named
Project to make sure that all projects share the same structure and have the same data points.
Project has two values: name which is a string, and time which is an integer. Fun fact, if you are using Date() in your app: it’s basically an integer of seconds passed since January 1st, 2001.
ProjectView takes in a
Project as an argument and has one
Text("\(project.time)") and a button. If you are wondering why the two text views have values written in different way, there is a simple explanation for it. A Text-view takes in a String. Our
project.name is a string so SwiftUI can use it for text views without any extra work. But
project.time is an integer, so we need to use string interpolation to cast it as a string. This is how our
ProjectView.swift looks like:
I thought about making the projects time an optional value, but decided that it would make more sense to initialize it with a 0 when creating a project. I’ll also move the
Project struct into it’s own file, just to make our files clean and clear. Our
ProjectView should be for declaring the View, not the struct of a project.
That’s it for the basics of UI, now we will create some functionality.
@State variables are needed: a boolean called
timerStarted and an integer for seconds. For counting seconds we can use Apple’s Timer and have those seconds added to our projects time. SwiftUI gives us an extremely simple way to do just this, it’s called .onReceive(). This adds an action whenever data changes on a given publisher, in our case the timer. That way we can increment the seconds variable and because it has the
@State property wrapper, our Views (that depend on the value) will update automatically.
The button will toggle the
timerStarted boolean. If the boolean is true, then we’ll keep adding seconds to the seconds variable and show those seconds. This is how our code in
ProjectView.swift looks like now:
Now we are expecting this view to get a
Project in, because we are not giving the variable project a initial value. That means that where ever we create these views in will need to provide that value. In this case it is
ContentView.swift . For us to be able to test that our functionality works nicely, let’s make the ContentView use our new ProjectView:
That is the first iteration of the app.
The app now has the very basic timer functionality to track tasks. In the next part, we’ll go over a way to add your own projects to track and save the data in UserDefaults. We’ll also format the time to show hours, minutes and seconds. Keep an eye out, the next article will drop in one week.
Want to stay up to date with this series and future articles? Sign up for my weekly newsletter where you’ll receive links and resources to all things Swift and iOS.
The Swift Watch — newsletter
Your weekly resource for Swift and iOS development news, articles, prodcasts and videos.