How to make an MVVM Swift UI App

Liyicky
6 min readAug 22, 2022

--

Step by step guide. I’m going to teach you how to make a simple habit tracker. Let’s start!

Create a new project

The most important thing when creating a new app is a clever app name. Spend hours coming up with one if necessary. After 6 hours I came up with “Habit and the Blowfish”. Perfect.

Organize your files. We need 3 new groups. Models, Views, and View Models.

Plan out the app extensively

Cut 2 mesures trice

Here’s the battle plan I wrote out carefully. You can see we have our view on the left, and our model on the right. Now that we preemptively thought about the code we are going to right, nothing will go wrong and we can get started right away.

Make the model

Habit.swift

We are going to let the users select an image from the phone to represent the habit they are tracking. So we want the model to store that for us in the from of a UIImage.

Make the View

Make a view file called HabitsView.swift. The app is a scrollable list of habits with a stepper button on each row. I decided to add ponies to mine because ponies fill me with hope.

Here are the horseys.

Make the View Model

We are going to use a sheet to make the habits. Let’s make a view model to keep track of that. Then add it as an environment object in the struct that make the app.

Add it like the images above.

Make the HabitCreationView

Make a new view file called HabitCreationView.swift. It’s gonna be pretty simple. Just a form where we pick a name and an image to represent the habit. I chose a blowfish cause blowfishes help ease the constant waves of sweat inducing panic caused by schizophrenic thoughts that my life my be just a simulation.

Originally the plan was to let the user choose an image on their phone, but we want to keep this app simple and let everything be saved via User Defaults. Let’s cut out that feature and simply show some icons to choose from.

Here is the list of all SF Symbol names. Make it into an array. Make that array a static variable of the Habit struct.

Then let’s make a lazy grid of all the symbols as images. Add a onTapGesture so when the user clicks on an icon, it will show up on the form. Throw in a “create” button.

Our updated HabitCreationView
Looking good!

Create habits and save them via User Defaults

This is the last really difficult thing. Create the habits, persist them, then show them in the Habits View. Close blacked.com and get back to work.

Our create button isn’t doing anything yet. We are going to have to do a little work before we can create views.

What we want to do is have this button save a Habit object in the user defaults. Then have another method in the Habit View Model that pulls all of the habits out so we can show them.

Setup the View Model

We are going to create a list of habits. When ever we add a habit to this list, it should automatically try to save it to the user defaults.

P.S. var habits = [Habit]() is the same as var habits: [Habit] = []

Make our Habit struct Codable. Add this @Published list to our View Model. This has a didSet that will try to save to user defaults whenever something is added to it.

Now we can add this code to our create button.

If you run the app you will see that this is working. But we can’t see any habits. Let’s do that now.

Make a new file called HabitRowView.

How long do you think I spend coming up with those habit names?

Now are rows are showing up in the Habit View. But if you re-run the app, all of the habits disappear. Oh no!

Persist the habits

Add this code to the Habit View Model.

Now when the View Model is created, our habits will be loaded in from UserDefaults. Pretty cool.

Increment the completion count

We want the app to have a stepper for counting how many times you completed the habit. This is probably the laziest way to make a habit tracker but it works.

Run the app and we have a nice stepper. Re-run that app and we lose everything. How can we save the data?

The entire app is saving one JSON. We make a list of Habits and turn that into a JSON, then save that in the User Defaults. We cannot simply change the count value of one of the habits because: #1 Structs are immutable and #2 even if we did change it, Swift UI wouldn’t know and wouldn’t redraw the views to show it.

Here’s where I would say, try to figure this part out on your own. It’s a little tricky, but once you solve it you will be a better programmer.

How to solve it

We can’t update a struct. So we are gonna delete the struct and replace it with one identical to it with the exception that its count will be changed.

Add this function to the Habit View Model.

Here we are going into the list of habits. Finding one that matches, cloning it, then resaving it in the array.

One problem. We can’t use “==” on Habits. Let’s update our Habit struct to be Equatable.

Add the @EnvironmentObject vm to the Habit Row View and update the stepper.

I wrote this code in one try and it worked so it must be perfect.

Now our app is completed. It’s a barely functional habit tracker. Spend about 1 or 2 or 300 more hours on it and it could go on the app store.

Can perfection be a habit?

Thank you

If you read this far, thank you. I’m broke and jobless and documenting going from 0 to programmer. Become a follower if you’re interested in that. ❤

--

--

Liyicky

Hey, I’m Jason Cheladyn. Going back to the coding world after 6 years of teaching English in Japan. https://www.twitter.com/liyicky https://www.liyicky.com