Networking in iOS with URLSession | JSON and Image data

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
pretty much every ios app these days makes some sort of http request to a server [Music] in this video i'm going to show you the basics of networking in an ios application using url session and i'll show you how you can get json data and download files like images so i've got an app here that has a label at the top and a button in the middle that says random and if i tap on that button nothing happens because i haven't written any codes do anything yet but what i want to happen is i want this label to display a random kanye west quote every time i tap that button and fortunately for me there is an api that i can use called api.kanye.rest that produces a random kanye west quote every time you make an http request and it returns the data as json so all i have to do is make a request to this api get the json data and put it into that label so i'm going to show you how to do this using url session i'm going to start with a very basic example and then i'll work my way up to a more realistic more complex example as we go through the video [Music] so if i run this application right now and tap the random button it's not going to display in the label but it's going to print it out to the console and that's because i'm just printing it out on line 31 instead of putting it in the label right now so every time i tap this button i'm going to get that json object so it looks like that in the browser i'm going to get that as a dictionary in swift and then just print it out and that's what we see here i'm getting it as a string string dictionary every time i tap it i get a new kanye west quote now if we take a look at the code this first line here is creating the url given the url that i want to get the data from and because i know this is a valid url string i'm just going to force unwrap this and that's fine then on line 22 i'm using the shared url session to create a brand new data task and this requires a url which i'm passing in right here and this completion closure this callback function that will get executed once the request completes now it may complete successfully and i get the quote or it may complete with an error so always the first thing we do is check for an error this is a network request and errors are not unlikely so we can check for the error we can handle the error in any way right now i'm just printing it and if there's no error then the body of the http response that's all of the the quote data basically just this thing right here that's going to be in this data object and we need to then pass that as a json object into a dictionary in this case so we're taking that data we're we're forcing it into a dictionary and then i'm just printing out the quote here notice also that when i create the data task here i have to store it in a variable and then i have to resume that task because the url session actually creates the task in a paused state so we always have to remember to resume the task if we actually want to make the http request uh that's a common mistake to just forget to put task.resume when you actually want to make the request so it's this url session and the related objects with url session that are actually responsible for executing these http requests and getting us the data we need as you can tell i'm currently using a lot of forced unwrapped optionals around here and that's definitely not a good thing this is just a really really basic example just to get it working but it's definitely not production ready code we're going to have to add a lot more things before it's ready for a real application before we update all of that i want to just change this so instead of printing out the json i'm going to put it in that label so this is a dictionary where the key is quote and the text is actually the quote so really all i need to do is set the labels text to be json quote so now if i run this we should see the text actually in the label rather than being printed out so i'm going to tap random and then nothing happens and i get an error and this is because all of this code in the completion closure is being executed on a background queue so this is an http request it's a long running task it has to run in the background otherwise it's going to block up all of the ui so this just automatically without us doing anything we'll run on a background cue we can't alter that it'll just have to run on a background cue but when we update the ui we have to update this on the main queue so i have to add that dispatchqueue.main.async right here and put that code inside of this block because this now tells the application anything inside of this block must run on the main queue so now if i rerun the application this should work perfectly actually i should hit the random button and then we see that quote at the top and every time i tap that button it's making that http request it's getting the json data back it's passing that json data and then putting it into the label and this is a really common way of doing things you'll set up your data task you'll resume your data task you'll check for errors you'll pass your data you actually need and then perform any ui updates on that main queue so this actually feels a little bit more like a real app now now that we're actually updating the ui but like i said this code is pretty awful uh we should be checking the response to make sure that the http request was actually successful so checking for a 200 status code we should be actually trying this in a do try and we should be not forcing so many things so i'm gonna update this code to make it a little bit [Music] better [Music] this is definitely more code now but it's more like what you're going to write in a real application it's more realistic so at the top i'm now making sure that this response is actually an http response once we get that we can actually check the status code and this is important of any http api that the code is the correct code we expect it to be and that's going to be in the 200 range then we want to make sure we unwrap the data appropriately and then we want to try and get the json data from that data object and only then do we want to try setting that quote on the labels text and this still works in the exact same way as before but we're doing those appropriate checks and in a real application you would definitely want to change these print statements to something more appropriate so you could notify the user that something went wrong or you could try and do something else present the user with a different quote or something but you wouldn't just rely on print statements so this isn't the nicest code to look at but it definitely does the job the only thing that really bugs me here is the code right here where we're taking that json object and we're converting it to this dictionary that's actually going to turn out to be an optional and it just it doesn't feel quite right i mean we're in swift and i'd much prefer this to be an object i don't want to have to access things using quotes quote i want to do dot syntax i want to access it as if it were a normal swift object and fortunately for us this is a really really easy thing to do in swift the first thing we have to do is create a struct or a class that contains the same properties as the json object so this object only has a single property it's called quote and its type is a string so my json object has a quote and its type is a string and then as long as this conforms to the codable protocol it will be able to be converted to and from a json object so now in my view controller instead of writing this code to just pass it into a dictionary [Music] i can write this code to actually get the data into that kanye struct so now i have this object that i can use in a much nicer way so i'll just go kanye dark quote and that's how i can get the data out and in this small example it makes this code a tiny bit nicer but imagine a very large application where you're dealing with tons of properties and nested properties within your json this comes in really really handy this is still working really nicely but before we do anything else i want to get all of this networking code out of the view controller in general it's a really good idea to try and keep your view controllers small try and keep any code that doesn't have to be in there out of there but networking code in general can really start to grow and get really really big so it's a really good idea to try and put this into its own place so i'm just going to take all of this code and i'm going to create a class that is just responsible for all of the networking code in this application so i'm just going to call that networker but it doesn't really matter what i call it and i'm going to make this a class and then i'll just make a function here that gets a quote so i'll just call it get quote and then i've dumped in the same code that i had in the view controller now the big issue here we're getting a warning about it is that obviously this new class has no idea about the label from my view controller so i can't just set the label's text here i don't want to set the labels texture i want to keep this more generic so instead this function needs to get that quote data out it needs to be able to share it with the view controller so the view controller can just call get quote and get that quote data but because this is all asynchronous and happening on a different queue i can't just return that value as i would if it were a normal function so the way to handle this the default way anyway is to use a completion closure or a callback function [Music] so now this function requires a closure to be passed in and it will pass either a kanye object or an error object depending on how the request goes and it won't return anything this also has to be marked with escaping so that swift knows that this closure may get called even after this function has returned which is generally the case with these asynchronous functions so with this signature here i can now where i'm setting the label i'm just going to replace this with completion pass in the kanye object and nil for the error so if everything goes well this should now work it will call that completion closure but we should also handle all of these [Music] errors [Music] so i've created this network error enum just as i would if i was handling errors synchronously using throw and try and catch but now i'm using it for this completion closure so when there's the bad response i'm going to pass that in as a parameter i'm not going to throw it and i always have to pass it into the second parameter and pass in nil for the first one then if there's a bad status code i'll pass in there or if there's bad data so now every single time there's an error or there's success i'm going to call this completion handler and i'm always going to make sure i'm calling it on the main cue so there's no confusion there so now from my view controller every time random quote gets called i'm going to create an instance of my networker and every time my random quote gets called i'm going to call get quote and then here is my completion handler so this will either give me the object i want or an error uh if there's an error we'll handle it i might just set the labels text to be error and if there's no error then we will set the labels text to be kanye dots quote and remember that because oh gotta do self remember because uh we are running all of these or running this block on the main queue here so i'm always calling dispatch queue dispatchqueue.main.async before calling completion that means that i don't need to explicitly run this on the main queue it's already on the main queue but if i didn't run those dispatch main async things within this function i would have to run those uh in the view controller so you have to choose whether you're actually going to run it in the view controller or in your network code uh just make sure that if you're updating the ui this is happening on the main queue so now the behavior should be the same there we go uh except my view controller is significantly smaller now this is really nice so all i have to do is say i want the quote i'll handle any errors i'll handle the success case and the rest of the networking code can just stay over here now let's talk a little bit more about this url session i'm currently using the shared url session which is a singleton that any object in my application can use to perform any networking tasks and this is fine to use this singleton uh for simple cases like demonstrations like this but in a real application you're never really going to use this shared instance of url session you're going to instead want to create your own instance of a url session so you do something like this where you create a url session but in order to create a url session you need a url session configuration object so you'll create a configuration object and most likely you'll start with the default configuration so then when you go to create your data task it would be something like this you just pass in your created session and create your data task this way and the benefit of doing things this way is that you get to completely customize the configuration of the url session so this object can be used to configure things like timeouts and caching and network policies and all things that you might actually want to customize in your application so we can create a configuration object using the default configuration and then customize it as needed most of the time the default is going to be okay but you can customize it as much as your application needs then we create the session from that configuration object and use that to create all these tasks and by creating it this way we have another option to actually provide a delegate to the url section so if we use this constructor to create the url session object we pass in the configuration but we can also pass in a delegate so let's say we wanted this object to be the delegate for the url session we could conform to the url session delegate and that would allow us to get notified about any data coming from the session and allow us to customize it a little bit further and that means that we wouldn't use the callback closure like we are here instead we do everything through delegates which can sometimes be desirable most of the time i don't use this i prefer to just use the callback closures and i already have enough control everything i need but it's another option when you're creating your sessions this way another thing to notice here is that every application will probably only ever need a single session for the entire application so instead of creating a brand new session every single time i want to get a quote it would be better to maybe do something like this [Music] now i'm creating a shared instance of the networker class so it's a singleton that contains that url session object and now every other object in my code can just access this shared instance of my networker and now they're all using the same session and configuration which is usually what you want in your applications we can use the configuration and the url session to configure options that will apply to every request we make using this url session but what about when we need to modify the options for a single url request for example what if i needed to change this to be a post request or i needed to add some sort of authorization token or modify any other header information we can do this using the url request object which is very similar to the url object we construct it using a url but but we can modify things for example the http method to turn it into something like maybe a post request of course this one needs to be a get request so i'll just leave it at that but then we can pass that url request to the sessions data task constructor instead of passing in a url and that just allows us to customize more things so we have the url request and the session objects but the really interesting things are happening with the tasks so the session is used to create a task in this case a data task but the task is the thing that's actually doing the url request and handling all of that data and the url session object can create different types of tasks depending on what we're trying to achieve so the data task here is used when we need to retrieve small amounts of data that will fit into memory so this is like getting json or xml but if we needed to say download a file like an image we would want to use the download task which downloads all of the data and stores it in a temporary file or if we wanted to do the opposite of that and upload a file we could use an upload task or if we're dealing with web sockets we can use a websocket task so the session creates the tasks and the tasks are the thing that actually do the requests so downloading a file like an image is actually a really common thing that you'll want to do in your application so i'm going to create another function here that will grab an image every time the function is called and then we can display that in a ui image view [Music] so now the app works in the same way but it's downloading this image from kanye west's wikipedia page and presenting that in an image view and if we take a look at the code in the networker here the download image function is almost identical to the get quote function it has a url it has a session and a task and it has all of those checks to make sure that everything went well but instead of using a data task it uses a download task and when we use a download task we end up with a local url as opposed to a data task where we get data so data is in-memory data and this is a local url to a file in the temporary directory where the image now exists so this isn't going to be persisted it will go away at some point so we have to do something with that data but it currently exists in a temporary location on the device so we can then use that to load the data into memory so we can actually put it in an image view so right here instead of uh instead of using the json data we're actually just taking that local url bringing it into in-memory data and then passing that over back using the completion handler which in the view controller we're then using to set the image on the image view and i've just hooked up a new image view and i should actually make this the shared instance and yeah now running the app as soon as it starts up it does that http request takes a second and shows that image right there we've gone over the basics of http requests to get kanye west json data and images into an ios app so try some of these techniques out in your own applications and stay tuned for more videos on ios development [Music] you
Info
Channel: Sam Meech-Ward
Views: 2,904
Rating: undefined out of 5
Keywords: swift, urlsession, data task, download task, codable, decodable, encodable, image, data, urlsession tutorial, ios networking, urlsession ios, urlsession tutorial swift, ios networking with swift, ios networking tutorial, ios networking interview questions, ios networking urlsession, urlsession ios swift, urlsession swift 5, swift json
Id: zvfViYmETuc
Channel Id: undefined
Length: 22min 45sec (1365 seconds)
Published: Sun Oct 25 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.