How to Integrate Combine with SwiftUI And Make A Better Apps

Written by Kate38429475 | Published 2019/12/24
Tech Story Tags: ios | swift | ios-13 | ios-platform | software-development | combine | swiftui | latest-tech-stories

TLDR SwiftUI and Combine, Apple’s latest frameworks, were the highlights of the WWDC. The long-expected declarative UI, at last, became a reality, and this is truly an event of historic proportions in the world of iOS development. Combine is basically an implementation of the Functional Reactive Programming (FRP) paradigm. It allows you to concentrate on events that determine the business logic of the application, instead of spending time on dealing with a large number of details. All this allows reducing the amount of implementation details.via the TL;DR App

SwiftUI and Combine, Apple’s latest frameworks, were the highlights of this year’s WWDC. The long-expected declarative UI, at last, became a reality, and this is truly an event of historic proportions in the world of iOS development. 
Its declarative approach stands contrary to an imperative one, that iOS developers, including us, were using before iOS 13. No wonder they are excited! It removes a lot of complications from the UI-building process.
By presenting Combine Apple have made their first steps towards Functional Reactive Programming (FRP), that is expected to gain popularity in the upcoming years. Apple is making progress. For the end-users, it means that developers would sooner meet their requirements.
But possibly the most useful effect can be produced from making Combine work along with SwiftUI to improve the development process with the help of relevant tutorials and consequently make better apps.

SwiftUI

Apple defines SwiftUI as a new way to build UIs for Apple platforms using the advantages of Swift.
Another possible definition for SwiftUI is that it’s a framework for building UIs in a declarative programming style using Swift in the form of Domain-specific language (DSL).
How SwiftUI simplifies UI building
Today in the vast majority of cases UI building involves using an Interface Builder where you set UI components and their locations relative to each other. Then these components get attached to the code using Outlets, Actions, etc. Thus, the user interface and its behavior are essentially created in two separate places, which in itself is not very convenient.
An alternative to this is making UI in code. Though at the moment this is an even more difficult task. For example, here’s what button building looks like:
As you can see from a piece of code above, it takes to be aware of a lot of nuances:
  • You have to set corner radius for a button on its CALayer, instead of a button itself.
  • To set onclick event you need to create a selector, and in order to specify the hello() function there, you set the @objc attribute for a button. 
  • One more thing you have to be acquainted with is constraints creation.
Along with all the difficulties, you can see the results only after launching an application.
 To compare, this is how you create a button in SwiftUI:
The advantage of the declarative programming of SwiftUI is that you describe the desired result, as opposed to the imperative approach when you need to set the sequence of specific actions to obtain the wanted result.
How SwiftUI works
As mentioned above, SwiftUI uses DSL. What exactly is Domain-specific language? It’s a so-called domain language that allows describing primitives of a certain particular domain on the basis of a high-level language (in our case it’s Swift). Then, with the help of these primitives, tasks specific to the subject area are solved. In essence, a new language is built on the basis of the old one.
In the example above, such primitives are Button and Text.
In order for SwiftUI syntax to be as simple and straightforward as possible, some changes were introduced in Swift 5.1.
Here are some of those changes:
Function builders [SE-XXXX]
Thanks to this addition, the syntax like this one became possible:
HStack {

<span class="hljs-function" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none;"><span class="hljs-title" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none; color: rgb(28, 0, 207);">Text</span><span class="hljs-params" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none; color: rgb(92, 38, 153);">(<span class="hljs-string" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none; color: rgb(196, 26, 22);">"SwiftUI"</span>)</span></span>

<span class="hljs-function" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none;"><span class="hljs-title" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none; color: rgb(28, 0, 207);">Text</span><span class="hljs-params" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none; color: rgb(92, 38, 153);">(<span class="hljs-string" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none; color: rgb(196, 26, 22);">"rocks"</span>)</span></span>

}
Opaque return types [SE-0244]
It allows you to return a protocol with associated type from a function and eliminates the need to explicitly specify generic parameters of the return type:
<span class="hljs-keyword" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none; color: rgb(170, 13, 145);">var</span> body: some <span class="hljs-type" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none; color: rgb(92, 38, 153);">View</span> <span class="hljs-meta" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none; color: rgb(28, 0, 207);">{...}</span>
Property wrappers [SE-0258]
SwiftUI uses this to bind the object’s properties to View.

Combine

