Using Closures, generics, POP and protocols with associated types to create reusable API’s for your networking layer

http://startappstudio.com/

As an iOS Developer, most of the apps, you’ll build will have to get some sort of data from a server. Although there are more than a few networking libraries that you could integrate into your application such as Alamofire or Tron , I wanted to make this tutorial using the URLSession class which is what Apple provides to handle networking calls, this will help you understand how third party networking libraries work under the hood, will let me show you how to create a reusable API for your networking layer mixing different tools that Swift provides, and also will let me give you a brief explanation of how closures works and why they are needed when declaring asynchronous methods.

We are going to create an App that fetches data from the Itunes movie API, and will display a list of movies in a tableview. We will make this application step by step covering important Swift topics. Before we start let me list them for you…

  • Protocol Oriented Programming
  • MVVM
  • Closures
  • Generics
  • Asynchronous calls
  • Parsing JSON with nested dictionaries
  • Reference cycles
  • JSON
  • URlsession
  • Handling network errors in Swift
  • PAT’s aka Protocols with Associated Types.

Let’s start by cloning or downloading this repo, As you can see in the files I won’t use storyboards in this tutorial so we will do all the layout in code, you can also see that there is a Movie.swift file that holds two structs for our models, and a file with a custom cell, now run the project.

You should see a tableview with one cell with data coming from the dummyMovie function inside MovieFeedVC. Let’s focus our attention on the cellforRowAtIndexPath method.

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID) as! MovieCell
let movie = moviesArray[indexPath.row]
cell.movieTitleLabel.text = movie.title.uppercased()
cell.dateLabel.text = "Release date: \(movie.releaseDate)"
cell.priceLabel.text = "\(movie.purchasePrice.amount) \(movie.purchasePrice.currency)"
return cell
}

As you can see here, we are just displaying the data from the movie object in the cell’s UI elements, you can see that we are doing some modifications in order to display our data in a “formatted” way, the problem here is that by now, the model and the views are tightly coupled, this approach will also make harder implement testings, and depending on the amount of data that you want to display your viewController will potentially become massive.

We are going to use a View Model to clear separation of concerns between model, view, and controller. Let’s start by creating a new file called MovieViewModel. it will contain this struct, don’t forget to import UIKit.

struct MovieViewModel {
let title: String
let imageURL: String
let releaseDate: String
let purchasePrice: String
let summary: String

init(model: Movie) {
self.title = model.title.uppercased()
self.imageURL = model.imageURL
self.releaseDate = "Relase date: \(model.releaseDate)"
if let doublePurchasePrice = Double(model.purchasePrice.amount) {
self.purchasePrice = String(format: "%.02f %@", doublePurchasePrice, model.purchasePrice.currency)
} else {
self.purchasePrice = "Not available for Purchase"
}
self.summary = model.summary == "" ? "No data provided" : model.summary
}
}

This struct contains the same properties of the Movie object, so why are we over engineering things here and creating a brand new object? well, in an app this size could probably look too much implementation and probably we can just set the format of our display using computed properties in the model so we can return a formatted string for each of them, but this is not the model’s job; by using a View Model all you have to do is pass a model data and this object will handle the state for your display giving you a formatted representation of your model. Let’s fix this by creating a method in MovieCell that will take as an argument a view model and will display it, and also use it by replacing the implementation of the cell for row like this…

//1 This goes in MovieCell class
func displayMovieInCell(using viewModel: MovieViewModel) {
movieTitleLabel.text = viewModel.title
dateLabel.text = viewModel.releaseDate
priceLabel.text = viewModel.purchasePrice
}
//2 This goes in MovieFeedVC
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID) as! MovieCell
let movie = moviesArray[indexPath.row]
let movieViewModel = MovieViewModel(model: movie)
cell.displayMovieInCell(using: movieViewModel)
return cell
}

You should see the same but this time you used a View Model to do it!

Now, let’s get rid of the dummy data and let’s actually get something to work with, from a server, start by creating a new empty file and name it JSONDownloader, we are going to create an object that will handle our network call. Copy and paste.

