Networking in Swift the Right Way

Data:contentsOf: will not suffice, because as soon as the user ends the app, the data is lost.

It doesn’t store partial data and magically resume when your app resumes.

This heavy lifting is where the networking libraries of Swift come in.

Do not allow me to understate Data:contentsOf:, it has its place, and it’s wonderful.

This method is meant for Local and App resources only.

Note: If you want to get to the local system resources outside of your app, you will need to turn off sandboxing.

If you want to try out the example above, just create a new playground and drop a file in the playground’s resources folder named wishlist.

txt with whatever your wishlist is.

So we talked about local files and that Data:contentsOf is the wrong way to go about remote resources, but what is the right way?The right way is to use URLSession.

URLSession provides a nice library of utilities to help you download your files and keep track of requests.

Let’s take a look at a simple implementation using Arthur the cat from above.

New stuff to cover here.

First, we create a session instead of using the default implementation.

While I could have just used URLSession.

shared.

dataTask I opted to create the session.

This way I always have the option to customize the connection properties later on.

It is my own personal preference.

For more information on shared sessions see Apple’s documentation.

shared — URLSession | Apple Developer DocumentationIn general, when working with a shared session, you should avoid customizing the cache, cookie storage, or credential…developer.

apple.

comNext, we have to create a task.

This task is responsible for downloading the file and handling of the response.

The task itself is a closure, but we use trailing closure syntax and pass (data, response, error) to the closure.

These are all optional values, and if you use Xcode, the autocompletion will note these are optional.

data is the data we expect to receive from the remote location, response is the response header information we receive and error is populated only when an error occurs.

Inside of the closure, we first check if we received an error.

The error isn’t related to a client or server error, but a device communication error such as the device being in airplane mode.

If we received a 404 — Page Not found message from the server, the call would still be considered successful by URLSession even though the data retrieval was not successful.

This is why the error is separate from the response.

If we didn’t receive an error, then we could continue on, first checking to ensure we received a status code of 200.

The URLResponse type does not contain a status code property, however, HTTPURLResponse does.

What’s even better is URLResponse can be cast toHTTPURLResponse giving us access to the status code.

200 — OK is not the only good response code we could check but it’s the most popular.

For a full reference, there are resources available.

This one is pretty nice.

HTTP Status CodesHTTP status codes and how to use them in RESTful API or Web Services.

www.

restapitutorial.

comIn our case, we expect to get a status code of 200 back, so it’s the only one we need to check.

If we wanted to check more we could make an array of all of the status codes we wanted to check such as:let acceptableCodes = [200, 201, 202, 203, 304]Then use the following to check the status:if acceptableCodes.

contains(httpResponse.

statusCode) { .

}This allows us to add more acceptable codes to our array as needed, simplifying our future changes.

Finally because data is optional, we need to unwrap data?.into something that we can use right away.

I unwrapped data into receivedData which gives us non-optional data, and in the off chance where data was still nil, we don’t crash our program.

With our unwrapped data, we can set the image property of imageView to the a UIImage representation of our receivedData.

Because this is UI work we need to do it on the main queue:DispatchQueue.

main.

async { imageView.

image = UIImage(data: receivedData)}Easy enough.

There’s one more thing that needs to be done before all of this even downloads the file.

Remember when we create a task which was responsible for downloading files?.That task has a method associated with it called resume().

resume() tells the task to perform this download task.

We need to call this method to actually start the download process.

If it seems cumbersome to do this, think about how much flexibility this provides for your application.

You can make your download tasks, then call them at the opportune time, or you can make a download task generic, and use it for multiple calls, reusing the same download task for multiple remote resources.

SummaryThere is a lot more we can expand on when talking about URLSession, but this is just a simple introduction to the right way to download files instead of thinking data:contentsOf: is the way to do it.

URLSession also offers delegates so you can allow your user to pause, resume, and stop downloads, much like a user would when using Apple Music, the App Store, or even watching videos.

There is even a background task option which allows you to continue downloading files, even when the app has been pushed to the background (not when the user force quits the app).

For quick one-off tests in development (e.

g.

figuring out what the format of your received JSON will look like), Data:contentsOf: is fine, but for production environments, you should always use URLSession for remote data.

.

. More details

Leave a Reply