0% found this document useful (0 votes)
11 views

How To Use UIKit With MVVM and Combine - by G. Abhisek - Better Programming

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views

How To Use UIKit With MVVM and Combine - by G. Abhisek - Better Programming

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 20

22/11/2023 21:12 How to Use UIKit With MVVM and Combine | by G.

Abhisek | Better Programming

Open in app

Search

Member-only story

How to Use UIKit With MVVM and Combine


Integrate Apple’s Combine into your view models

G. Abhisek · Follow
Published in Better Programming
5 min read · Jun 14, 2020

Listen Share More

It’s been a while since Apple’s own reactive framework Combine came onto the
scene. Being a RxSwift user for quite some time, I was more interested in Combine
than Swift U, so I decided to learn it first.

https://betterprogramming.pub/uikit-mvvm-combine-912c80c02262 1/20
22/11/2023 21:12 How to Use UIKit With MVVM and Combine | by G. Abhisek | Better Programming

I started migrating one of my UIKit + Swift + MVVM projects to Combine and MVVM
to learn. This project creates an app named Nearby that displays some places
around you (restaurants, ATMs, Cafes, etc). In this article, I’ll be sharing my
experience of doing the migration, pain points, as well as sharing relevant resources
with you.

For the scope of our discussion, let’s take the example of a few pages of Nearby. You
can download the Nearby codebase and start referring to the blog.

Model-View-ViewModel

Configuring the ViewModel

https://betterprogramming.pub/uikit-mvvm-combine-912c80c02262 2/20
22/11/2023 21:12 How to Use UIKit With MVVM and Combine | by G. Abhisek | Better Programming

Place Detail

https://betterprogramming.pub/uikit-mvvm-combine-912c80c02262 3/20
22/11/2023 21:12 How to Use UIKit With MVVM and Combine | by G. Abhisek | Better Programming

The view shows the name, location, image, open status, and distance of a place from
your location. These details will be supplied from the ViewModel for this view. Let us
jump to it:

1 class PlaceDetailViewModel {
2 // MARK: Output
3 @Published private(set) var title = ""
4 @Published private(set) var distance = ""
5 @Published private(set) var isOpen = false
6 @Published private(set) var placeImageUrl: String = ""
7 @Published private(set) var location: CLLocation? = nil
8
9 private let place: NearbyPlace
10
11 init(place: NearbyPlace) {
12 self.place = place
13 configureOutput()
14 }
15
16 private func configureOutput() {
17 title = place.name
18 let openStat = place.openStatus ?? false
19 isOpen = openStat
20 location = place.location
21 placeImageUrl = place.imageURL ?? ""
22
23 let currentLocation = CLLocation(latitude: LocationManager.sharedManager.latitude, long
24 guard let distance = place.location?.distance(from: currentLocation) else { return }
25 self.distance = String(format: "%.2f mi", distance/1609.344)
26 }
27 }

PlaceDetailViewModel Combine.swift hosted with ❤ by GitHub view raw

PlaceDetailViewModel

What’s going on here?


Your ViewModel receives a NearbyPlace object which holds all the relevant
information for this particular place. The ViewModel then configures the outputs.
Outputs are the data that is to be displayed on the view. So far everything is going
great.

You may have noticed that all output properties are annotated with the @Published

keyword. This property wrapper in Combine contains a stored value and its

https://betterprogramming.pub/uikit-mvvm-combine-912c80c02262 4/20
22/11/2023 21:12 How to Use UIKit With MVVM and Combine | by G. Abhisek | Better Programming

projected value provides users with a Combine publisher, receiving updated values
for the property whenever it’s changed. In Swift world, you have to call an update
callback or delegate implemented in the view, but in Combine you get it for free!

Configuring the View

https://betterprogramming.pub/uikit-mvvm-combine-912c80c02262 5/20
22/11/2023 21:12 How to Use UIKit With MVVM and Combine | by G. Abhisek | Better Programming

1 class PlaceDetailController: UIViewController {


2 private var subscriptions = Set<AnyCancellable>()
3
4 private var viewModel: PlaceDetailViewModel!
5
6 override func viewDidLoad() {
7 super.viewDidLoad()
8 setupBindings()
9 }
10
11 private func setupBindings() {
12 // Properties that can be assigned using default assign method
13 subscriptions = [
14 viewModel.$title.assign(to: \.text!, on: titleLabel),
15 viewModel.$distance.assign(to: \.text!, on: distanceLabel),
16 viewModel.$isOpen.map { $0.openStatusText }.assign(to: \.text!, on: openStatusLabel
17 viewModel.$isOpen.map { $0 ? UIColor.green : UIColor.red }.assign(to: \.textColor!,
18 ]
19
20 // Properties require custom assigning
21 viewModel.$placeImageUrl.compactMap { URL(string: $0) }
22 .sink { [weak self] imageURL in
23 self?.placeImageView.kf.setImage(with: imageURL, placeholder: UIImage(named : "plac
24 })
25 }
26 .store(in: &subscriptions)
27
28 viewModel.$location.compactMap { location -> (MKCoordinateRegion, MKPointAnnotation)? i
29 guard let lat = location?.coordinate.latitude,
30 let long = location?.coordinate.longitude else { return nil }
31 let center = CLLocationCoordinate2D(latitude: lat, longitude: long)
32 let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelt
33
34 let annotation = MKPointAnnotation()
35 annotation.coordinate = center
36 return (region, annotation)
37 }.sink { [weak self] location in
38 self?.mapView.setRegion(location.0, animated: true)
39 self?.mapView.addAnnotation(location.1)
40 }.store(in: &subscriptions)
41 }
42
43 }

PlaceDetailController Combine swift hosted with ❤ by GitHub view raw


PlaceDetailController

https://betterprogramming.pub/uikit-mvvm-combine-912c80c02262 6/20
22/11/2023 21:12 How to Use UIKit With MVVM and Combine | by G. Abhisek | Better Programming

Once the view has been loaded, we set up the bindings of our ViewModel configured
output properties to our UI components.

Why do we call this binding? Because here you’re not only assigning your UI
components to their respective values, you’re also subscribing to any future changes
in that property:

assign(to:on:)

For clarity, we’ve segregated our binding as the ones which can be assigned directly
using the assign(to:on:) API of combine, which assigns a publisher’s output to a
property of an object. If you rewind to our last section’s discussion, we’ve annotated
our properties in ViewModel with @Published . In the binding we’ve used the projected
value of a property, using $propertyname to assign the underlying publisher to the
assignable properties of our UI component. The result of every assignment is an
AnyCancellable type.

For example, for assigning $title to the text property of titleLabel , we write
viewModel.$title.assign(to: \.text!, on: titleLabel) .

All AnyCancellable results are stored in a subscriptions set, ensuring that your
subscriptions are still in memory to receive any upcoming events.

Custom Assigning
For custom handling of attributes, we can use different operators provided by
Combine over our Publishers , such as sink(receiveValue:) and handleEvents , to
receive the values and work directly on them. In the code snippet above, we used
compactMap to map the stream of CLLocation values from the $location publisher to
a tuple of MKCoordinateRegion and MKPointAnnotation followed by sink to render the
details on the map.

Passing UI Events to the ViewModel

https://betterprogramming.pub/uikit-mvvm-combine-912c80c02262 7/20
22/11/2023 21:12 How to Use UIKit With MVVM and Combine | by G. Abhisek | Better Programming

There are times when we need to pass certain UI events to the ViewModel for further
processes — perhaps an API call, a database query, or something else.

https://betterprogramming.pub/uikit-mvvm-combine-912c80c02262 8/20
22/11/2023 21:12 How to Use UIKit With MVVM and Combine | by G. Abhisek | Better Programming

Nearby home

The above screen shows the Nearby home page. Certain UI events occur on this
view — user taps to refresh the feed, taps on category, taps on any place, etc. These
events trigger certain actions in the ViewModel , like triggering the API or on the UI
itself, in navigation for instance. For our discussion,let’s take a communication
where the user taps to refresh data on screen:

1 class HomeViewController: UIViewController {


2 private var subscriptions = Set<AnyCancellable>()
3 private var loadDataSubject = PassthroughSubject<Void,Never>()
4 private var viewModel = HomeViewModel()
5
6 override func viewDidLoad() {
7 super.viewDidLoad()
8 prepareTableView()
9 setupBinding()
10 loadDataSubject.send
send()
11 }
12
13 private func setupBinding() {
14 viewModel.attachViewEventListener(loadData: loadDataSubject.eraseToAnyPublisher())
15 viewModel.reloadPlaceList
16 .sink(receiveCompletion: { completion in
17 // Handle the error
18 }) { [weak self] _ in
19 ActivityIndicator.sharedIndicator.hideActivityIndicator()
20 self?.tableView.reloadData()
21 }
22 .store(in: &subscriptions)
23 }
24
25 @IBAction func refreshButtonPressed(_ sender
send : Any) {
26 ActivityIndicator.sharedIndicator.displayActivityIndicator(onView: view)
27 loadDataSubject.send
send()
28 }
29 }

HomeView_load_data_Combine.swift hosted with ❤ by GitHub view raw

HomeViewController

https://betterprogramming.pub/uikit-mvvm-combine-912c80c02262 9/20
22/11/2023 21:12 How to Use UIKit With MVVM and Combine | by G. Abhisek | Better Programming

1 class HomeViewModel {
2
3 var reloadPlaceList: AnyPublisher<Result<Void, NearbyAPIError>, Never> {
4 reloadPlaceListSubject.eraseToAnyPublisher()
5 }
6
7 // MARK: Input
8 private var loadData: AnyPublisher<Void, Never> = PassthroughSubject<Void, Never>().eraseTo
9 private let reloadPlaceListSubject = PassthroughSubject<Result<Void, NearbyAPIError>, Never>(
10
11 func attachViewEventListener(loadData: AnyPublisher<Void, Never>) {
12 self.loadData = loadData
13 self.loadData
14 .setFailureType(to: NearbyAPIError.self)
15 .handleEvents(receiveOutput: { [weak self] _ in
16 self?.allPlaces.removeAll()
17 })
18 .flatMap { _ -> AnyPublisher<[NearbyPlace], NearbyAPIError> in
19 let placeWebservice = PlaceWebService()
20 return placeWebservice
21 .fetchAllPlaceList()
22 }
23 .receive(on: DispatchQueue.main)
24 .handleEvents(receiveOutput: { [weak self] _ in
25 self?.tableDataSource.removeAll()
26 })
27 .sink(receiveCompletion: { _ in },
28 receiveValue: { [weak self] places in
29 self?.allPlaces.append(contentsOf: places)
30 self?.prepareTableDataSource()
31 self?.reloadPlaceListSubject.send
send(.success(()))
32 })
33 .store(in: &subscriptions)
34 }
35 }

HomeView ViewModel Cobine swift hosted with ❤ by GitHub view raw

HomeViewModel

A PassthroughSubject can be used to send events to subscribers of your subject.


Think of this as your water supply pipe from which you take your daily water supply.

In our case the loadDataSubject is used by the view to send events to the ViewModel

to load app data from the server. Whenever the user presses on the refresh button or
the view loads for the first time we ask the ViewModel to load the data. If you closely

https://betterprogramming.pub/uikit-mvvm-combine-912c80c02262 10/20
22/11/2023 21:12 How to Use UIKit With MVVM and Combine | by G. Abhisek | Better Programming

look at the attachViewEventListener(loadData: AnyPublisher<Void, Never>)

implemented in the ViewModel , we do not actually pass the loadDataSubject to the


ViewModel to receive the event. Rather we type erase the subject to an AnyPublisher

and send it. An AnyPublisher can only be used to receive events and not send events.
By using this type erasure we are avoiding any chance of abuse of the
loadDataSubject from the ViewModel .

A similar approach goes for communication between ViewModel and View . For
reloading our list on a successful API fetch, we use reloadPlaceList:
AnyPublisher<Result<Void, NearbyAPIError>, Never>

Pain of UIKit and Combine Compatibility


Missing bindings
Though we have assign(to:on:) which uses the key path to bind a publisher to any
property, it lags some major binding functionality as its Rx counterpart bind(to:)

has. Especially for UIControl , there’s no straightforward way to send control events
to a AnyPublisher property. In our case, we’ve used PublishSubject and type
erasures to send a button tap event. The community was pretty quick to develop
wrappers for missing binding functionality for UIKit components.

To implement a UIControl binding functionality such as assign, we have to write our


own custom publisher which is a lot of overhead for anyone.

Don’t be disheartened. We can still use combine to drive many of our business logic
asynchronously and exploit the power of Combine and reactive programming.

Property Wrappers and Protocols


Property wrappers can not be declared in protocol definitions. You may have
noticed, we used our ViewModel s directly in the view, using their concrete types.
This reduces the scope of the reusability of our views. If we were to abstract out
ViewModel s and expose our outputs and inputs by protocols, we could not use

https://betterprogramming.pub/uikit-mvvm-combine-912c80c02262 11/20
22/11/2023 21:12 How to Use UIKit With MVVM and Combine | by G. Abhisek | Better Programming

@Published directly in protocol definitions. The reason and the workaround are
explained in this blog. Feel free to check that out.

Completed Project

GABHISEKBUNTY/CombineExperiments
Repo to hold all my combine experiments in Swift. Contribute to
GABHISEKBUNTY/CombineExperiments development by…
github.com

References
https://heckj.github.io/swiftui-notes/

https://forums.swift.org/t/a-uicontrol-event-publisher-example/26215/19

https://theswiftdev.com/the-ultimate-combine-framework-tutorial-in-swift/

https://www.caseyliss.com/2019/6/17/combine-wheres-the-beef

Combine Swift Swiftui IOS Programming

Follow

Written by G. Abhisek
https://betterprogramming.pub/uikit-mvvm-combine-912c80c02262 12/20
22/11/2023 21:12 How to Use UIKit With MVVM and Combine | by G. Abhisek | Better Programming

919 Followers · Writer for Better Programming

Software Developer(iOS), Speaker & Writer

More from G. Abhisek and Better Programming

G. Abhisek in Swift India

OOPs in SWIFT
OOP(Object Oriented Programming), is the three-lettered magical concept on which almost all
modern programming language stands. SWIFT…

7 min read · Jun 9, 2018

2K 8

https://betterprogramming.pub/uikit-mvvm-combine-912c80c02262 13/20
22/11/2023 21:12 How to Use UIKit With MVVM and Combine | by G. Abhisek | Better Programming

Benoit Ruiz in Better Programming

Advice From a Software Engineer With 8 Years of Experience


Practical tips for those who want to advance in their careers

22 min read · Mar 20

12.1K 230

Vinita in Better Programming

4 Strategies to Give Effective Feedback to a Difficult Person


https://betterprogramming.pub/uikit-mvvm-combine-912c80c02262 14/20
22/11/2023 21:12 How to Use UIKit With MVVM and Combine | by G. Abhisek | Better Programming

Respect is like air. As long as it’s present, nobody thinks about it. But if you take it away, it’s all
that people can think about.

· 8 min read · Nov 5

965 21

G. Abhisek

Mastering iOS Interviews: What Not to Ask Candidates


As someone who has been on both sides of the interview table in iOS development, I’ve come
to appreciate the nuances involved in the…

4 min read · Oct 23

25 2

See all from G. Abhisek

See all from Better Programming

https://betterprogramming.pub/uikit-mvvm-combine-912c80c02262 15/20
22/11/2023 21:12 How to Use UIKit With MVVM and Combine | by G. Abhisek | Better Programming

Recommended from Medium

Manish Pathak

Future and Promise in Swift Combine Framework


Why do we need Fututre in combine?

6 min read · Jun 26

17

https://betterprogramming.pub/uikit-mvvm-combine-912c80c02262 16/20
22/11/2023 21:12 How to Use UIKit With MVVM and Combine | by G. Abhisek | Better Programming

Ben Myers in Better Programming

Create a Scalable SwiftUI MVVM Project


Make a well-documented, organized, scalable SwiftUI project using the MVVM architecture
that will make your code reviewers say “wow”.

7 min read · Sep 15, 2022

333 4

Lists

General Coding Knowledge


20 stories · 595 saves

Coding & Development


11 stories · 279 saves

Stories to Help You Grow as a Software Developer


19 stories · 572 saves

ChatGPT
22 stories · 272 saves

https://betterprogramming.pub/uikit-mvvm-combine-912c80c02262 17/20
22/11/2023 21:12 How to Use UIKit With MVVM and Combine | by G. Abhisek | Better Programming

Vivek Sehrawat

Understanding Publisher and AnyPublisher in Swift


When working with Combine, Apple’s framework for handling asynchronous and event-driven
code in Swift, you’ll often come across the…

4 min read · Oct 5

14 1

Payal Kandlur

https://betterprogramming.pub/uikit-mvvm-combine-912c80c02262 18/20
22/11/2023 21:12 How to Use UIKit With MVVM and Combine | by G. Abhisek | Better Programming

Lenskart iOS Interview Experience (2023)


Hey all, here’s my interview experience for Lenksart. So there are usually 3 technical rounds,
the first being a screening round.

3 min read · Sep 10

133 1

Tuan Hoang (Eric) in Level Up Coding

Automating Memory Leak Detection with CI Integration for iOS


A solution to automate memory leak detection in iOS Development

6 min read · Oct 11

173

https://betterprogramming.pub/uikit-mvvm-combine-912c80c02262 19/20
22/11/2023 21:12 How to Use UIKit With MVVM and Combine | by G. Abhisek | Better Programming

InRhythm™

Modern Scalable iOS Architecture: Building For The Future


Our iOS Practice focuses on the art of crafting modern, scalable architectures, with an
emphasis on iOS app development.

6 min read · Oct 18

108

See more recommendations

https://betterprogramming.pub/uikit-mvvm-combine-912c80c02262 20/20

You might also like