struct JSONDownloader {
//1) 
let session: URLSession
init(configuration: URLSessionConfiguration) {
self.session = URLSession(configuration: configuration)
}
init() {
self.init(configuration: .default)
}
typealias JSON = [String: AnyObject]
typealias JSONTaskCompletionHandler = (Result<JSON>) -> ()
//4)
func jsonTask(with request: URLRequest, completionHandler completion: @escaping JSONTaskCompletionHandler) -> URLSessionDataTask {

//5) let task = session.dataTask(with: request) { (data, response, error) in
guard let httpResponse = response as? HTTPURLResponse else {
completion(.Error(.requestFailed))
return
}
if httpResponse.statusCode == 200 {        
if let data = data {
do {
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: AnyObject] {
DispatchQueue.main.async {
completion(.Success(json))
}
}
} catch {
completion(.Error(.jsonConversionFailure))
}
} else {
completion(.Error(.invalidData))
}
} else {
completion(.Error(.responseUnsuccessful))
print("\(error)")
}
}
return task
}
}
//2)
enum Result {
case Success(T)
case Error(ItunesApiError)
}
//3)
enum ItunesApiError: Error {
case requestFailed
case jsonConversionFailure
case invalidData
case responseUnsuccessful
case invalidURL
case jsonParsingFailure
}

So this is a lot of code, I divided this snippet into 5 parts so let’s go step by step, we start by creating a URLSession property and setting its configuration to default configuration when the JSONDonwnloader object it’s initialized; in the next lines we are just using a typealias to improve readability, the first one named JSON is a dictionary of type [String: AnyObject] and the second one is a completion handler that takes as a parameter an enum called Result that is constrained to a typealias JSON type.

On section 2, I declared an enum called Result that will handle the network response, it has a generic constraint type T and two cases, one for success with an associated value of type T, and one for potential errors with an associated value of type ItunesApiError. On section 3 I declared an enum called ItunesApiError that conforms to the Error protocol and has a list of cases with potential errors.

On section 4 we declare an asynchronous function that takes as parameters a URLRequest, and a completion handler and returns a URLSessionDataTask. So, when creating asynchronous functions why do we need to accept a closure as a completion handler for logic that we want to execute after the function is complete? well, that’s because we can’t return values from asynchronous code because we don’t know when that will be returned. Since we have no guarantee when the asynchronous code will be completed we can not expect logic to follow normal order, also completion handlers have to be marked @escaping since they are executed some point after the enclosing function has been executed, we will talk more about closures and how to avoid reference cycles in just a bit, let’s jump for now to section 5 and let’s talk about how to handle errors in Swift.

Swift can handle both automatic and manual propagation of errors while objective C can not. In Swift, we usually handle errors automatically by throwing errors but unfortunately in asynchronous functions that are not the way to do it and we have to check for errors following Objective-C conventions, where we have to check for the result first and if nil, check the error. I bet you a beer (no kidding) that sometimes on debugging you are just checking if the error it’s not nil and wondering why it’s not if you intentionally made a bad request, well that’s because sometimes (very often actually) the data can be nil and the error too.

That’s why in section 5 we are going to use a guard statement to check for the response first then we will check for the status code of the response and if it’s 200 we will go from there then check the data and after that, we will use a do catch to safely perform the JSONSerialization logic, if that’s successful we will use our completion with the .Success case and pass the JSON as a parameter, remember that because these are asynchronous calls they may be run on a different thread.

For potential errors, we are using the .Error case and passing an ItunesApiError case as a parameter, Lastly we just return the task.

Now that we have our JSONdownloader object let’s create our API, but first, let’s see the iTunes API data that we are going to use, open this using JSON viewer or any tool that you use to see JSON structures, https://itunes.apple.com/us/rss/topmovies/limit=25/json as you can see this is a feed dictionary that contains an entry key that holds an array of dictionaries with nested dictionaries inside of them, we need to parse this data, go to your Movie file and add this extension.

