Data Flow in a SwiftUI App

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Hey, Code Crew, it's Chris here in this video I want to review with you how data plays a part in your views, and also how data flows through your app across multiple views. We'll start with the simplest scenario with a single view, and then we're going to work our way into more complex scenarios involving multiple views. If you've ever been confused about all these keywords, like observable object observed object published state bindings, all these things we are going to go through in this video. And by the end, I'll all be straightened out in your head. Hey, Code Crew, I'm Chris, and if you're new here, welcome to Code. Is Chris the place to be? If you want to learn how to code and how to make apps, if you're just starting out, check out our 14 day beginner challenge right over there. It's the perfect place to start, even if you've never coded before. And in fact, we have a whole community in our Code crew form going through the same series right now. All right. Without further Ado, let's get on to today's topic. So. First let's start out with the simplest unit of data, which is a state property. Here I have a new Xcode project. Let's start by declaring a regular property. So. I'm going to declare a property called counter, and it has the value zero right now and now I'm going to turn it into a state property by using a property rapper. Now, a property wrapper is a special keyword that you put in front of a property to give it special powers or special abilities. So property wrappers all start with the app symbol, and in this case, the state property rapper is at state, and what this has done has turned this counter property into a state property. So what can it do now? Well, now the value in this property can change and then be whenever it changes. It'll notify any view code that is referencing it, which causes the view code to recompute itself and show the updated view. So let's take a look at an example inside our view code right here. It's just a static text label with the string Hello world. Why don't we change it to say your age is, and then I'm going to reference my state property, right? Typically, state properties are used for two different reasons. A it's a value that your view depends on and B that value can change over time. So coming back into Xcode here in this text label, it now depends on that state property to display what we wanted to. And whenever this value in the counter state property changes, it's going to automatically cause the view code to recompute updating has been positive. Let me resume that. It's just automatically pause again. But when you actually run your app, if you had some external source changing this state property, the value of it. I mean, such as maybe a button when you tap on it, we update this counter property, or maybe some background process that changes this state property over time. Whenever this value changes, whatever view code depends on, it will recompute itself. So with that said, let's do a quick recap. Number one state properties store values that your view depends on. Number two, the value in a state property can change, and when it does, the view recomputes itself using the new value and number three state properties are usually declared in the view that needs them. All right, now let's move on to bindings. What are bindings now in the code that we wrote for the state property, we sort of have a one way relationship going on where your view code reads the value from your state property. A binding simply changes that into a two way relationship so that your view code or your view component can read the value from the state property, but it can also write values back to it. So I can change the value of the state property. And that is what a binding enables. Take, for example, the text field component. To display a text field, you need to specify a state property to go with it. The text field will read the value in the state property and display it in the text box. However, since the user can put their cursor in the text field and then write some text into it, the text field component needs to be able to store that user input somewhere. That's why we need this relationship to be a two way relationship. The text field needs to be able to write to the state property to accurately represent the state of the text field. And we can do this by adding a dollar sign in front of the reference to the state property in the view code. This now becomes a twoway relationship, so the text field can both read and write to the state property. So now let's do a quick example inside Xcode. So coming back to this view here, let's put this into a V stack and let's create a text field. Now a text field. As you can see here, we can put in some default text, which I won't. And we also put in a binding. So to specify a binding, you use the dollar sign followed by the reference to the state property. So I'm going to reuse our counter state property right there. And what this is going to do is it's got to be a string. So let's actually turn this into a string instead. So we have a counter right there. Let's just run it in a simulator actually, so that we can actually type it in. You can see. Alright. So we have our text field here. You can't really see the outline, but I can actually type something into it. In this case, what's happening is that when I launch it right, it's reading the value of the state property and it's putting it into my text field. By default. It's three. So it's reading three in there. And this text label is also reading the counter state property. However, this is a one way relationship, so it's only reading, whereas in my text field, when I put in some text here like that, it's actually writing the value back to the state property. And I can do that because of this dollar sign in front of that state property reference. It is allowing that two way relationship. And you can also see when the value of the state property changes. This text label automatically detects that and recomputes the view code to show the new label. So now you see state properties working in action as well as bindings. So let's do a quick recap of bindings. You put a dollar sign in front of a state property reference in your view code. Remember not in the declaration, but in the reference to turn it into a two way read write relationship. So the next question is what is the app binding property wrapper? Let's go back into Xcode so we can see it in action. So let's create a brand new view for this and we're going to go new file is create a brand new Swift. I view a call this Control panel view, and you'll see why in a second up here we are going to declare a new property called user input, and this will be a string type, and we are going to use the app binding property wrapper. Remember, the app symbol means that this is a property wrapper, which is a key word you put in front of a property to give it special abilities. So by declaring this user input property and giving and making it a binding property, you are specifying that when an instance of Control panel view is created, the creator must also pass in a binding that binding is going to be from outside of this view, and when it gets passed in, it's gonna be stored in this user input property right here and you're going to be able to use it inside your view code inside Control Panel view. Now, why would you want to do this? To answer this question, consider the fact that usually a view will be comprised of smaller sub views. This is the break up larger views into smaller components so that it's more manageable, but more than that, it promotes reusability in your app. If a particular piece of your UI shows up in multiple views in your app, break it out into a sub view. That way you can use that sub view in multiple places. So whenever you have sub views and you need to access a state property from outside of it, this binding property rapper is going to come in handy. Now let's demonstrate how this works. So I'm going to create API. Didn't know this project was running. Let's stop that. Let's create a brand new view here and call it parent view for lack of a better name. And let's say inside the parent view, you declared a state property called user input, and this was a string. Initially, it's not going to be anything. And you're going to have a rectangle. And the color of this rectangle is going to depend on the user input right there. So we're going to say foreground color. We're going to say user input. We're going to test it is equal to blue. If it is equal to blue, then use the color blue. Otherwise use the color black. Now having your view code depend on this state property right here. Let's see what we get in the preview. Okay. So we're supposed to pass a binding in here. I am just going to comment comment out the whole preview for now. So inside the preview. Alright. So it's black right now. Right. Because the user input is empty. But if it's blue, then you see blue. Now we're going to have a text field where the user can type in in the text field is going to change this. So if the user actually types in blue into the text field and it's going to be blue, but let's assume that this text field is part of a control panel that appears in multiple places in your app. So we want to turn it into a sub view, which is what we have here. Right. So what we're going to do is we're going to create an instance of control panel view, and you can see that it accepts a binding. Right. Because in the control panel view, we've declare this app binding user input property here. So when we declare an instance of it, it's expecting us to pass through a binding. So what we're going to do is pass through a binding to the state property. So dollar sign right to turn that into a two way relationship, and we're going to pass in user input. That's a reference to this. And then what we're going to do is put both of these views into a V stack. Okay. So that is our control panel view, and that is the rectangle. And this is right. Now let's change that into an empty string. So we're going to be black now going back into the control panel view instead of a label. Here, let's turn this into a text field. And as we demonstrated earlier with the text field, you need to specify a binding so that whenever the user keys in a piece of text into the text field, it can store the user input into whatever state property it's Bowen to. And will you look at that? We have a binding property and a binding gets passed into this right here. So you can just specify user input just like that. Okay, there we go. Now, let's run this app and see what we get. It's still showing Content viewer here. Let's change our app entry point to parent view. Alright, so we have black API. Want to change our text field style here? Text field style, rounded border and surrounded border text your file so we can actually see a border around it here. Okay, so if I type in green, nothing happened. But if I type in and this is actually a lower case blue, I can actually type in a lower case right now. So I didn't change my code here to ignore case lower. Oh, what's wrong with my keyboard? Okay, so I'm going to say lower case, so it doesn't matter what the casing is, and then we're going to type in blue. So look at what happened here. This rectangle is in the parent view, and it depends on the state property right to determine if it should be black or blue. This text field is actually inside a sub view called Control panel view, and we are passing a binding through to the state property and from within the sub view. It's actually modifying that state property that is in the parent view, which is then triggering the rectangle to be black or blue. So that is what the app binding property rapper is for. So to recap number one, you put a dollar sign in front of a state property reference in your view code to turn it into a two way read write relationship number two. The app binding is a property wrapper to indicate that it expects a binding to be passed to the view when an instance of the view is created. And this lets a sub view modify a state property that is not in that sub view. That is, maybe in the parent view. So let's consider what we've learned so far. You learn that state properties are used to store values that a view depends on. You learn that you can use a dollar sign when referencing a state property to allow a view component to change the value in a state property. This is called a binding to the state property. Furthermore, you learn that if you have a sub view, you can declare a binding property in the sub view. That way, when you create an instance of the sub view, you can pass through a binding to a state property, and the sub view can now change the value of that state property that exists outside of that sub view. All right, next, let's talk about observable objects. Remember when I told you that state properties are used to store a value that the current view depends on? Well, sometimes there may be a value or values that multiple views depend on like a logged in state. So which single view would you declare this state property in? It doesn't make sense to declare the state property in any single view since multiple views depend on it. So this is where an observable object can come in handy. It can be used to store that state property, which then multiple views can depend on. Let's see how this works. So first you have your class conform to the Observable Object Protocol. This lets views observe or listen to objects of this class for any changes by. Changes to the class aren't published. You must specifically Mark the properties that will publish changes whenever the value changes. Use the app published property rapper to do this. Now, if the is logged in property changes, it will publish the change to any views that are observing my object. Lastly, in the actual view, use a property to store a reference to the observable object. In order to listen for any published changes from this observable object, use the app observed object property wrapper. Now in your view code you can reference the is logged in property and change your view according to its value. And when its value actually changes, your view code will be notified and it will pick up that change and it's going to recompute the views to reflect the new state. Alright, now let's do an example in Xcode. Alright. So I reorganized the files that we were working on into folders so that if you download this project, it's a little easier for you to find the sections. Since then we're talking about observable objects. Now I've created this folder. Now let's create some new files on here. So we are going to first declare a new view. So let's say this is the account view and then we're also going to create a another view. This is going to be a sub view called Balance view and inside the balance view. First of all, we're going to create our observable objects. So let's go into new file and say Swift file. This is not a view, right? Remember the observable object is a class. It's independent of any view. So we're going to call this the profile. So. Import first of all Swift UI because that's going to give us access to the property wrappers and keywords that we need and we're going to have class profile and to confirm to observable object to use colon and then just observable object. Don't use observed object. Okay. And then we are going to have a published property called is logged in and we'll initially set this to false. So then that's all we're going to do here. And then in account view. Remember what we do, right? We use the observed object property rapper and we're going to call this our maybe profile model and we're going to create a new instance of profile. Let's just call it profile for simplicity stake. So by doing this it means that the account view is interested in listening for any changes in this profile object. Right? This profile object has to be an observable object in order to publish changes. And if nothing is marked at published, no changes are going to be broadcasted. The account view is not going to really get notified of anything. Right? So it's a combination of all these different keywords that makes this whole system work using this app. Published property wrapper means that whenever this value changes, it is going to notify any views that are observing this observable object. So in this case since account view is observing this whenever the is logged in property is changing, it's going to update any view code that depends on it. So now that we have this observed object property we can use that is logged in property like so in our view code can depend on it. So we're going to say if logged in is true, we are going to display Hello and welcome back. Otherwise if it is false then we are just going to say please log in. Okay. And then we are going to have a button that when tapped on it's going to just change that is logged in property and flip it to true. So let's do stack here. I'm going to put a button here action label. The action is going to be profile dot is logged in and we're going to just make that true. And then this button is just going to say log in. So let's clean this up and then Furthermore in balance view, we are going to do the same thing actually before we do the balance view, let's just test it. This works. I have to update the entry point instead of opening up parent feel we want to open up account to view. All right. So please log in when I click this button. What's going to happen is it's going to flip this to true? And because our view code depends on that property, this text label is going to change. So it's going to say Hello and welcome back. So there you have it that is an observable object. Our view code inside account view is depending on what's essentially a state property that is in another object in another class. Now before we move on to see what happens in a scenario with sub views, let's do a quick recap of observable object. Number one. The observable object pattern allows you to store state in an object that is independent from any views. Number two, have your class conformed to the observable object protocol to allow views to observe it. Number three in the observable object. Use the app publish property rapper to Mark any properties you want to broadcast its changes. Number four in the views you need to Mark the reference to the observable object with the app observed object property wrapper. Alright, now let's take a look at how this affects sub views. This is what I created the balance view for. So in here we are going to also put a balance view here. We're just going to declare a balance view like that an inside balance view. We are also going to depend on the is logged in. So if the user is not logged in, we're just going to show that you cannot see the balance. However, if they are logged in, then we're going to show the actual balance. So why don't we again use what we've learned and declare an observed object or profile equals profile right. And that means in here we can do profile is logged in. If you are logged in, then you are going to and you can use equals equals. True. If you want to be really clear or you can just omit this for brevity's sake, it's up to you how what your coding style is. So we're going to say in this case, balance is one dollars. If you're not logged in, we are not going to show show the actual amount. Okay, so let's run this and see what we got. Okay, so please log in. We can see the balance. I'm going to hit login and this changes. This detected the change, but the sub view balance, you did not detect the change. And what is going on now the problem is actually that the balance view is looking at a different profile object than the account view is. You see here in the account view, we created a new instance of the profile object, so the profile class and we assigned it to this observed object profile property. And then when we tap on the button, we are changing the is logged in to true of this object that we created up here in the balance view, it is creating another profile object and it is observing that one. Right. And that is why inside the account view. When we tap on this login button, it's only changing the state in this profile object and not in the one inside balance view. So let's look at a quick diagram so you can visually see what I mean. As you can see in this diagram, account view is looking at its own instance of profile and balance view is looking at another instance of profile. What we actually want is the account view and the balance view to both be observing the same instance of profile. So in order to fix this so that both views are referencing the same instance of our observable object, we can use the environment object modifier and property wrapper. So what's an environment object? Well, it's simply a way to create an instance of an object and have that object accessible from multiple views. Now consider this diagram. We have a view and then it has a sub view and that sub view has another sub view. This forms a sort of hierarchy, and this is known as the view hierarchy. Now it's important to visualize this in your head because when we attach the environment object, it is only accessible to whichever view we attach it to and all of its sub views. So there are two parts to environment objects. One is a way to create an environment object, and then the second part of it is a way to access that environment object. So first of all, to create an object and attach it as an environment object, you use the environment object modifier like you see in the yellow text here that makes that object accessible to the view and all of its sub views in the view hierarchy. Now to access the environment object in any view and its sub views, use the app Environment object property wrapper, and that property will automatically be filled with a reference to that environment object. Now let's try this out in our Xcode project. So in our account view and balance view, let's change this. Instead of creating a brand new profile object here we are going to get rid of it. And then in Balance view, we are also going to get rid of it. And then in the entry point for our app right here where we are creating an instance of account view. We can use the environment object modifier and we're going to create that profile object right here. And what this is going to do is make this particular profile object accessible from the account view and all of its sub views, which means the balance view. So let's go back to account view. Now. The way that okay, go back to the account view. For some reason, the canvas is taken up a lot of space inside here to access that environment object which we created and attached. We are going to use the environment object property wrapper and then declare the property like normal. We have to specify the data type of it though, and notice here I'm not creating a new instance of profile. What's going to happen is it's going to read this instance and it's going to populate this property with that instance? So here I can still access the property. And here when the button is app, I can still change that is logged in property. Now from the balance view, I want to access that same object. Right again, because Balance view is a sub view of the account view you see here, right. It can also access the environment object. All I need to do is use the environment object property rapper like that. And again, I'm not creating a new instance. In both these cases. It's acting this instance right here. Now if I run this and I hit this button, you can see that both the account view and Balance view are looking at the same profile object. So let's do a recap number one. The environment object modifier allows you to make an object accessible to the view you attach it to plus all of its sub views. Number two from the view that it is attached to an any sub views. Access the environment object with the app Environment object property wrapper. So what about identifiable hash able and decodable? You might be confusing these keywords for something to do with dataflow, but these are actually used for different things. The identifiable protocol helps Swift UI distinguish from different items when you're iterating through them. Maybe you're trying to display it in a list or an A for each loop. Mashable is another keyword for the navigation view and the tab view to distinguish between different values as well, and decodable protocol is used for JSON parsing. When you're trying to turn JSON data into usable instances of some struct or class, you might have declared, these are all topics that we can cover in another video, but I didn't want to cover in this video because they don't really have to do with what we're talking about today, which is data flowing through a Swift UI app. There's also another topic which you may or may not have heard of, which is called a Singleton class, and this sort of has implications for dataflow and Swift UI. However, it is a topic that deserves its own video, so just to give a quick overview of it, if you turn your class into a Singleton class, that means you can only create one instance of that class in that instance is globally accessible anywhere in your app. This is really handy, but it can be easily abused, because if you made everything a Singleton class, then you wouldn't care about data flow through your app because everything would be globally accessible from anywhere. And the danger of that is that it can make debugging really hard. It can make maintenance really hard, and also it's prone to a lot of logic errors simply because it's too free. There are no constraints. So I hope that clears things up for you. If this lesson helps make things more clear for you, please show your appreciation by giving this video a thumbs up down below. I appreciate that. The other thing I want to mention is that this lesson was actually taken out of our screencasts collection in our CWC plus program. If you're interested in getting access to all of the training courses and learning journeys inside our CWC plus program, there's a special offer for YouTube viewers like yourself down below in the description. Lastly, I just want to say I appreciate you. Thank you for watching and I'll see you in the next video.
Info
Channel: CodeWithChris
Views: 7,209
Rating: undefined out of 5
Keywords: swiftui data flow, passing data, state property, state object, binding, environment object, swiftui state vs stateobject, swiftui data model, swiftui binding, swiftui environmentobject, swiftui data binding, swiftui observedobject, swiftui observedobject example, swiftui observedobject binding, swiftui observedobject vs stateobject, swiftui observedobject vs binding, pass parameter, swiftui pass data, swiftui, app development, ios
Id: jD6c9y8CFGQ
Channel Id: undefined
Length: 33min 15sec (1995 seconds)
Published: Wed Oct 13 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.