SwiftUI Tutorial: What's the difference between @State, @ObjectBinding, and @EnvironmentObject?

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] in this swiftly wire tutorial I'm going to answer one of the most common questions people have when using swifty why what the heck is state and why should they care what are you swifty why you'll see things like this at state object binding and at environment object and if that wasn't confusing enough you'd also see things like this dollar show Advanced Options what are those at and dollar signs mean and when are they needed in this video I'll be explaining exactly how state works in Swift UI and more importantly how state object bindings and environment objects are different things by the end of this tutorial you'll know exactly how swift UI manages state how each of its options work and which to use in every situation we've got so much to do let's get started in Swift structs are immutable which means they have a fixed value and we can't change that Swift relies heavily on structs we use them for almost everything integers strings boolean's arrays dictionary sets tuples and more are all structs which means all have fixed values being fixed in value means we can't just change them when we please think about it it doesn't make any sense me to say that I've changed the meaning of 5 to equal 6 it's nonsense swift UI follow the rest of Swift in using structs for its views that means when you design your layouts most of the things you're working with are structs so structs are simple things that are fixed in value like strings and integers and swift uy uses structs with views what does all this have to do a state to find out let's look at some code first I'll make a property called is authenticated equal to false then inside the body I'll make a button that toggles that boolean with a text toggle authentication this boolean here is state it's something that tracks the way our programs configured and changed over time based on user interactions when we build this code Swift complains we'll try to change a property of our struct and like I said structs are fixed in value so that isn't possible obviously this causes a problem because we need to make changes to our program state all the time so how can we fix this Swift five-point-one introduced a new feature called property wrappers which allows us to write custom code to handle away properties are created and stored for example if we want a lazy property and swift we change this by reverse name is a string a return string named reversed to this lazy by reverse name is a string equals and then put parens after the closing brace if you think about it that lazy keywords doing a lot of work for us Swift won't do any of the work to calculate the reverse name until it needs to and when we do run it the value all gets stored away so the work isn't repeated the lazy keyword is actually baked right into the Swift compiler it's designed as a hard coded language feature which means we can't make something like it ourselves or at least we couldn't this is exactly what property wrappers are designed to let us do with property wrappers we can design our own version of the lazy keyword entirely in Swift so on one side we have swifty wise instructs reviews which means we can't modify properties in our views and on the other side swift gives us property wrappers to control the way we create and store properties in Swift UI these two features come together to do the impossible it gives us multiple property wrappers that allow us to modify state instructs yes you heard that correctly Swift UI let us modify state inside structs to demonstrate this we can go back to our earlier code and use a property wrapper just by adding at state here the at state part is a property wrapper that tells Swift that this property will change over time it's part of our program state if you imagine our struck as a chunk of data that it's a memory and it properties restore are in there too which means we can't change them but when we use the at state property wrapper for a property it comes out of the struct and is stored somewhere else somewhere that's managed by swift UI and can change so when you see at state before our property it means this is data that's going to change as my program runs so let's Swift UI take care as memory for us but there's something even cleverer here you see one of the other things that State does is let Swift you wire monitor for changes so when a state property changes Swift ey knows to automatically reload its views so you reflect the new state so if we had a user name property we could use it in a view like this text welcome string interpolation user name but if we use the at state property wrapper anytime the property changed swiftly one knows to automatically reload the body of the view so the Welcome message showed the new name we can actually see this in action if we use a little bit of juicy decode at state var reload count equals zero dispatch Q dot main dot ASIC after deadline now plus one self dot reload count plus equals one and then return text reloads two interpolation reload count that code increments reload count by one after a second is passed but when we change reload count swiftly wire will also reload the body of our view which will in turn run the gcd code again and the counter will keep going up so the at state property wrapper that has modified values in our views over time to reflect the changes happening in our program even better when we change one of these state properties Swift UI knows to automatically reload our views anyway before I move on there's one more thing I want to talk about this code creates a user name property and use it inside a text view as its marked at state the text you will be reloaded whether property changes we can say that the value of the text is bound to the value of user name when username changes the textview must update but what happens if the property is being used in something that's editable by users for example if this text were a text field instead when the user name property updates we want that text field to update to when the text field changes we also want the user name property to change the to need to stay in sync to solve this we need to bind the property to the text field so when the property changes the text field updates and we also need to bind the text field back to the property so any changes to the text field are saved in the property - this is what's called a two-way binding a binding where data flows in both directions so everything stays in sync now of course we don't want to have to write code to handle all that synchronization of data but fortunately we get that functionality for free thanks those property wrappers I mentioned earlier when you see a property being used just by itself like user name it means you want its value to be used there Swift will replace that property name with its value when the code runs but when you see a dollar sign before it you're actually accessing the value through its property wrapper and that allows Swift dy take care of a two-way binding for us so text some VAR reads a value text field dollar some bar reads and writes a value so that's how at state works it asks with UI take control over a property whose value can change over time and that's also how dollar some property works it's a two-way binding between a view and a property so when one changes the other is also updated but what do you have more complex data let's take a look at another example here we have a view that has several state properties a username a password and an email address together those represent a user in our program and we might write some code that lets them edit those properties text field dollar username text field dollar password text field dollar email address but what happens if we want to send that user later to other views in our app or save a later or transmit it over the network we could just send each of the values individually in all those cases but that isn't convenient what we really want to do is wrap them all up in a single user type it can be passed around as one object for very simple situations we could move those properties out into a struct then use an at state property so when any part of the structures it will reload our view we could say struct user and it pastes those properties in and then create an at state property for that back in my content view and update my three text fields to be bound to user dot user name use it up password and use it or email address that works fine when the user is only available in a single view what happens when multiple views need access to that same user then we have a problem you see as well as being immutable structs also only ever have one owner this means if you create an instance of a struct and use it inside several views they all have their own unique copy of that struct so if one view changes user name to be three straws rather than two straws all the other views won't be updated this means different parts your app will have different ideas of what the same data should be which is a recipe for disaster having the share state across multiple views is such a common problem that swift UI comes the great solution for it built right in called object binding the best way to understand how it works is take our current code and upgrade it so here's our current code and what it changed user to be a class classes are reference types in Swift which means they create an instance of our user class we can pass into many places and they'll all share the same value when a property changes in one they all change remember we used at state before making a struct which means that we were giving Swift UI control over how that struck was created and stored every time has struck changed so if you I could detect that it is being recreated and it would automatically update our UI but classes don't get recreated when their values change we can change them in place this means Swift UI can't automatically detect changes so it can't automatically update our UI to fix this we're gonna bring in the combined framework combined is Apple's way of letting us protest values over time which is a fancy way of saying we can make things that watch for changes and make other things that announce changes in this case it allows us to make a class that can announce when it's changed so anyone watching to respond to those changes first we import the combined framework next we create an instance of pass-through subject which is combines a way of sending notifications when changes happen we get to specify whether we want to send anything specific to whoever's watching us and we also need to say whether those updates might throw errors in our case we don't want to send any of the Irvan saying a change happened and will never throw errors so we'll say void and never var the change equals pass-through subject angle brackets void never prints so now we have a way we can tell the world when values in our class change we can ask that pass-through subject to send an announcement to whatever is watching so if we want to send that announcement whenever our user name changes would add a property observer like this did set did change not send in this case we wanted to send notifications when any of our three properties change so we'll add did set to the other two as well so now any of it watching our class will automatically be notified when it changes but that's only half the problem how can we tell swifty why to watch for those announcements so it knows our betters UI obviously we don't want to write all that code ourselves we want something like at state it'll do all the monitoring for us of course swift UI comes the great solution baked right in called at object binding this is another property wrapper and will cause your view to watch any object for changes that get announced and when it happens it'll reload its UI soirée place at state with at object binding the early changes we to make that work is to make our user class conform to a protocol called bindable object this protocol has a single requirement which we must add added change property that announces when changes happen but we already added that so Swift UI can now watch a changes to try it out let's make two of our text fields point to the same property on the user class now when we run the program typing in one text field will automatically update the other this happens because our did change property is announcing those changes to whatever is listening and now that us with view is watching it'll have tech those changes and reload the view even better because we used here class we can now share that single user instance in many parts of our app and Swift UI will automatically keep them all up to date so if one view triggers the user name all the other views will automatically update please just stop and think for one moment how amazing that is before we had the problem that different parts of our app could get out of sync with each other because add their own copies of data now though swifty Weider's aware of those problems entirely all parts of the app automatically stay in sync so now we've seen at state four simple properties like strings integers and billions and we've also seen at object binding which is great with complex properties like custom types and when you want to share one piece of data in many views before I move on I'll add three more things that come about as a result of the things we've learned so first when you use at state you're defining something that belongs over to the current view it's not designed to be passed around as a result Apple recommends you mark all your at state properties as private second whenever you want to use a reference type of some state ie an instance of a class you always need to use at object binding so Swift UI knows when their values change third whenever you're announcing changes in your bindable object classes you must always do so on the main thread swift ey like most user interfaces only work on the main thread and because your update announcement will cause a UI refresh it must happen on the main thread so that's at state and at object binding there's a third option here at environment object this is another way of working with state in your app and it's just as important as the other two now I know what you're thinking why do we need another way of handling State surely between at state and at object binding we have every base covered well not quite let's say a user logs into your product and starts browsing your storefront they select the product to buy in your app and you ask them where should be delivered then you ask them what speed of shipping they want and you ask them whether they want gift wrapping and you ask them to enter the payment details and finally boom you save the oil to your server user two four six zero one or the loaf of artisanal bread to be delivered tonight with no gift wrapping because well it's a bluffer bread in order for the last part of our app to know what user placed the order waited pass at the screen B C D and E even though none of those screens actually want or use the information other than is passing along now wouldn't it be great if we could automatically share that user in any view that needed it well that's exactly what app environment object is shared data available anywhere so rather than passing objects down our long chain of views we can instead put them into the environment so any of you we embed or present automatically gets access to that data and before you ask yes environment objects automatically stay in sync just like object bindings which means if two of you share the same environment object and one changes it the other one updates as well in fact it even uses the same bindable object protocol so you already know how to use it the only difference between an environment object and a bindable object is how we reference them because environ objects aren't past the views we don't have to give them default values or pass them inside initializers so we could just take our existing user and sale gate from the environment rather than being passed in explicitly or creating our own an environment object VAR user is a user that means user no longer has an initial value and there's no custom initializer for it which means it's possible for it not to exist it's possible for this view to be created and shown without us ever actually providing that value and Swift doesn't even complain like it normally would that doesn't mean it works though in fact if we try resuming the preview you'll find it crashes immediately we've told Swift ey that the user property will come from the environment but it isn't there it's a bit like implicitly unwrapped optionals we're making a promise that it'll have a value so Swift won't write a check that's definitely true before using it but if we're wrong if the data we've said would be there isn't actually there then our app will crash the fix is pretty easy we need to create an instance of our user and place it into the environment so Swift you I can read it so in our scene delegate we might make an instance of our user data as a property we create at once and keep it alive by user data equals user we can then pass that into our content view like this dot environment object user data those changes are enough to make our program work great when it's run but it'd be nice to have the echo previews working well - those don't come from the scene they look at though they come from a special part of our content view code down in that hash if debug session that codes only run when we're in debug mode we've pushed the app directly from xcode so it's safe to make test data right in there so I'll head back to contribute Swift and down here I'll make some space and say let user data equals the user content view dot environment object user data and now you can resume the preview and it will work great can add some example environments to work with now if there's one thing that confuses folks and they first start using the environment it's this how does Swift know that the environment object we passed in belongs to the property inside our struct after all we wrote no code that puts them together well behind the scenes the environment is effectively a key value store like a dictionary well the types of an object are the keys for the dictionary so or in place an instance of a user class into the environment swiftly one will automatically use that for any environment objects there are of type user the great thing about environment objects is that they break our long chain of data being passed around if you a create a user they can stash out away in the environment then present view B which presents view see which resents view D and then view D can lead the user from the environment Ryan having it passed along the chain and again don't forget that when one view changes an environment object all other views using the object get notified and update their UI we get all the benefits of sharing objects without the hassle and having the watcher changes ourselves the important thing to remember is that the environment flows downwards if you a put some into the environment that any views it shows can read that value if it wants to or any of you show my those views and so on but that does not mean some other unrated view will get it only views that have view a as an ancestor will Swift ey makes extensive use of the environment things like dark mode and dynamic type and size classes all come from the environment so any of you can use them whenever it needs to anyway we've now covered at State at object binding and at environment object while also looking at the dollar sign for two-way data bindings so let's summarize use at State for simple properties that belong to a single view at state properties should usually be marked private use at object binding for complex properties that might belong to several use any time you're using a reference type you should be using object binding for it use at environment object for properties that were created somewhere else in the app like share data make sure you set them in the environment of for use otherwise your app will crash and those dollar signs just mean you have a two-way binding swiftly wire will read and write property as its value changes to make sure your properties and the UI stay in sync so that's the end of this tutorial video I hope it's give you a deeper understanding of how Swift UI works with States and how to effectively use all the various options available to us anyway if you enjoy the video please hit the like button and don't forget to subscribe to my channel I make lots more videos like this one to help you improve your skills as a swift developer you
Info
Channel: Paul Hudson
Views: 82,124
Rating: undefined out of 5
Keywords: swiftui, swift ui, swift, xcode, ios, state, object, object binding, bindable, bindableobject, objectbinding, environment, environmentobject, preview, code, programming, tutorial, binding, class, struct, bindings, property, wrapper, ui
Id: stSB04C4iS4
Channel Id: undefined
Length: 22min 52sec (1372 seconds)
Published: Fri Jun 21 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.