extension Movie { 
struct Key {
static let titleDict = "im:name"
static let imageURLArray = "im:image"
static let releaseDateDict = "im:releaseDate"
static let categoryDict = "category"
static let rentalPriceDict = "im:rentalPrice"
static let purchacePriceDict = "im:price"
static let itunesLinkArray = "link"
static let summaryDict = "summary"
static let label = "label"
static let attributes = "attributes"
static let amount = "amount"
static let currency = "currency"
static let href = "href"
static let term = "term"
}
//failable initializer
init?(json: [String: AnyObject]) {
guard let titleDict = json[Key.titleDict] as? [String: AnyObject],
let title = titleDict[Key.label] as? String,
let imageURLArray = json[Key.imageURLArray] as? [[String: AnyObject]],
let imageURL = imageURLArray.last?[Key.label] as? String,
let releaseDateDict = json[Key.releaseDateDict] as? [String: AnyObject],
let releaseDateAttributes = releaseDateDict[Key.attributes],
let releaseDate = releaseDateAttributes[Key.label] as? String,
let purchasePriceDict = json[Key.purchacePriceDict] as? [String: AnyObject],
let purchasePriceAttributes = purchasePriceDict[Key.attributes] as? [String: AnyObject],
let priceAmount = purchasePriceAttributes[Key.amount] as? String,
let priceCurrency = purchasePriceAttributes[Key.currency] as? String
else {
return nil
}
self.title = title
self.imageURL = imageURL
self.releaseDate = releaseDate
self.purchasePrice = Price(amount: priceAmount, currency: priceCurrency)
if let summaryDict = json[Key.summaryDict] as? [String: AnyObject], let summary = summaryDict[Key.label] as? String {
self.summary = summary
} else {
self.summary = ""
}
}
}

This extension has a struct named Key that holds all the keys of the JSON data, I also provided a failable initializer to prevent our app crashes in the event that the object is nil. Finally, I access to each key of the nested dictionary using a guard statement and assign the values to the properties, run the app and you can see that nothing changed that’s because we are not using this initializer yet. Let’s do it now in our own API, create a new file called MovieService and copy and paste this.

//2 conforming the protocol
struct MovieService: Gettable {
//3
let endpoint: String = "https://itunes.apple.com/us/rss/topmovies/limit=25/json"
let downloader = JSONDownloader()
//the associated type is inferred by <[Movie?]>
typealias CurrentWeatherCompletionHandler = (Result<[Movie?]>) -> ()

//4 protocol required function
func get(completion: @escaping CurrentWeatherCompletionHandler) {

guard let url = URL(string: self.endpoint) else {
completion(.Error(.invalidURL))
return
}
//5 using the JSONDownloader function
let request = URLRequest(url: url)
let task = downloader.jsonTask(with: request) { (result) in

DispatchQueue.main.async {
switch result {
case .Error(let error):
completion(.Error(error))
return
case .Success(let json):
//6 parsing the Json response
guard let movieJSONFeed = json["feed"] as? [String: AnyObject], let entryArray = movieJSONFeed["entry"] as? [[String: AnyObject]] else {
completion(.Error(.jsonParsingFailure))
return
}
//7 maping the array and create Movie objects
let movieArray = entryArray.map{Movie(json: $0)}
completion(.Success(movieArray))
}
}
}
task.resume()
}
}
//1 using associatedType in protocol
protocol Gettable {
associatedtype T
func get(completion: @escaping (Result<T>) -> Void)
}

Again, this is a lot of code and we will go piece by piece, the first part it will probably the most complicated part to understand on this tutorial, don’t worry if you don’t understand protocols with associated types just yet, you are not alone, me as many other iOS developers are trying to understand the best way to use protocols with associated type aka PAT’s .

We’ve seen so far that in Swift functions and types can be made generic, but what if you want to create a generic protocol? we might think that a protocol is already something generic because any type can conform to it but what about the internals of a protocol? can we define a protocol where the requirements themselves are generic? yes, we can but not in the same way that we do it with a function, that’s why Swift introduced Associated types to protocols in order to help us create the internal implementation of a protocol generic. If you think about it PAT’S are just a fancy name for a protocol that has generics inside of it. If you want to know more about them I recommend this talk.