Apple describes Combine rather simply as a framework to customize the handling of asynchronous events by combining event-processing operators.
What is behind this description
When you begin to dig into Combine’s documentation and deal with such technical terms as Publisher, Subscriber, Operators, Cancellable, Scheduler, it immediately becomes clear that the Combine framework is basically an implementation of Apple’s functional reactive programming (FRP) paradigm.
iOS programmers familiar with such libraries as RxSwift and ReactiveCocoa, immediately get why Combine was created in the first place and what tasks it was supposed to solve. Besides, with Combine, there’s no more need to use any third-party solutions.
What tasks Combine solves
FRP paradigm in general, and Combine in particular increases the level of code abstraction. It allows you to concentrate on events that determine the business logic of the application, instead of spending time on dealing with a large number of implementation details. All this allows reducing the amount of boilerplate code (DispatchQueues, Delegates, KVOs, Target-Actions and etc.).
Benefits of using Combine framework:
  • Simple asynchronous code.
  • Multithreading is simplified.
  • Composable components of business logic that can be easily combined into chains.
Let’s say it is necessary for our application to execute 3 asynchronous requests at the same time, and get the execution result.
We could use DispatchGroup and DispatchQueue to solve this complex task. It will require some boilerplate code to create a queue, group, add tasks to queue and subscribe to the callback. Things get a little more complicated if asynchronous tasks should return some kind of result. In this case, you’ll need to create instance variables and write and read values from there.
This is how the solution looks like in Combine:
Publishers.Zip3(intAsyncTask, stringAsyncTask, voidAsyncTask)
<span class="hljs-selector-class" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none; color: rgb(155, 112, 63);">.sink</span> { (intValue, stringValue, _) <span class="hljs-keyword" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none; color: rgb(170, 13, 145);">in</span>
   <span class="hljs-comment" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none; color: rgb(0, 106, 0);">// tasks executions are finished</span>
}
The alternative is more concise. This is the declarative approach in action. You can focus on what you need to get, rather than burrowing into an abundance of implementation details.

SwiftUI + Combine

And now let’s see what happens if we use SwiftUI and Combine side by side. Consider a simple example of a username input form.
Let’s define the ResetPasswordModel that has only one field taking the user’s input.
<span class="hljs-class" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none;"><span class="hljs-keyword" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none; color: rgb(170, 13, 145);">class</span> <span class="hljs-title" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none; color: rgb(92, 38, 153);">ResetPasswordModel</span>: <span class="hljs-type" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none; color: rgb(92, 38, 153);">ObservableObject {</span></span>
  <span class="hljs-meta" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none; color: rgb(28, 0, 207);">@Published</span> <span class="hljs-keyword" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none; color: rgb(170, 13, 145);">var</span> email = <span class="hljs-string" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none; color: rgb(196, 26, 22);">""</span>
}
ResetPasswordModel conforms ObservableObject protocol, which means that the ResetPasswordModel’s fields can be used for SwiftUI’s bindings.The @Published modifier creates a publisher for the email field, so now it is possible to observe the email property. 
Second, let’s define the UI with SwiftUI.
struct ResetPasswordView: View { 

  @ObservedObject private <span class="hljs-selector-tag" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none; color: rgb(170, 13, 145);">var</span> model = ResetPasswordModel()

  <span class="hljs-selector-tag" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none; color: rgb(170, 13, 145);">var</span> <span class="hljs-selector-tag" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none; color: rgb(170, 13, 145);">body</span>: some View { 
    Form { 
      Section { TextField(<span class="hljs-string" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none; color: rgb(196, 26, 22);">"Email"</span>, text: <span class="hljs-variable" style="-webkit-tap-highlight-color: transparent; -webkit-font-smoothing: antialiased; box-sizing: border-box; outline: none; color: rgb(102, 102, 0);">$model</span>.email) }
    }
}
  1. The @ObservedObject is a property delegate that creates a connection between the View and the Model. View is notified when the data source is about to change and consequently re-render self.
  2. $model.email –  $ sign here is used to create a property wrapper that provides a two-way binding to data, so any changes to the value of email will update the TextField, and any changes to the TextField will update email.

Conclusion

It seems that Apple has done a great job by providing us with instruments that make the UI building process more efficient.
New SwiftUI’s declarative approach delivers a set of advantages including the increased speed of development, better integration between designers and coders, and the code quality enhancement.
Use the combining effect of SwiftUI coupled with Combine to produce a well-structured, efficient and maintainable code.
Written by Ruslan Krohalev and Kate Shokurova. Previously published at https://shakuro.com/blog/how-to-integrate-combine-with-swiftui-to-make-better-apps

Published by HackerNoon on 2019/12/24