Threading, Async & Await
The course is part of this learning path
This course explores the concepts of threading, async, and await in iOS and how you can employ them in your app builds.
- Understand how to implement threading (aka concurrency) in your iOS apps
- Learn about the concepts of async image and await and how to use them
This course is intended for anyone who wants to learn how to develop apps on iOS.
To get the most out of this course, some basic knowledge of iOS development would be beneficial. We recommend that you take this course as part of the Developing Mobile Apps for iOS learning path.
Hi, within this lecture, we're going to continue our Crypto Crazy application and we have just completed the model. Now what we're going to do, we're going to write the service, okay? So that we will be ready to move onto the ViewModel next and then go to the view in order to use everything that we have been building so far. Great. Now, since we have the model, we're going to use it in order to decode the incoming JSON data once we download it. But how do we download it? Of course, we know the answer to that, right? We can just use the URL session with data task and we can just do it asynchronously and use the dispatch queue in order to update the main thread. So, let's see how it's done. I'm going to go into the service. Of course, you don't have to group every file, right? But it's a good habit to do so. We do this every time like in a daily basis in the professional industry, so you better get used to it because you will be working with big projects once you go into the professional life. So, I'm going to create a new Swift file and call this Webservice, okay? So, that's it. We're just going to create a class and I'm going to call this Webservice. So, inside this class, I'm going to have only one function and that function will be responsible for creating the URL session stuff. So, I'm going to call this downloadCurrencies or something like that, I believe it's pretty straightforward so we can go with the downloadCurrencies and under the function, we can just call the URL session, right? So, URLSession.shared.data task and we have worked with this before. If you remember, we're going to have to go with the URL not the request and also with the completion handler. So, it gives us the data response or error. It's great. That's all we need. So, of course, it's very easy to work with these NV, we are used to it by now. So, for the URL, I'm going to ask it from here, okay? I don't want to just give the URL right now, so I'm going to just write URL and ask for it. And also I'm going to write a completion block over here. So, we see this every time, we use this every time, but how do we create this? So, what is a completion block? It's like a block that we actually receive when the function is completed, like completion, okay? So, how do we do that? I can write this like @escaping and open a parenthesis and call Result, like this. Don't worry about it, I'm going to explain what this is but I'm writing Result with capital R and I'm going to open the angler brace over here. So, let me try and show you if we can see what kind of thing it expects us to write. For some reason, its doesn't actually let us know what's going to be happening so I'm going to say just return void over here but inside of the Result, it actually asks us to write the success and error or success and failure cases. So, what do I mean by that? So, what I'm doing over here is that I'm going to download something with data task, okay? And this will be of course, run asynchronously, okay? This will be in the background by default and once it completes, I'm going to just execute this completion block since I am using @escaping over here, it is now available for me to do so. Since I'm using @escaping it means that wait until this is done and just get the result out of it. So, since we are downloading something it can be a success or a failure for many reasons, right? It can be a success and we can get what we want like a list of cryptocurrencies in this case, so I'm going to write that. List of cryptocurrencies and beware that I'm writing this optional and also inside of an array because we're going to be receiving a list of cryptocurrencies, not one cryptocurrency. And for the failure, I'm just going to receive an error. Of course I can use and like a regular error protocol over here but it's a good idea to create your own error protocols, or own error cases and just deal with that. So, if you come over here and create an enum, you can write DownloaderError and you can create some cases, okay? We're just going to make this up. We don't have like rules over here, like we can have a case of bad URL, we can have a case of no data, we can have a case of data parse error. So, what do they mean if we provide a bad URL then we're not even going to get a response back. Maybe we get a response back but there is no data. Maybe we get the data but we cannot parse it for some reason, maybe we have written the model wrong or decoded wrong for some reason and we need to identify these errors. So, that's why I have created this and it's very easy to do as you can see. So, I'm going to come over here in this Result and write the DownloaderError. So, again, what I did over here is to create the downloadCurrencies with the URL and a completion block since I've used the @escaping. This will wait until this is done and get the result, okay? So, I can just use this result in anywhere I use this downloadCurrencies function. So, since this is going to be run, be executed asynchronously, we need this to be @escaping. So, if you hit enter to this completion block, I'm going to write data to it and response to it and error to it. Of course, they are all just variable names and you can just make it up. So, how do we proceed? We have an error, right? So, they are all optional by the way, the error, the data, the response, they are all optional. So, first of all, we need to take this into consideration. First of all, I'm going to unwrap the error and check if there is any error. If there is any error most probably there isn't any data or response so I can just give it like a bad URL or any other error type that I want. So, I'm going to say if let error = error, okay? And open this, and if this is error, if this equals to error, it means that there isn't any data or response as you can see everything is optional in this case. So, if this is not nil then I know that there is something wrong and I'm going to just say print error.localizedDescription but more importantly I'm going to call the completion block over here and as you can see it asks for a result and for a result, I'm going to hit . and give it a success or a failure. This is coming from the Result itself, the Result class itself, the Result of Swift, okay? I didn't write this failure or success cases, it's coming from the Result itself but we need to specify whether this is a failure or a success. If the error is not nil, of course, this is a failure and as you can see now, it's asking for a DownloaderError and I can just say .badUrl. So, you can name this anything you want, maybe badUrl is a poor choice of word but you can just make it up your own, okay? So, if there is actually an error then I'm just going to call the completion block and just say badUrl. That's it but I'm going to continue with guard let this time, rather than if let, and I'm going to show you why. So, if guard let data equals to data and if the error is nil, okay, else. So, remember the guard let just make sure that we write the statement first and we are certain that this is going to be holding but if it's not then we are going to return something else, okay? So, in this case I know that data is actually not nil and error is nil in this case but else, if I don't have any data then it means that I don't have any data. So, I'm going to return completion one more time and this time rather than the badUrl, I'm going to say noData failure, okay? Because I don't have any data. So, if I get this data, I'm continuing to write this. If I get this data, then it means that I can create the currencies out of this data like we've done before. So, how do I do that? I'm just going to say guard let currencies equals to JSONDecoder this time. So, JSONDecoder.decode and as you can see it asks for the decodable protocol over here and we already have a decodable struct. So, what I'm going to do, I'm going to say Cryptocurrency.self and from data itself. So, this is the data that I have been guard letting over there, okay? So, unwrap this. And of course, we're going to have to write the else statement as well. But we have some kind of errors over here as you can see, first of all this should be a try rather than a regular thing because it can throw an error and I'm going to do this try optional. And I believe this should be inside of the braces over there because it will be decoded as a list, not only a single Cryptocurrency again, so don't forget to add this to it. And then later on, if we cannot decode it, what will happen? Then I'm going to call the completion block, okay? I'm going to call the completion block and I'm going to say this is a failure but this time this is a data parse error because at this point I know that I have the data but I cannot convert it to the currencies. But as you can see this is very, very valuable to create the DownloaderError or error enums of your own. And at last if I can get everything then I'm going to just say completion success and just give it the currencies, okay? That's it, that's how you build your else session. Of course, you can just do it with another way and don't forget to say .resume at the end as well. So far so good, now we have created this downloadCurrencies and it's perfectly fine. We're using this @escaping, we're using this Result and stuff and later on, we're going to change this a little bit, okay? Just bear that in mind. So, if we manage to do that then it means that we downloaded the whole thing, and remember you can change this badUrl, you can change this noData or data parse error, this is all the terms that I came up with, you don't have to follow the same exact rules when you write your own applications. Maybe it's very viable this time but maybe you can find some more appropriate things in another application, okay? So, again, if we can get everything then we're going to just pass it to the completion block and once we apply this function into another class like a view model, then you will see how it's used. So, if you have never seen this before, don't worry about it. If you haven't understood the @escaping and if you haven't understood the Result class over here, don't worry. Once we apply this to the view model in the next lecture, it will be much more clearer to you. So, we're going to stop here and within the next lecture, we're going to go ahead and create our view model in order to execute or implement this Webservice that we have just written.
Atil is an instructor at Bogazici University, where he graduated back in 2010. He is also co-founder of Academy Club, which provides training, and Pera Games, which operates in the mobile gaming industry.