Ok, here we have the implementation of a protocol that I saw in one talk of Natasha the robot where she talks about real daily examples of using PAT’s , the Gettable protocol has an associated type T and also has a required function that takes as an argument a closure that accepts a Result enum constrained to a generic type T and returns void.

On section 2 we are just making the MovieService conform to the protocol, in section 3 we have a couple of properties one for the endpoint and the other one for a JSONDownloader object. Also a typealias for a closure, again just to improve readability, the closure takes as an argument a Result enum constrained to an array of optional Movies and returns nothing, now the compiler inferred that the associated type is an array of optional movies, just like generics infer types.

On section 4 we are making the implementation of the required get function of the Gettable protocol, we start by using a guard statement to check for the URL, in section 5 we create a request constant and a task constant, in this last one we are using the jsontask method of our JSONDownloader instance that returns a URLSessionDataTask, inside its implementation we are checking for the result which is of type Result enum, and using a switch statement to perform actions for each case.

On section 6, inside the .Success case, we are parsing the response from the server, if you go to the endpoint and see the JSON format you can see that is a dictionary with a key “feed” and has a nested array with a key “entry”.

On section 7 we are mapping the array using our failable initializer and creating Movie objects inside the movieArray, and passing it in the .Success case of our completion handler.

Finally, the task.resume() will trigger the code.

I know that this is a lot of implementation but the good thing is that the basic structure of this two objects (JSONDownloader and MovieService) will help you create different API implementations for your own projects.

Time for a break, now we have our JSON parser and API service objects, I am going to drink some tea and you should do it too, in the next section we will use what we have done so far to finish our app, and we will talk a little bit more about closures and how to avoid reference cycles when using them.

Ready? ok let’s go to the MovieFeedVC and let’s replace the dummyMovie call for this, copy and paste

