How To Use UIKit With MVVM and Combine - by G. Abhisek - Better Programming
How To Use UIKit With MVVM and Combine - by G. Abhisek - Better Programming
Open in app
Search
Member-only story
G. Abhisek · Follow
Published in Better Programming
5 min read · Jun 14, 2020
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
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
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!
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
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.
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:
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 }
HomeViewModel
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
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>
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.
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.
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
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
OOPs in SWIFT
OOP(Object Oriented Programming), is the three-lettered magical concept on which almost all
modern programming language stands. SWIFT…
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
12.1K 230
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.
965 21
G. Abhisek
25 2
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
Manish Pathak
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
333 4
Lists
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
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
133 1
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™
108
https://betterprogramming.pub/uikit-mvvm-combine-912c80c02262 20/20