Beginning Swift Programming Part 15 — Enums with Generics and Closures

Bob Roebling
Swift2Go
Published in
5 min readApr 29, 2018
Photo by Kelly Sikkema on Unsplash

In the previous article, we talked about error handling.

We covered the basics on how to create your own errors and use them in code instead of just crashing.

If you are interested to know, I only have 5 more topics left and then I’ll point you to some of my favorite resources that helped me write programs.

This article is going to be short, but it will blend a few concepts that we’ve learned and may make you think about how you write code in a new way.

Enums with Generics and Closures

Previously when we talked about enums, I told you that it would give you options to chose from and that we were locked down to only those options. We can use associated values in our enum cases to define the type that will be instantiated when we use the Enum.

Pretty cool right? If we don’t know what type will be entered we can use generics.

This allows us to use any type when we create a new address or coordinate.

Photo by Markus Spiske on Unsplash

Now I told you about using closures, enums, and generics altogether. This is a cool trick I learned from Natasha the Robot and I’ve used it a lot in my own code.

I would be lying if I told you this was easy to understand. Even with whitespace, this is still pretty difficult. So I’m going to break it down.

struct Download — The struct that holds our logic. Might be a class since it’s doing network calls.

This gives us one of two options, either .success(anything) or .failure(someError).

func start(_ completion: @escaping (Result<T>) -> Void) { }

This is a method that takes a function. The function takes a case from our Result Enum and returns nothing.

let session — creates a session from URLSession with an ephemeral configuration. Ephemeral means in-memory only, or the equivalent of private browsing.

let url — creates a URL from the string we pass in, this would be whatever web page you want to download from.

let task = session.dataTask(with:URL) { data, response, error in ... } provides us with a task that we can use to download our data. Think of this as the horse you ride to get where you are going. data is the binary (1s and 0s) data you receive, response is the response headers that you receive. More on this in a sec. error is if our call failed. This is important. If you receive a 404 page not found error in your call, then you would get that information from theresponse. Even if you got a 500 which means something on their server failed, you’d still get this in response. error for us means, we could not do what we needed to do to even initiate the request. WE failed, not THEY failed.

guard error == nil else { ... } — this is a pre-flight check we perform. If we received an error there is no need to go on, we just return the error here and exit.

You already know about DispatchQueue.main.async so I’ll just talk about defer { session.invalidateAndCancel() }. Invalidate and cancel means stop whatever you are doing and get rid of session, we don’t need it anymore. But I bet you were asking about defer. defer is used when we want this bit of code to run no matter what, but wait until this method is finished before running. It’s like a deinit for your methods, functions, and closures.

Then we use completion(.failure(error!). completion comes from the parameter name in start. .failure is a case from our Result enum. And error! is the force unwrapped error we received from our closure. It’s ok in this situation because we already checked if it was nil and since this code executed, it means it wasn’t nil.

Then we do a little more checking.

if let httpResponse = response as? HTTPURLResponse — unfortunately Swift doesn’t give us direct access to the status code, however, HTTPURLResponse does and we can typecast response as an HTTPURLResponse and it should succeed. If it does, we immediately check to see if we had a successful response with if httpResponse.statusCode == 200.

If both of those are true then we can safely send back our data to be parsed or whatever we are going to do with it. First, we make sure we are doing this on the main queue with DispatchQueue.main.async then use our completion handler to do this with completion(.success(data!)) force unwrapping data because all three of our closure parameters are optional values.

At the end of the start function, we call task.resume() which kicks off the data task. When the call is completed, then we run through all of that code we walked through previously.

To show you how this would look on the other side where we call start it might look something like this.

Summary

So I walked you through a network call in this example. It showed you how you could leverage the power of enums with functions and generics to assist you with your closure returns. This also gave you another look at @escaping to give you more context in how it’s used.

I can’t promise that this network code is an optimized solution, but it is a solution.

You get a lot of flexibility in all of this and I’ve even seen solutions posted in Stack Overflow about how to use closures in enums to provide many paths for your code to take.

It’s a bit tough but when you actually write this out a few times, it makes more sense in how it works. Code autocompletion is a great teacher if you are paying attention.

What’s Next

We’ve been talking a lot about value types, I mean Swift loves it’s value types, but there are use cases where reference types come in. I think they are fascinating and dangerous if not used correctly.

So up next is inout, Lazy, and Getters and Setters.

inout is all about references, Lazy helps with performance, and getters and setters help you change how values are accessed.

Even though some might label these as miscellaneous things, they are extremely useful in various situations.

For now, we covered something new and slightly difficult which means…

Keep practicing.

--

--

Bob Roebling
Swift2Go

Bob is a Senior Infrastructure Administrator and tech evangelist with a background in multiple programming languages.