//add this a property   
let service = MovieService()
//add this in viewDidload
service.get { [weak self] (result) in
switch result {
case .Success(let movies):
var tempMovies = [Movie]()
for movie in movies {
if let movie = movie {
tempMovies.append(movie)
}
}
self?.moviesArray = tempMovies
//dump(self.movies)
case .Error(let error):
print(error)
}

Here we are creating a service property of type MovieService, and in viewDidload, we are calling the get function, the completion returns a result enum, inside the implementation of the closure we are switching between the .Success case and .Error and perform actions, in the success case, we are declaring a tempMovies array variable where we are going to append our movies just if they are not nil, remember that the MovieService provides in the success case an array of optional Movies, after safely unwrap the optional movies we append them to the tempMovies array and after that, we assign it as a value to our moviesArray property. Now let’s add a property observer to our moviesArray property that will reload the tableview when the moviesArray is set. Replace the moviesArray property for this…

private var moviesArray = [Movie]() {
didSet {
self.tableView.reloadData()
}
}

Run the app and you should see a list of movies.

If you are asking yourself… man, this pink dots looks cool but where are the movie pictures? it’s ok we will show them in a bit, first please let me talk to you a bit more about closures and why are they needed when declaring asynchronous functions.

Let’s focus our attention on the get method on viewDidload; first, did you ever wonder why when writing asynchronous code we need to specify self to refer to a stored property or a method? well, that’s because since the code is dispatched to a background thread we need to capture a reference to the correct object. Unfortunately, by using self inside a closure and how closures are designed, relying on capturing variables and the context they are defined on, we are now capturing self as a strong reference and this result into a reference cycle.

We need somehow to stop that reference cycle by providing a weak or unowned reference to self, and we can do that using a capture list, the syntax is just an array that holds a list of references, you can see the syntax as [weak self] in the get method in viewDidload, we also can mark self as unowned and that will depend on your own implementation, so if you are wondering what is the difference between them and when to use which, follow this two rules.

  • We mark a captured object as unowned if it has the same lifetime or longer than the enclosing closure.
  • We mark a capture object as weak if it has less lifetime than the enclosing closure.

So good so far, our code in viewDidload works but by implementing it this way it will make it hard to test when you want to run unit testing on it, so let’s change the implementation by creating a generic function which will be constrained to our Gettable protocol. Take out the service call in viewDidLoad and put it inside this new method like this…

private func getMoviesGettable>(fromService service: S) where S.T == Array<Movie?> {
service.get { [weak self] (result) in
switch result {
case .Success(let movies):
var tempMovies = [Movie]()
for movie in movies {
if let movie = movie {
tempMovies.append(movie)
}
}
self?.moviesArray = tempMovies
//dump(self.movies)
case .Error(let error):
print(error)
}
}
}

So what is going on in the signature of this function? well as I mentioned before this is a generic function constrained to the Gettable protocol type, it takes as a parameter a service that conforms the Gettable protocol and we added the where keyword in the signature of the function to add another constraint, but this time to the associated type of the protocol that in this case must be an array of optional movies, this approach will make your function injectable and a bit easier to test. I wanted to show you just some real implementation of the benefits that protocol with associated types can bring. (Again, I need to mention Natasha the robot which I took the idea for this implementation).

Ok now call the method in viewDidLoad like this…

getMovies(fromService: service)

Run the app and everything should still work, ok now let’s add the images to this app using POP. Following Swift conventions, we are going to create a protocol to extend the functionality of the UIImageview Class instead of subclass it, and we will give it the power to cache images. Create an empty file and call it Cachable.

import UIKit
//1 Create the protocol
protocol Cachable {}
//2 creating a imageCache private instance
private let imageCache = NSCache<NSString, UIImage>()
//3 UIImageview conforms to Cachable
extension UIImageView: Cachable {}
//4 creating a protocol extension to add optional function implementations,
extension Cachable where Self: UIImageView {
//5 Creating the function
typealias SuccessCompletion = (Bool) -> ()
func loadImageUsingCacheWithURLString(_ URLString: String, placeHolder: UIImage?, completion: @escaping SuccessCompletion) {
self.image = nil
if let cachedImage = imageCache.object(forKey: NSString(string: URLString)) {
DispatchQueue.main.async {
self.image = cachedImage
completion(true)
}
return
}
self.image = placeHolder

if let url = URL(string: URLString) {
URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in

//print("RESPONSE FROM API: \(response)")
guard let httpResponse = response as? HTTPURLResponse else {
return
}
if httpResponse.statusCode == 200 {

if let data = data {
if let downloadedImage = UIImage(data: data) {
imageCache.setObject(downloadedImage, forKey: NSString(string: URLString))
DispatchQueue.main.async {
self.image = downloadedImage
completion(true)
}
}
}
} else {
self.image = placeHolder
}
}).resume()
} else {
self.image = placeHolder
}
}
}

First, we create an empty protocol so it will make objects that conform to it not have required implementation. Second, we have an imageCache private constant of type NSCache which is the class in charge of cache images just like holding values for a key like a dictionary. On section 3 we are making that UIImageview conforms to the Cachable protocol.

On section 4 we are creating an extension of Cachable and constraint it to UIImageView class, this means that only UIImageViews instances or child’s of this class will have access to this functions.

Finally, on section 5 we create a function that takes as a parameter a string, an optional image for a placeholder that will be used until the real image from the server appears, and a completion handler that accepts as an argument a Bool and returns void, this completion handler will pass true as argument on success and will help us perform actions after the image is loaded. Now let’s use this, go to your MovieCell file and inside the method of displayMovieInCell add this…

movieImage.loadImageUsingCacheWithURLString(viewModel.imageURL, placeHolder: nil) { (bool) in
//perform actions if needed
}

Now run the app for one final time, and you should see this (if not check your console for the App transport security setting error and go to your info.plist and change allow arbitrary loads to true)

Let’s recap on this tutorial we use MVVM to show a formatted version of our data into our views, we mix protocols with associated types, generics and closures to create reusable API’s to handle networking, we saw the convention to handle errors in asynchronous calls in Swift and how to avoid retain cycles by using capture lists and also understand why does closures are needed on asynchronous methods.

I hope you find this useful in your applications, I will be more than happy to know any tip to make this even better!

You can find the final project here.

Peace!

If you need an app just let me know http://startappstudio.com/