Combine and URLSession in UIKit

Together, combined

Image for post
Image for post
Photo by Gregory Hayes on Unsplash

Difficulty: Beginner | Easy | Normal | Challenging

This article has been developed using Xcode 11.4.1, and Swift 5.2.2

This article is about using Combine in conjunction with UIKit in order to get the basics of the former down without the expectation of knowledge about SwiftUI. This implementation calls a network manager much like a login for any particular App.

The approach

I’m not going to use storyboards, but am going to use UIKit and MVVM to keep things rather simple. I’m going to apply some Dependency Injection skills in order to test URLSession, and this uses a keychain manager to save the token (which is also tested).

Prerequisites:

  • You will be expected to be aware how to make a Single View Application in Swift.
  • I recommend that you download the repo and read the code along with this article, although I have written the article to be a stand-alone document

Terminology

Combine: A framework that provides a declarative Swift API for processing values over time

The Examples

Combine has many possible uses, and we are not constrained when linking Combine to SwiftUI.

The example code shows instances of:

  • Setting up pipelines to lock a UIButton until the values entered into a UITextField are valid
  • Create a pipeline to perform an asynchronous network call, choosing how and what to update within a view
  • Create a pipeline to adjust the state of a UIButton dynamically according to the text in a UITextField updating the user interface accordingly

You know what this reminded me of?

Material Design!

The App

Image for post
Image for post

The App has a simple login and a material-design alike interface. When the username and password are entered, the view controller asks the viewmodel to make an API call.

For fun, I made the views have a xib file although the project has been created with a storyboard, using this technique.

Each of the following sections focus on the LoginViewControllerLoginViewModelLoginView sections of the App enclosed in the repo, but similar ideas are sprinkled through the App as a whole.

The View Controller

LoginViewController is not substantially different from any UIViewController that you might write in UIKit.

Initially I left the cancellable objects AnyCancellabl` as single vars in the project class. The reason that *any* of these exist is that a transaction may be cancelled when the token is deinitialized

Image for post
Image for post
Click fot Gist

whereas a better approach is to store these subscriptions

private var subscriptions = Set<AnyCancellable>()

perhaps in an array as shown above, and then the binding can be stored in the array:

Image for post
Image for post
Click for Gist

I’ve decided to create view model instances that conform to a protocol (indeed, the UserDataManager() does the same thing)

Image for post
Image for post
Click for Gist

on the side of the view model we can bind to a AnyPublisher<Bool, Never> in a view model, and the view can be attached accordingly (here updating the loginButton).

Image for post
Image for post
Click for Gist

likewise we can bind out UITextField to a @Published var username: String = "" which is situated in the view model.

Image for post
Image for post
Click for Gist

which of course links ot the selector as defined:

Image for post
Image for post
Click for Gist

The View Model

The view model has two @Published var which creates a publisher of this type, which can then be accessed through use of the $ operator.

Image for post
Image for post
Click for Gist

Now validLengthUsername is defined as the following, where debounce is used ensuring we only receive elements when the user pauses or stops typing. removeDuplicates ensures that only distinct elements are used.

Image for post
Image for post
Click for Gist

similarly we check the password

Image for post
Image for post
Click for Gist

and both of these use eraseToAnyPublisher to make the publisher visible to the downstream publisher.

To combine the two, .zip is used to combine the two operators together, and that’s an awesome.

Image for post
Image for post
Click for Gist

of course this is subscribed to in the view controller (as shown above).

Now the API calls are made from this view model (actually upon the pressing of a button in the view controller)

This makes use of sink in order to store the token in the keychain, and then send a message back to the view controller using shouldNavSubject.

Now this shouldNavSubject is an AnyPublisher<Bool, Never> that leverages PassthroughSubject which is an operator sitting in between a Publisher and Subscriber.

Image for post
Image for post
Click for Gist

The HTTPManager

Networking is taken care of by a HTTPManager as defined below, but the approach that is taken is much like my basic network manager in that a protocol is used — and the URLSession can be swapped out during testing so when we make API calls they aren’t actually hitting the API (in testing, of course)

Image for post
Image for post
Click for Gist

The protocol?

Image for post
Image for post
Click for Gist

We can come onto the testing later in this article.

Since we are using that URLSession that is replaceable during testing. This is quite tricky when using dataTaskPublisher — here is the approach to obtain the correct Output and Failure types by creating a protocol that we get URLSession to conform to, creating a typealias that can be returned by the actual class or the mocked version.

Image for post
Image for post
Click for Gist

Testing

As has been mentioned above, we can mock URLSession

Image for post
Image for post
Click for Gist

which can then be used by a mocked HTTPManager

Image for post
Image for post
Click for Gist

This is then setup in the test classes themselves since we can setup

Image for post
Image for post
Click for Gist

adding a test

Image for post
Image for post
Click for Gist

Which means, crucially, that we don’t use a real API call to get data during testing and instead we get the data from a json file in the test target — that’s a good job all round.

Conclusion

This article is a rather useful implementation of Combine using UIKit, we can see this in this article.

It is rather complex in the description, but rather easy in the implementation. Take a look at the attached Repo. It is a rather wonderful thing.

People do like Apple’s documentation on this topic.

If you’ve any questions, comments or suggestions please hit me up on Twitter

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