Image for post
Image for post

I’m Only Happy When It’s MVVM

SwiftUI + MVVM + Combine + ReactiveSwift

Preface

After an initial adjustment, and a hurried refurnishing of my apartment, I found my creative mind swimming with motivation for the first time in months. Previously, after commuting to, working at, then commuting home from the office, I was little inclined to do yet more programming during my downtime.

These past few weeks, I found myself more motivated than ever to learn something new. I decided to further my understanding of the oft-used MVVM app architecture, and learn how to apply it to future apps I make or contribute to using Combine and SwiftUI.

Inputs & Outputs

… but that’s not all. I knew that this approach ought to work with Combine as well, since it is a first-party solution to ReactiveSwift provided by Apple.

In essence an Input is an event which occurs on a View, such as viewDidLoad or didTapLoginButton, and an Output is the eventual result of an Input.

@State, @ObservedObject, @EnvironmentObject?

With SwiftUI, our views are struct types, and thus cannot be mutated. We need to hand over control to the State, so that when it updates the view will show the latest data. Without any of this, your app would be non-functional.

If your “state” comprises of a few simple types such as a String, a Bool, and an Int, then you can go ahead and annotate your properties as@State types. As this implies that the state only refers to and controls the current screen, it’s important to mark these properties private. Scope matters!

If your “state” is more complex, such as a ViewModel, and/or may apply to multiple views, you’ll be better off using @ObservedObject. This is the same as @State in many ways, but when you create properties in your state, you get to decide whether changes to each property force a refresh or not.

Lastly, @EnvironmentObject is very similar to @ObservedObject however these objects are available to all views of your app at any time. If you share a lot of data around screens of your application, these are incredibly useful. The closest comparison to @EnvironmentObject that I can give you, is the AppState employed in apps built with a Redux architecture, or even simpler — a global singleton.

Combining Approaches

The first step was to port over my ViewModel protocol, and then find an equivalent for my .pipe() objects from ReactiveSwift, which conveniently use the I/O principle already.

The closest match ended up being PassthroughSubject. According to the documentation, these doesn’t have an initial value or a buffer of the most recently-published element, very similar to .pipe() from ReactiveSwift.

More documentation goodies led me to @Published properties, which do what they say on the tin. They “publish” values to objects which observe them. Thus, my ViewModel becomes as follows:

…and my ContentView just needs the following:

Running the app will show Hello, World! right in the middle of the screen as you would expect.

Image for post
Image for post

Congratulations! You have now implemented MVVM using Combine and SwiftUI!

That’s All For Now

As I learn more, I will publish more.

Stay tuned! In the meantime, please let me know what you think. 🤔

Update: The Next Day

Thanks Tony! 🍻

東京在住iOS Guy | 日英可

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store