How to use @ObservableObject and @StateObject in SwiftUI | Bootcamp #50

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] welcome back everyone i'm nick and uh we are almost done with the swift ui boot camp so i hope you guys have been enjoying it but unfortunately i left a couple of the harder videos for the end of the bootcamp here and these videos are although they're a little difficult i think they are crucially important for actually building full apps and we're going to use them every time we build an actual application so they are very important to be in the boot camp and the first of these difficult topics is this one which we're going to talk about observable object and state object and these sound a lot harder than they actually are but they are basically just property wrappers that we can use to observe other classes in our app and have them update our view in real time so just like we use an at state variable for all the variables within our view we can use an at state object to reference another class if something is happening in that class and have our view update so they look a little ugly they look a little daunting but they're actually super easy to understand and i'm going to take the time in this video to really go through what's going on so you can understand why we use these property wrappers hope you enjoy it let's take a look what's up everyone i am back in xcode this is going to be one of possibly the harder videos of the course so if you just did a whole bunch of videos it might be time to take a break and come back or better than that actually just grab a coffee and come on back but let's get into this video let's start by creating a new file in our project let's right click the navigator create a new file it will be a swift ui view and we're going to call this view model bootcamp and we're calling it view model because as we get into mvvm architecture and you're going to start hearing this mvvm a lot as you build apps the view model is basically the class where all of the data behind the scenes for your app is going to be located and we're going to be creating a view model in this video so we're going to call it view model boot camp let's go ahead and click create once you're inside click resume on that canvas and let's get coding so we do have some setup for this video that we need to get through first and i'm going to start by creating a navigation view here opening the brackets inside the navigation view we're going to add a list we're going to open the brackets again and in this list i want to loop on a bunch of items in the last video we learned how to make custom models custom data types and we're going to do that now so if you did not watch the last video if you do not know how to make a custom data type go watch that one and then come on back but we're going to make a list of fruits in this app right now so let's create a model for our fruit so we're going to create a struct call it fruit model open the brackets and each fruit is going to have a name so let's say let name of type string and each fruit is also going to have a count it's going to be how many of those fruits that we want to buy at the store so maybe we're going to buy five apples or four apples we can use the count so let's do let count of type int we have our custom data type fruit model here and we want to make it conform to identifiable this way we can use it easily in a for each loop we covered that again in the last video so to make it conform to identifiable we need to add an id so we'll say let id of type and i like to use strings and let's set it equal to a uuid open and close parentheses dot uuid string so this uuid function creates a random user id and then we're just converting it into a string so that it is a string nothing new here let's loop on these fruits so let's do four each open the parentheses we're gonna use the first one with the data and content we of course need a data array forgot to do that so at the top here let's do at state var fruit array of type and it's going to be an array of fruit model and let's just set it equal to a blank array for now so we're not going to actually add any fruits right here and we're going to loop on this fruit array so for each the data is going to be fruit array in the content we'll press enter get rid of this ugliness and just type in fruit so we're looping on each fruit in the fruit array which is currently empty and what do we want to put here for each fruit so very simply let's just add an h stack and on the left we're going to have the title so let's do a text with fruit dot name let's actually create let's actually add one fruit in here just for now so we can see what we're typing so let's add a fruit model here let's call it apples and let's do five so i will click resume on the canvas just so we can build and we can see what this apple is gonna look like apples and before the apples let's just add the count of how many apples we want here so let's do another text and we'll do make it a string forward slash open and close parentheses and we're going to add the fruit dot count in here so we're converting that integer into a string so it says five apples let's make the name with a dot font of headline and maybe bold this count let's just give this a color so let's do dot foreground color of maybe red doesn't really matter for now so we have five apples here this is nothing special let's wrap up this view let's add a navigation title here let's call this fruit list and let's add a style to the list so let's quickly just add dot list style and i'm going to start typing in list style and i'm going to go this time with maybe the grouped list style just to change it up here so we have our fruit list with five apples nothing new here and when this list appears on the screen i want to add a bunch of fruits to this fruit array so underneath this navigation title i'm going to add on appear this again should be nothing new to you because we've covered this all in this course and when we appear i want to call a function to fetch a bunch of fruits from our database and then append them to our fruit array so underneath the body here i'm going to create a function i can just it's going to be called get fruits open close parentheses open the brackets on up here we're going to call get fruits and all we're going to do is append a bunch of fruits to our fruit array so let's say let fruit one equals let's fruit model and we're going to create some fake fruits here let's do orange count one let fruit two equals fruit model open parentheses let's do banana the count here i'll do two doesn't really matter let fruit three equals fruit model open the parentheses and this time let's just do another fruit let's do watermelon with uh maybe 88 we're gonna get 88 watermelons here and then we're just append these three fruits into our data array so let's just do fruit array dot append and we'll just add fruit one i'm gonna copy this paste it two more times and then just do fruit two fruit three so on a pier we call get fruits we create three fake fruits append three fake fruits to our data array i'm going to click resume on the canvas quick and when it appears we get all four fruits here let's actually just cut this apple out of here so we start with a blank array and we just have our three fruits here orange banana and watermelon and now we are in a list and i did a whole video on lists earlier in this course so you should be comfortable with what a list can do and we're going to use some of its really cool features now such as the swipe to delete so on this for each i'm going to add dot on delete and we can perform an action on delete and we're going to create a function to delete from this index set so at the bottom here let's create a new function func delete fruit open and close parentheses open the brackets and when we go to delete fruit we need to know exactly what the index is that we're swiping on so i'm going to create a parameter called index and this will be of type index set and this index set is coming from this index set here and all we're going to do is call fruit array dot remove at and then there is a completion at offsets which expects an index set so that's exactly what we have and we're just going to add here and then we're going to pass in our index and now we have our delete function to remove at this index let's put this in our code and as we've done before because this is an action and it's looking to pass in an index set and that's exactly what this delete fruit is i can just delete from the start of this bracket to the end of this bracket here and in this perform we're just going to call delete fruit with an index and i think we can actually even delete this index here and just call delete fruit and it will know exactly what's going on so let's test it out quick we have our function to get fruits we have our function to delete fruit and let's click resume [Music] press play on a pier we called get fruits now we have our orange our banana and our watermelon and now i can swipe to delete and if i click this delete button it should actually delete and it's gone forever so this is perfect this is all working this should not be anything new if you have been confused about anything in this video so far i highly recommend looking at the earlier videos in this course because i did a whole video on lists a whole video on uh models i did a whole video on on a pier and all of this should not be new and now what we're going to do is basically instead of having this fruit array directly in the view and instead of having all of these functions directly in the view we're going to create a custom class to host all of this extra data and the reason we do this is because when we get into complex applications it is better and smarter to start to separate all the logic in our app because although this is a very short screen and this works it is recommended to separate the code and the logic for the different functions in your app so for example in this body here this is all this is all code that's related to the actual ui of our app this is the actual code for the view and that's why we should keep this in the view but all this other code here this get fruits this delete fruit this has nothing to do with the view directly this is only to do with our data array so if this function had a lot of other information and it was downloading from our database that has nothing to do with the view it only has to do with getting data and storing that data so we basically are going to separate the data functions from the actual view from the ui functions so it sounds a lot harder than it is we're going to very simply create a class and we're going to call this a fruit view model and then we're going to open the brackets and in this fruit view model we are going to then put our fruit array so i'm going to say var fruit array of type array of fruit model i'm going to set this equal to a blank array so same thing that we have right here but you'll notice that i did not include the at state and that's because we can only use at state when we're in a struct when we're in a view and we are now in a class so this class has nothing to do with the actual view it just has to do with the fruit data here so what we can do when we're in a class is use a new property wrapper called at published and the publish property wrapper uh there's a lot of really cool crazy things that we can do with a published property wrapper but for now and our purposes all you need to really think about is that the at published is the same thing as the at state except it's within a class so when this fruit array gets changed it notifies this fruitview model that hey something changed you might have to update something because it's going to publish the new changes so just like a state alerts the view that hey this fruit array changed this published will alert the class itself the fruitview model so now what we need to do is get rid of this fruit right here and reference this fruitviewmodel class so i'm going to comment this out and we're going to add a new variable here we'll say var fruit view model and i kept this f lower case here but it's uppercase here so fruitviewmodel it's going to be of type fruitviewmodel and we're going to set this equal to a new fruitviewmodel and we're just going to open and close the parentheses here we're immediately going to get an error that this fruit array it can't find this fruit array so instead of accessing this fruit array directly we're going to access the fruit view model and then the fruit array that's inside it so here we'll add fruit view model dot fruit array now our error goes away we're also then going to have this issue down here at the bottom because it cannot find this fruit array in our get fruits and our delete fruits so i'm going to cut these both of these functions notice how we're removing all this excess logic from our view and i'm going to put this into our fruit view model here so i'm going to paste it up here in the fruit view model and now when i go down to our app here this on delete we can't reference this delete fruit directly but we can access it through the view model so we'll call fruitviewmodel.deletefruit and again we don't need that index here and again in the on appear we now need to call [Music] fruitviewmodel.getfruits so this logic is a little longer but in total this view is now much cleaner and much shorter because we don't have all that excess data all that excess logic that is in regard to downloading and updating the fruit view model so the logic that's within this view and i'm even going to delete this now actually so all the logic that's in this view here just has to do with updating the view it's just the ui components all of the logic that has to do with the actual data so if we went and fetched from the data source if we updated the data source if we deleted the data that's all in this fruitview model and this might not seem that important when we're creating these very simple apps but as you make complex apps this is going to be highly important because all this logic for downloading from your database is going to be complex and when you want to go in and change something for downloading something from your database you don't want to spend time going through this view trying to figure out what part is for the data what part is for the view what part is for something else you want to instead just go straight to the fruitview model and then all of your logic for that downloading those fruits is going to be right in this class now we're almost done here but if we click and resume you'll notice that this actually does not work so why is this not working well that's because this right now this fruitview model we're creating it but we're not telling this view that it's going to be updating so just like we create regular variables here when we add at state we need to add a property wrapper to this view model to tell the view that hey this might be changing and if it's changing we need to update our view so what we're going to use is a new property wrapper called at observed object this basically just tells the view to observe this object because if this object changes we need to update our view so it's kind of doing the same thing as at state except this observed object is a whole object this is a whole class here right this is a whole class whereas at state is usually just a single variable and we're getting this error here finally because observed object requires that fruitview model conforms to observable object that sounds so complicated but it's the easiest fix all we need to do is go to our view model and make it conform to observable object so we'll add a colon here and we will add observable object this basically just lets xcode know that this class is now observable and there might be views in our app that are observing what is happening within this view model so let's click resume again on the canvas and see that it's actually working we can still swipe to delete these items and just to recap what we've done so far before we move to the rest of this video because there is still a lot i want to cover here but basically we have our regular view and we want to take all of the data logic outside of the view so all the logic that's in the view just has to do with the ui and all the logic that has to do with downloading updating our data is now in our for review model and to do that we created a custom class the class is called fruitviewmodel and every time we create a variable inside this class that's going to be observed by the view we add a published property wrapper on it because this just means that it's going to push every time this changes it's going to push and update to the entire object and when this object gets updated we know it is observable and we are observing it down here so we're using observed object on the view model before we move forward let's add one more published variable up here so let's do at published var is loading of type bull equals false and when we get fruits i'm just going to simulate like we're going to a database and add a delay before we actually append these fruits so let's add a dispatch dispatchq dot main dot async after and we're gonna async after now plus three seconds and then hit enter on the execute and i'm just gonna append this inside this closure so before we append it let's put is loading equals true and after we append these let's do is loading equals false again i've done previous videos in this course with our dispatch queue i'm getting this quick error message here because we're inside this closure and because we're in this closure we need to just explicitly tell tell xcode that this fruit array is in this class here so all we need to do is call self self dot fruit array dot append and i'll just paste that here and same thing for is loading just to reference self which is the current view model that we're inside not a big deal there so now we have is loading so now we have our is loading booleans set up and it is a published variable so we can watch that in our view directly so let's incorporate that into our list here so in our list if it's loading i just want to add a loading indicator and if it's not loading we'll have this for each loop so i will add if fruitviewmodel dot is loading open brackets so if it's loading let's add a progress view and then we'll say else so if it's not loading we will cut this for each and the on delete and then paste it inside all right let's click resume on the canvas one more time to see this in action so it's loading three seconds we have our loading indicator and then after three seconds we append and we get our fruits so it's that easy to add another published variable up here to our fruitview model and now i know this video is getting long i know it's getting confusing but there's a bunch more that i want to cover here that is very very important so please hang with me here let's talk about this observed object so this observed object is great and it makes this view model observable so that we can access it when it's being updated on our view here but the unfortunate downside of this observed object is that if this view gets recreated so if it gets refreshed for whatever reason maybe there's some animation maybe there's something else going on in your app that just causes this view to reload well unfortunately this observed object would also reload and that's just how the observed object is made and usually in your app when you are downloading like a data set so like users or whatever data is on this view you don't really need it to reload and that's because even though the view is reloading right all the data the underlying data is not really changing so you kind of want the data to persist even if the view reloads and so there is another property wrapper we can use that is called at state object and a state object is the same thing as an observable object except basically if this view reloads if it re-renders this object will persist so it will not refresh and this is better for most cases when the view model is holding all of our data because that underlying data is not really changing when the view needs to be updated so if we click resume the exact same thing is going to happen let's click play we have three seconds of loading we load our information except now if for any weird reason this view gets reloaded this view has to refresh this state object will not refresh it will persist which is exactly what we want so my quick rule of thumb for everyone from beginner developers to expert developers is when you are creating these classes with this observable object type if it's the first place you're creating it in your app use state object but if you're passing it into a second view or a sub view use observable object so let's put a comment for that we'll do at state object and let's say use this on creation or init whatever you want to call it so the first time i'm using this fruit view model in my app i would use state object but if i'm going to pass it into another view if i pass this to the second view i'll use observed object in the second view so let's do another comment we'll do at observed object let's say use this for sub views so let's just do an example of this so that this is not too confusing so so let's create a second view to this screen here and i'm going to scroll down to the bottom uh maybe underneath this view but before the preview i'm going to create another struct this will be a second screen of type view open the parentheses and again i've done other videos on creating sub views and sub screens so this should not be new either but to conform to view we need to add a body open the brackets and this is exactly what it looks like when we start our new ui view and in here let's just add a z-stack on the background of the z-stack let's add color dot green dot ignore safe area on top of the z stack let's just add a button open the parentheses let's give it a action and label for the label this will say uh go back let's make the foreground color.white let's make the font.large title and let's make the font weight of semi-bold i'm getting a quick error here invalid redeclaration of second screen it's because i've used this second screen in elsewhere and other videos in this course so i'm just going to rename this let's just call this a random screen and i want to see what this looks like so in our preview temporarily let's comment out the viewmodel bootcamp let's add in the random screen and click resume let's see what it looks like i think that looks pretty good when we click this i want to actually go back so we're going to add that presentation mode which we've done before so we'll do at environment open the parentheses keypad backslash dot presentation mode var and we'll call this presentation mode when we click the button we're going to call presentation presentationmode.wrappedvalue.dismiss i've done a whole video on this i hope this is not confusing this is just going to go back in our navigation view so this screen is all done let's put that viewmodel bootcamp back let's delete the random screen from our preview and click resume let's add a button to our navigation bar so we can go to that second screen let's dot navigation bar items let's do trailing i'm going to hit enter in here and add just a very simple let's add an image with a system name of maybe arrow.right let's give this a font of title make it a little bigger and let's make this a navigation link so that we can push in this navigation view so let's add navigation link open the parentheses we're going to use the destination and label destination of course will be our random screen and the label let's cut this image and font and paste it into the label here now let's click to that second screen we have our data loading here let's click that second screen and now let's press the back button and you'll notice here that it went to start loading again and then it actually appended three more items to our data and if you're wondering why i did that it's because we have this on appear function and on appear gets called every time this fruitless screen appears on the screen so when it started it appeared when we went forward and we came back to it it appeared again so this is getting called multiple times and this is obviously not what we want so instead of calling this on appear and i'm going to delete this on appear function entirely and then up in our fruitview model we're going to create a custom initializer so when we init this when we create this observable object let's just call getfruits on the first time it is created so we'll call init and i've done a whole video on a knits as well so this should not be new and in this initializer we'll just call get fruits so when are we initializing this through view model well we're initializing it right here in our view when we call fruitview model and we open and close the parentheses this is our initializer so as soon as we have this in our code it's going to run this init function let's check it out let's click resume we're loading we have our data click to the next screen i guess we didn't need this go back function because we have this fruits list here already so let's just click the back button and you notice how it did not reload this time and it did not reload because we got rid of that on up here so this fruitview model loaded when it was first created and it did not have to reload when we went back so this is better this is exactly what we want and now the whole reason i added this second screen is because what if we want to access this fruitview model from this screen what if we want to access it on this screen well on our second screen here on our random screen we can just add a variable here that is at observed object and remember remember what i said use observed object when we're passing it in to a second screen so that's what we're doing here observed object var and we're going to call this fruit view model this will be of type fruit view model and then we're just going to leave it blank we're not going to add an initial value here so that when we create a random screen in our init it's going to ask us what is the fruitview model so back up in our code when we're calling random screen it's now asking us to give it a fruit view model here so i'm going to pass in our fruit view model to this screen so fruit view model here so we're creating it with a state object we're going to pass it into our second screen and our second screen is a observed object and now we have this all this data of this whole class in our second screen so we actually don't need this button i'm just going to delete this button and in this second screen let's just add maybe a v stack and in here just to prove that we have this data let's add a for each loop here open the parenthesis the data is going to be fruitview model dot fruit array the content will hit enter and it will be for each fruit and in here we'll just add some real quick text with the fruit dot name let's make this that foreground color of white let's give it a font of headline and let's just prove that this is all working let's click resume on the canvas we're loading our data orange banana watermelon now when we click this we are passing that into the random screen we are passing in our review model so we go to the next screen and now our second screen has access to that same fruit view model we can see the data here but but we actually did not create that view model in this screen created in the last screen and passed it through so this is a perfect example of when to use observed object and on the first screen when we initialize it it is a perfect example of when to use a state object so that's it for this video i know this was a very long very confusing one i would probably if i were you just learning this the first time i would probably re-watch this again because there's a lot of really important information here when you create actual apps when you have production apps you're going to need to create classes that are going to hold all of your extra data all of your data for downloading user profiles user information for downloading posts you want to put all that logic into a class and of course you want your view to be updating when the data in your class is updating so inside the class you have to create a published variable which tells the class that something changed and then we need to make the class conform to observable object so that we know xcode knows that it can observe this object and then when you actually initialize this class you need to use a state object and this way with the state object the view knows that if anything gets published in this view model in this class it knows to update automatically and lastly if you're going to pass this view model around your app from screen to screen you got to use observed object for all the other instances but state object just for that first one so very long video hope it wasn't too confusing i definitely hope you learned something in this video and in the next video we're going to learn about environment objects which are also very very similar to both of these so both of these videos go hand in hand definitely excited to show you guys that as well leave a comment below if you learned something but also leave a comment if you are still confused about anything i'm always happy to help thank you guys for watching as always i'm nick this is swiffle thinking and i'll see you in the next video
Info
Channel: Swiftful Thinking
Views: 6,490
Rating: undefined out of 5
Keywords: SwiftUI Bootcamp, Learn SwiftUI, SwiftUI StateObject, SwiftUI @StateObject, SwiftUI ObservableObject, SwiftUI @ObservableObject, @StateObject, @ObservableObject, SwiftUI what is @StateObject, SwiftUI what is @ObservableObject, @StateObject vs @ObservableObject, SwiftUI observable object, SwiftUI state object, state object vs observable object, @ObservableObject SwiftUI, @StateObject SwiftUI, observable object SwiftUI, state object SwiftUI, @ObservableObject not working
Id: -yjKAb0Pj60
Channel Id: undefined
Length: 35min 12sec (2112 seconds)
Published: Wed Mar 10 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.