SwiftUI MVVM Async/Await Networking Example(SwiftUI 3.0, Xcode 13, Error Handling, SwiftUI Tutorial)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

Great! Thanks for the video

👍︎︎ 3 👤︎︎ u/Whabadah 📅︎︎ Sep 26 2021 🗫︎ replies

Yay, another MVVM propaganda post.

👍︎︎ 2 👤︎︎ u/Fluffy_Risk9955 📅︎︎ Sep 27 2021 🗫︎ replies
Captions
hey and welcome back to another video so in today's video what we're actually going to do is we're actually going to look at how we can use asynchrony and mvpn in our applications so obviously xcode13 has come out and we can now change the way that we interact with apis so for this tutorial i'm going to look at how we can use the rick and mort api to basically interact and create a list of characters and see the improvements that async await gives us compared to using closures for you know interacting with asynchronous code also as well i've recently did a video before where i talked about my mvbm um async await project structure if you want to see that i'll leave a link so let's jump into this video and let's get started all right cool so the first thing i actually want to look at is the model for the data that we are going to be using but like i said this is going to be a simple example just to show you async await and you know how we can use it so in the rick and morty api if you go on the rickamoreapi.com website if we go to docs and if you scroll down we're just going to basically show a list of characters so in our documentation here there is a url we can hit here so we're going to use this one so i'm just going to copy this and i'm going to actually put this in our api online testing tool so request bin and as you can see i've actually already done it so we hit send you'll see the response that we get back so in our list we're just going to show the character's name so we actually need the name property but we need to actually model our data to match this response so in our xcode project let's create a brand new folder called model and inside a model will write a code to actually you know reflect our json response cool so as you can see we've got our model here so it's we've got our result and it holds an array of characters so like i said before for each character we're going to get the um name from the um response and also we're going to use the iv so the reason why i'm using the id is because that's what we're going to use as our unique identifier for each character within the list that we're going to use later so if you want to actually improve on top of this all you have to do is go to the json and if you just look at the result here as you can see i'm using a name you can add whatever you want so if you want to add status you just need to add it and then species and then sort out the data type that it needs all right so the next thing that i'm actually going to do is create a service class so the purpose of this service class is to simply interact with the service so make the actual api call so in this service class this is where we're going to be using the new async await um functionality to interact with an api and i'll also discuss the benefits of using this over a closure so let's do that now so in our folder let's create a new group called service and then i'm going to create a new file called character service and then do a bit of typing cool so we defined our character service and our function to fetch our characters now you may be looking at this i'm wondering why have i done the character service destruct as opposed to a class so the reason why i did this is because with this stroke you don't need to observe any like changes to it like you would with a observable object if you were you know binding a view to some kind of view model this service's only purpose is to literally execute an api request i want to execute the api request it will just give us a result so in order to make this a lightweight class and we might as well just mark it as a struct so in our function here effects characters so this function is what we're going to use to interact with the service and use the async awake um functionality within it so normally what you would do is something like this right cool so i finished typing that out and obviously i sped up for you but for me that was a lot to type out so let's break this down so what i've done is i've now introduced a error you know so we can actually you know throw errors whenever something goes wrong also as well on top of that you'll see that i've added a completion handle so the completion handler uses the result so we can have a success when we get a successful um you know response and things go well and we have an error when things go wrong and we're using url session to call our api and then within here we're just doing a couple of checks and decoding our data depending on the status of what's going on we'll throw an error over pro success now before async await and this was valid and you know most people would structure their code like this but now that we have async await there's a lot of problems and things that helps us you know resolve so the first thing that helps us resolve is this so we've got this completion handler here right so what is the problem with using this completion handler well what about if i actually have another function that i also need to chain after this if i want to use these two functions in conjunction i will get into something called call back hell so just to show you what that looks like okay cool so in this example callback hell function i've got two closures nested with each other so if we have like two three four functions we need to chain one after the yoga this can get pretty pretty nasty and not very nice to look at so that's the first thing that async await helps us resolve also as well if we just go back to our function you'll notice something that it's a lot of code so just to call an api and execute a request it's a lot of code to type so async await also helps us reduce the amount of code that we have to write as well and then finally the last thing is i don't know if you realize but it's actually a possible issue with the code that i've written so as you can see in the response we're checking for certain things and depending if something goes wrong then we call a failure but in this case here we've actually forgot to call bailly or if something goes wrong so this here could be a problem because what about if we actually do get in a scenario where our response object is nil if our response object is nil then the user when i use an r nothing's gonna happen because we don't actually call the completion handler on all the paths so async await allows us to actually throw an error so we actually can't get into situations like this where we actually mispass and forget to you know throw something so what we're going to do now is well unfortunately i'm going to delete all this and re-type out all over again but luckily async awaits a lot nicer when you want to type this type of stuff out so what we're going to do is we're actually going to remove our completion and removing our completion the next thing we're going to do is actually just delete all this code so what i'm going to do is i'm actually going to type out the exact same code i just written then but this time using async await syntax and i'll break down certain parts and explain what they mean so let's do that now so the first thing that we need to do in order to use asynchrony is we actually need to mark our functions with the async keyword so by doing so this will actually allow us to tell the compiler that this function is an asynchronous function so in order to do that after your signature all you need to do is actually type the word async like so and now it knows that it's an asynchronous function so you may get an error here and the reason why that is because async await is actually only available in ios 15 and above so if you're someone who's actually still supporting ios 14 then unfortunately you're not going to be able to use this capability but if your company or you're an individual or whatever and you're all using ios 15 then go ahead so in order to fix this error there's two ways of doing it so what you could do is if you need if you don't need to support ios 14 then simply just change the ios deployment target here to ios 15 like so but if you do need to support ios 14 then what you're going to need to do is where it shows you the error of you just hit on that and you hit fix it will add a available property wrapper so that this function is actually only available in ios 15. now for the purpose of this tutorial um i'd only support ios 14 so i'm just going to set it to ios 15 as a default and then what we need to do now is we need to actually handle our code in terms of how it throws errors so with asynchronous functions they can actually also throw so by that what do i mean so if something goes wrong you can actually throw an error and actually capture it in a do catch clause so in order to ma in order to mark this function as froze all we need to do is after the asynchronous keyword is just define rows like so so now we've told a compiler that our asynchronous function our function is asynchronous and it can also throw an error now the final thing that we need to do is actually give our function some type of return type so because we're not using closures anymore and we're using this all you need to do is specify the type that you want your function to return so in our case we want our function to return an array of characters so let's do that now cool so now we've got the base implementation for our function and it's looking all good so you'll get an error because obviously the function is expecting you to return something so just to remove this error for now i'm just gonna return an empty array so now let's look at how we can use the url session framework to use the new async await capabilities so let's do that now okay cool so we've got our url here and we're just defining the url i want to hit with our api so now we're actually using the url session and it has a new function that has asynchronous capabilities so this data function now rather than using a closure we're now going to use the asynchronous alternative and apple if you watched wwdc actually you know announced that there's actually a lot of functions that they've updated in frameworks to give us this new capability and this is one of them so you might be wondering why have i added this away keyword before here well the reason why we need to use this await keyword is because we're saying at this point that we want to suspend this execution so that we can await the value from the service so why do we need to suspend this well if you think about it when you're executing a network request you don't really know what time or how long it would take for it to come back so what we're saying here if you want to suspend and await the result back from the service before we actually carry on with any of our code so we don't actually want to carry on with the next steps that we're going to type until this comes back so as you can see this is a lot cleaner and nicer than us having a callback because we're just saying that on this line suspend the function and then resume it when you're ready to execute it and give us about that result and then continue so we also mark this with try so using the try keyword allows us to capture any errors so it's possible for url session to throw an error and by using try it allows us to try to execute this and if it does throw any kind of errors then this function fetch characters will throw those errors as well so after we do that we get two things so we get the response and we also get data so data is the data that we'll go use to try and decode the result that we get back from the service and response is the response that we get back from the you know network request so as you can see we've got that here in a tuple so the next thing that we're going to do is we're going to see how we can actually throw our own error from here after our function to request our date of resume so let's do that now okay cool and as you can see we're just casting our response checking the status quo is 200 and if it isn't then we're just simply saying here that we want to throw an arrow now this is a lot safer than what we had before where we had our closure because now we're actually saying here that like if this fails at this point then throw it so we're not actually able to actually miss any of the possible cases because a lot of this code is actually procedural procedural one after the other so the next thing that we need to do is actually decode our data and if it successfully decodes it then we're going to return our characters and if it doesn't then again we'll throw another error so let's do that now so what we've done here is we've got our json decoder we tried to decode it from the result that we get back from our api so from the data we mark our decoder we try so if at any point this decoder fails this will actually automatically throw an error for us to capture and handle from there and if everything's okay then what we'll do is we'll return the results which is the array of characters that we get back from the service so now that we've written everything that we need for our character service if you actually compare this to what we wrote before you can see there's a massive difference because number one it's easier to read and number two as well we can make sure that we capture all errors that are thrown as well so the next thing that we need to do is actually write our view model which will actually use this service to interact with the rick and morty api and we can actually handle getting the results back and using this within our switch to our view so first of all what we're going to do is we're going to create a new folder here called viewmodel and then within this folder we're going to create a new class this time we're going to use a stroke we'll use a class called characterviewmodel this time with our view model we've actually used a class rather than a struct so again why don't using cluster not stroke this time so with this character view model this is where we're actually going to so with our character b model this is actually where we're going to bind our array of characters to our view so in order to actually do that we need to make sure that our class is an observable object so that our swift ui view can check for these updates and if there is an update to a data property it can re-invalidate itself and render those new changes so we've got a function here called get characters which is what we're going to use from our servers to actually execute and get our characters like it says so what we need to actually do now is actually inject our service into our view model so we can actually use that function so we're going to use dependency injection to do this so let's write that out now okay cool so now that we've written this out so now we can actually use our function that we defined before which is its only purpose was to interact with the service within this class full so the next thing that we need to do now is we need to actually hold a publish property which is going to trigger some events to our content view to tell us what the current state is of our view model so let's type this out now so we've got our e m here state and it has four possible cases so n a this means not available loading means that the v model is currently loading we have success which tells us that everything was successful and here's the date of that you want so we're using an associated value with our enum to pass that data through and we also have failed which again is using an associated value to push any errors through so we can actually handle that as well so what we're going to do is we're actually going to have our published property here so this published property that we have is marked as private set and the reason why i've made this a private set is because i want the view that you have the view that has access to this view model to be able to access this state properly but not be able to actually change it so the contents of this variable can only be changed within the scope of this class and then by default we're actually going to set the state to be n a not available because nothing's happened so it isn't available so what we need to do now is actually write out some code within our get characters function to actually trigger our service and depending on the result then we're going to change our state so the first thing that we need to do is actually mark this function as a synchronous because we want this to also be an asynchronous function okay cool and then within the get characters function i'm just going to do a bit of typing and then we'll break it down so let's go and break this down so we've got our function here get characters which we obviously monitors and asynchronous we've also got our first change of state here so we're saying here that when we call this function the first change should be that it's loading which you know it's just started so it is loading so then after this what we're going to do is we're going to await our call to fetch our characters and if this doesn't fail so this function doesn't throw any errors then we will get it and put it inside of our characters and constant here so if this is all good then what we'll do is we'll then change our state again to success and we'll pass our characters be or our enum now in the event that fetch characters fails what we'll also do is we'll actually catch any errors and we'll basically say hey that we want the state to be failed and we'll also pass through that error so we can display it on the screen so as you can see whenever you use a function that's asynchronous you need to also make sure that you mark it with the awake keyword now it's also worth noting that not every single asynchronous function needs to throw as you can see here with our get characters function we're just basically saying this get characters function is just an asynchronous function and that is it so when you're doing your functions try to think if it needs an error so if it if it's possible that it might cause an error and you need to handle the error then you want to mark it as froze but if you don't need to handle any errors then you can just mark it as asynchronous so the next thing that we need to do is actually talk about actors so because we're actually making changes to our ui within our view model we actually need to make sure that these changes that we make are actually on the main thread now right now we've got a possible problem because we're not actually ensuring that our changes to our state are made on the main thread so that when our view updates it happens on the main thread so in order to do that what we need to do is actually mark our class here using an annotation called main actor so by using the main actor annotation what we're saying here is that we want to publish any kind of ui updates back onto the main thread the next thing that we need to do is we actually need to use this view model within our content view so let's go into our content view and at the top here inside of our content view i'm going to create an instance of our view model and inject our service into it as well so let's do that now so now that we've got our state object here we actually need to call our function to interact with the you know service so what i'm going to do is i'm actually going to type out a navigation view so i'm just going to add a navigation view in here so how do we actually call the function on our asynchronous function now you may be wondering and thinking that actually you know what you want to do something like this where you do unappear and then you call the viewmodel and then say get characters but if you actually do this you'll notice that xcode actually gives you an error and says that an asynchronous call and a function does not support concurrency so what does that mean so it's saying here is that the unappear modifier doesn't actually support concurrency in terms of the fact that you can't call an asynchronous function within this block so what we actually have now in swift ui 3 is a new modifier to handle asynchronous functions we want to execute them when views appear and it's called task so rather than using the unappear what we want to do is we want to use task and now we're saying here is we're using our task modifier to execute our asynchronous function so this task is worth noting that is bound to the life cycle of this view so whenever the view appears this modifier will be executed so now what we need to do is we need to mark our asynchronous function with the await keyword to fix this error as well so now we've got this working so when our navigation view comes onto the screen what will happen is it'll execute the get characters and the tasks will be executed and likewise as well if this view was to go off the screen and just disappear or get destroyed the task will actually be cancelled for us automatically as well so now what we need to do is actually use our viewmodels date within our navigation view to show a list of names in our and also as well when it's loading we want to show some kind of progress view so let's do that now okay cool so let's break this down so we've got our viewmodel state here and what we're saying is that if there's a success what we're going to do is we're going to get the data that we got back from our success and we're going to loop through it using a for each and use the id of the character so remember in the model do we have the id so we just go to the model again we're using this id as the unique identifier for it within our for each and then we're simply going to show the person's name on the screen in a text object also as well we're going to add a navigation title bar to the screen called characters likewise if it's loading then we're going to show a progress view or if none of these cases match if we don't match any of the other cases such as the n a or the failed one we're just going to show an empty view so [Music] what we're going to do now is we're actually going to resume and actually run this and see what happens really quick but as you can see now we've actually got a list of all our characters coming back from our api as you can see so in order to actually see our progress view because this um sometimes it's with previews like really quick you can't really see it so what i'm going to do is i'm actually just going to run this on the simulator so let's do that now and as you can see there you can see briefly when it's um trying to get the data from the api you can see a progress spin and then it shows our character screen here so what we need to do now is we actually need to handle errors so how do we handle when something's gone wrong i.e the internet's completely you know gone and there's no network connection well in our character view model want to actually have another published property within our view model called has error which will actually handle whether we show a alert dialogue with our error message and we can actually retry our network request so let's do that now so inside of here let's create a new property called has error so we're going to set our has error to false whenever we call the function because there isn't an error and if there is an error then we're actually going to set that to true so let's do that now what we're saying is that when we start our characters function we're going to set the state to loading and that there's no errors and then depending on whether it's successful then we won't you know has ever set to fall so that's fine but if there is an error the negotiator set that to true so what we need to do now is we actually to use this has error property within our view to show some kind of alert that something's gone wrong so let's do that now but what we need to do before i explain is we actually need to make a change so if i actually go into my view model we need to mark and remove the private set and then let's build this now and see what happens okay cool so now we're using the alert modifier so what's in here we're using the alert modifier and we're using the is presented property here so it's presented checking for true or false and that's checking it through our boolean here and presenting is passing through our viewmodels date so we're using the case that we get failed to actually grab the error message that we want which we'll get to in a second so inside of our alert we've got a button here called retry and within retry we create a task so the reason why we have to create a task is because remember like i said before if you actually want to run a asynchronous function it needs to be executed within a concurrent context so that concurrent context is this task so if you want to execute a concurrent function you have to make sure that it's executed within some kind of task and we mark it with a weight to get characters and then within our alerts message we get our detail so we get our state here so detail and then we check to see the detail of that so if it matches our failed case that we're just going to show the error message within the text for our alert so what we're going to do now is we're actually going to simulate this so in order to simulate this what you're going to do is actually well the quickest way to do it to turn off your internet and then i'm going to run this on the simulator oh so now we've run that and you might realize that actually wait a second my wi-fi is turned off but i'm still actually able to see a list of them characters so what's going on here well what's actually going on here is that the url session that we're using we use a shared instance with a shared configuration so by default that url session actually has caching enabled so in order to actually see the work that we've done with the error what we need to do is we need to go into our service class and actually disable caching by passing in our own configuration so let's go into our character service and then i'm going to do a bit of typing and i'll break it down so what we're saying here is you want to use this configuration called and say this thermal thermal and then what we're saying here is that this will actually not assist any kind of data on disk so all this data that we request using the url session will actually not be saved on the disk so we'll only use any data that's available in memory and then we pass this configuration into our url session so what we're going to do now is we're actually going to run this again and this time we're going to see what happens so let's run it and as you can see because we've now turned off the caching capabilities with url session we now get to see our actual error error dialog and it tells us what our error is so now what we're going to do is we're actually going to turn our wi-fi back on and now that we're connected to the internet again we're going to hit retry and as you can see we now get our list of characters visible on the screen now a word of warning what i've done here was just literally to test it out so i could actually show you how the um arrow handling um works and but normally when you're working with url session you do want to use the shared instance and you do want to use the caching because so when you're hitting a network a lot of times if there's a cash available which has some data that's still relevant then it makes sense to use that rather than making another request to a service for performance so looking at our list here we've pretty much done it and we got all our responses back from the api and so we've actually covered everything that we need to cover in today's video so that's everything from me if you have any feedback i'd love to hear it in the comment section down below also as well if you haven't already i'd really appreciate if you gave this video a thumbs up i'd really appreciate that and as well if you haven't already then hit the subscription button so subscribe to the channel and hit the notification bell so you can get updates whenever i release a brand new video that's everything from me i'll catch on a bit deuces you
Info
Channel: tundsdev
Views: 2,579
Rating: undefined out of 5
Keywords: Swiftui tutorial, Swiftui basics, Swift ui tutorial, Swiftui async await, Swiftui async task, Swiftui 3.0, Swiftui 3.0 tutorial, Swiftui 3.0 mvvm example, Swiftui 3.0 mvvm, Swift concurrency, Xcode 13, Swift tutorial 2021, swiftui mvvm project structure, swiftui async/await project structure, swiftui async await project structure, tundsdev, swiftui api example, Swiftui api json, Swiftui api call, Swiftui api call example, Swiftui api call tutorial
Id: 53lbqYYAPLw
Channel Id: undefined
Length: 29min 7sec (1747 seconds)
Published: Sun Sep 26 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.