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.