Protocol and Value Oriented Programming in UIKit Apps
Building on last year's Protocol-Oriented Programming and Building Better Apps with Value Types sessions, this year's session will highlight tips and tricks for building better Swift apps. See how you can incorporate these design approaches into a real MVC-based Cocoa Touch app, especially in the view and controller layers, where you might not have thought of using these techniques before.
I'm Alex, and Jacob and I are going to talk to you today
about how you can use value types and protocols
to make your app better.
And we're going to focus today
on something called local reasoning.
Local reasoning means that when you look at the code,
right in front of you, you don't have to think about how the rest
of your code interacts with that one function.
You may have had this feeling before
and that's just a name for that feeling.
For example, maybe when you just joined a new team
and you have tons of code to look at but very little context,
can you understand what's going on in that single function?
And so the ability to do that is really important
because it makes it easier to maintain, easier to write,
and easier to test, and easier
to write the code the first time.
And so we're going to talk
about how local reasoning can improve our UIKit apps
in the context of model view controller design paradigm,
which is what Cocoa uses, where the model stores your data,
the view presents that data,
and the controller coordinates between the two.
And so we need a real app to put our answers to the test.
Unfortunately, we actually had a problem at Apple
that we needed to solve.
So I'm going to tell you a little secret
about how we plan WWDC every year.
Engineers have dreams and that's what they're dreaming
about for what they're going to present.
And so somehow we need to take those dreams and record them,
but we noticed a lot
of engineers don't actually remember their dreams
by the time they get into work.
They forget them.
So Jacob and I developed this awesome app to do just that
and the app is called Lucid Dreams.
I want to show you some of the dreams
that people had over the past year.
Some people dream of unicorns.
This is serious [laughter].
Strangely, some people were also wrapped up in work stress issues
and even more wanted to just get
out of the office and ride their bike.
And some engineers even dreams of Crusty.
And so that was the inspiration for our application.
But since we're going
to be using the app throughout our session,
I want to give you a quick demo so you can get a feel
for what this application does.
And so if we launch the app, you'll notice it takes us right
to the list of dreams that we've had.
And if you tap one of the dreams, you can edit it.
And so you can see we have a preview at the top.
We can scroll down and we're going to add some effects,
like laser focus and rain.
And so if we scroll back up, you can see the preview of a dream
of a unicorn with laser focus, rain, and fire breathing.
And so when we're done editing the dream, we can just go back
to the list of our dreams.
And so that's the app.
And we wanted to think really,
really hard about how we could make our code just awesome.
And so we watched these fantastic Swift talks
from last year.
These sessions focus on the benefits of value types
and protocol-oriented programming.
And these ideas were so powerful that we wanted to take advantage
of them because they can help improve local reasoning
in our application.
And so we wrote our app with this different way of thinking.
Now some of these ideas might feel foreign
to you and that's okay.
When we were initially trying these techniques out,
they felt foreign to us too.
So don't worry.
Just stick with us.
And so we're going to quickly go over the benefits
of value types in the model layer.
This was already covered last year,
so it's going to be a quick recap.
Then we're going to focus on how we can use value types
in the viewing controller layers since that's
where we think most people don't think they can take advantage
of value types, even though there're some really
And along the way we're going to show you how using value types
and protocols made our code testable.
And as you just saw this awesome app, I know you want to test it.
We've shipped this as a sample app
so you can go download it yourself, see the code,
and log your own dreams.
All right, so now it's time to talk about the model layer.
So what is a dream?
A dream is our model type
that represents a dream entry in our application.
A dream as a description, a creature, and a set of effects,
as you saw in the UI earlier on.
Now I want to show you a version of the dream type
that we used last year in the first version of our app.
We started with a dream type being a class.
Now classes have reference semantics,
meaning that references
to the same instance share their storage
and that sharing is implicit.
So why is that a problem?
Well, let's say someone tries to modify dream2's description.
If we only care about dream1, we may be surprised
that the variable's value changed
from underneath our control.
And this really hurts local reasoning.
And we had exactly this kind of bug in our own application,
even though our test passed.
But why was that?
And so this diagram shows the relationships
of the first version of our application.
Some of these relationships can be explicit and implicit,
some of them can be one-way or two-way,
and some of them can even be dynamic or static.
And so these relationships can get very, very complicated.
So what happens when we try
and test just the dream type on its own?
Well, even if you create a dream that stands by itself,
this doesn't reflect the reality in the app
because there are many more dependencies
that actually exist.
And so that's not good.
And so we can solve this by making our dream type a struct
which has value semantics.
This means each variable has independent storage.
So changing the value
in one doesn't change the value in the other.
And so if we modify dream2's description,
we only change dream2's description, not dream1's.
And so this guarantees us that dreams aren't involved
in the complicated relationships that we saw earlier.
And so this really improves our ability to reason locally
because no code can change the value we're using
from underneath our control.
Now using value types --
So we just saw how we can take advantage
of value types in the model layer.
And using value types
in the model layer is actually pretty uncontroversial.
But wouldn't you want to take advantage of the same benefits
that we just saw in other parts of our application?
And that might actually raise a few eyebrows.
So I want to share with you a quote that I recently saw
on the Internet and that said, "Use values only
for simple model types."
That doesn't seem encouraging.
But do we believe everything that we read on the Internet?
And the answer is no, if you didn't know [audience laughter].
And so for the remainder of the talk, we're going to focus
on how you can use value types for more
than just simply model data in your app,
and at the same time we'll prove the Internet wrong.
There we go.
And with that, I'd like to hand it over to Jacob
to talk about the view layer.
I'm really excited to tell you
about how we use protocol-oriented programming
together with views.
So we spent a lot of time working
on our app's table view cells.
We had a specific design for their layout that we wanted
to implement to make sure that the unicorn the people dreamed
about showed up exactly right.
Now when we started our app, we wrote these layouts
as abstract subclasses of UITableViewCell.
For example, this simple layout we called DecoratingLayoutCell,
it just shows a small decoration on the left
and a larger content area on the right.
Then, we made a concrete subclass of the layout cell
that added our specific logic, like showing a dream.
And we did this separation because we wanted to be able
to reuse our layouts in different places.
But as we worked on the app more,
we found that this wasn't working very well.
It helped us reuse our layout in different cells, but it was hard
to use outside of a table view.
For example, we had a detail view
that showed more information about a dream
but we couldn't reuse our layout cell there.
So we wanted to find a better way to structure this
where we could use our layouts together with table view cells
but also in plain UI views.
And we also want to add SpriteKit to our app
to show those cool particle effects and we want to be able
to use our layouts with those SpriteKit nodes too.
So those are our goals and we use what we learned
about Swift to achieve this.
Now although I'll be talking about layout in detail,
I want you to keep in mind
that these techniques can be used all across your app.
All right, let's get started.
Now this is what our layout cell looked like before.
It has two views that it lays out, but there's really no need
for this layout logic to be trapped inside of a cell.
It's just some math and geometry to figure
out the right set of frames.
So let's change this from a cell to just being a plain struct.
It will still have our two views and we can put all
of that layout logic into a single method that can be called
in to, to lay them out.
Now just with that small change,
we now have a really isolated piece of code that knows how
to do our layout and nothing else.
Then, we can update our dream cell to use this new struct
to lay out its children.
And what's great is that we can now use this
in our UIView subclass as well.
Now that this layout logic is decoupled from table view cells,
we can use it in any UIView.
And there's another great benefit with this.
Now that our layout can be used in isolation, it's really easy
for us to run a unit test for it.
We can just create some views, add them to our layout,
and then lay them out in a known rect.
Then, we just have to verify
that the resulting frames are what we expected.
Our test doesn't have to create a table view or wait
for the right view layout callbacks to happen.
It can just tell our layout to work and then verify the output.
And this is part of a general benefit that we have now.
Our new layout struct is really small and focused.
This change has made it much easier
to reason locally about this code.
So if we want to understand our tests to our layout,
we just have to understand that small struct in isolation.
We don't have to think about what set
of view capabilities it might use or override.
Okay, now let's go back to our DecoratingLayout code.
So right now, this still only knows how to lay out views.
But as I said earlier, we want to use this
to support SpriteKit as well.
So we don't want to have to duplicate this code,
but SKNode is not a subclass of UIView.
So there's no common superclass that we can use here.
So how can we combine these together into a single layout?
Well since the only thing that our layout ever does
with these children is to set their frames,
that's the only functionality that we need them to have.
And we can represent that requirement with a protocol.
So we'll make the protocol
and it'll just have a single frame property for now.
This isn't very flushed out yet,
but we'll improve it in a little bit.
Then, we use this protocol as the type
of our children instead of making them views.
And finally, we can use retroactive modeling
to make UIView and SKNode conform to our new protocol.
And now we have a layout that works with both of these types,
and this is one of the great things about relying
on protocols instead of superclasses for polymorphism.
We can use this, add this functionality
to unrelated types to use both of them.
Now, our layouts no longer have any dependencies
on UIKit as well.
And so another thing that we could do is bring this same
system to AppKit and support laying
out NS views just as easily.
I think that's pretty cool.
So we're really close now,
but there's something here that we can improve.
When we're using a DecoratingLayout in a view,
we want to be able to add all of its content as subviews.
And similarly, when we're using it in a SpriteKit scene,
we want to be able to add our content as child nodes.
But right now, content
and decoration can be any type that has a frame.
And that means that for example we could have content
that was a UIView and decoration that was an SKNode.
But instead, we want our layout to just have a set
of only UI views or only SK nodes as its children.
And that way we'll be able to add them
to their appropriate parent.
Now Swift has a great way of expressing that with generics.
So we can update our layout to be a generic type
with a type parameter called child.
Then, we can make the content
and decoration properties use that as their type.
And this gives us exactly what we want.
We can enforce that they're the same concrete type.
And so we can have DecoratingLayout
with just UI views or one that just contains SK nodes.
So generics are a great tool
that let us have a lot more control
over the types in our code.
Another great benefit of generics is
that the compiler has more information
about what your code is doing.
So it can optimize more.
And you can learn about this in a lot more detail
in the Understanding Swift Performance talk.
It's a great talk for learning how Swift works and how
to write fast Swift code.
Okay, we now have a great implementation
of our DecoratingLayout.
But our app also includes a lot of other layouts,
like this fancy cascading one.
And this layout is very similar to the DecoratingLayout
that we just looked at.
They both show a large area on the right
with a detailed decoration on the left.
And we don't want to copy and paste our code
to create this new layout
because that would miss a great opportunity
to create a shared abstraction that both of these can use.
So how can we share this code instead?
Well, one tool you've probably all used before
to share code is inheritance.
But with inheritance, you have both your code,
and please don't try to read this code, but you also have
to consider what your superclass might be doing
and what your subclasses might want to change or override.
So instead of just thinking about the code
that you're working with, your mind has
to pull together a large amount
of code that's spread across your app.
And this is just the tip of the iceberg.
A lot of the time you also inherit from a framework class,
like UIView or view controller and there's orders
of magnitude more code there.
So inheritance is another place
where you really sacrifice the ability to use local reasoning.
But we can share code in a much better way by using composition.
Composition is a simple idea that's just combining smaller
pieces together to build larger pieces.
But when composing, you can understand those independent
pieces in isolation.
And you can also enforce encapsulation without worrying
about subclasses or superclasses poking holes
in your abstractions.
But composition isn't new either.
You've probably used in the past
with Objective-C or other languages.
And one way that we could've made this layout before would be
to compose views together.
So you could've written a UIView
that does this cascading layout behavior and another UIView
that does our decorating layout behavior.
Then you could've added both of those as subviews
in your table view cell.
But there's a big problem with this.
Class instances are very expensive.
When you make another object,
you have an extra heap allocation
and this is even worse with views.
There's a lot of work that's needed to support a view
to allow to do things like drawing and event handling.
And because of this, we try very hard
to minimize the number of views that we use.
So making a view that does no drawing and only acts
as a layout abstraction is very wasteful.
And that's why doing composition
with views doesn't work very well.
But with Swift, we have a much better way to do composition
and that's with value types.
Structs are very lightweight, so we can use them together
without paying the high heavy cost that we have
with classes and views.
And structs are also better because of value semantics.
With value types you have much better encapsulation
so you can use these pieces together for composition
without having to worry
about someone else modifying the copy that you're using.
So let's apply this to our layouts.
Well, we can write the cascading part of our layout like this,
with an array of children that it lays out.
Then, we want to compose this layout with our DecoratingLayout
to get the final effect.
But there's one more small thing we have to change here.
These layouts only expect to have children
that are UI views or SK nodes.
So let's generalize this so that we can use layouts
and compose them together.
Well the layout protocol that we're using
for our children requires a frame property.
But we never need to call the getter for that property.
We're only ever setting new values for it.
And we don't actually care
if our children have a frame or not.
We really just want to be able to tell our child
to lay itself out in a given rect.
So let's change this to a method that reflects that.
When we decide a rect for one of our children,
we'll tell that child to lay out in that rect.
UIView and SKNode can still conform to our protocol.
When they're asked to layout in a rect,
they'll just use it to set their frame.
But now we can make our layouts conform
to this protocol as well.
They already know how to do layout.
When they're given a frame, they just divide up that rect
and give it to their children.
Now we also need to make one small change to DecoratingLayout
to allow it to have more flexibility
in the types of its children.
And we'll look at that in detail a little bit later.
So now we can build our fancy layout
by composing together a CascadingLayout together
with a Decorating Layout.
The composition lets us build advanced layouts like this
in a very declarative way and there's even more examples
of this in the sample code.
So when you're working on your own app, the next time you need
to reuse code or customize some behavior,
trying using Composition.
It's a great tool.
So earlier I mentioned that we wanted to be able
to add the contents of our layouts
to either a superview or a SpriteKit scene.
And an important part of that is adding those contents
in the right order.
For example, our CascadingLayout wants its children to be ordered
so that they line up on top of each other, like this.
So let's expand our protocol to be able to support that as well.
We'll add a property to our layout protocol to be able
to return its contents.
And our combining layouts will return all of their contents
in the correct order from this.
And then leaf views and nodes can just return themselves.
But once again, if we make the type
of the contents just be our protocol, this would allow
for mixed environments of UI views
and SK nodes as the contents.
And since we're adding these children to a parent,
we only want to allow a homogenous collection
of just UI views or just SK nodes.
So to enforce this, we can add an associated type
to our protocol.
An associated type is like a type placeholder.
The conforming type chooses the concrete type
that it wants to use.
So our protocol's associated type is
for what we'll be putting in the array
of contents that our layout has.
This allows us to write something that just knows how
to lay out views and specify that its content type is UIView.
And similarly, we can write layouts
that only have SK nodes as their contents.
And this type safety is really great.
But just like before, we don't want to have
to write a separate layout for views and nodes.
But with the generic version of our layouts,
we can still have a single layout that supports both.
And for our content type,
we'll just use whatever the content of our child is.
And this means that we can make a DecoratingLayout that works
with just UI views and one that works with just SK nodes.
Both are strongly typed so that we can pull out their contents
and know exactly what they are
and they can still share all of the layout logic.
So associated types are a great way
that you make your protocols even more powerful.
So now that we have our improved layout protocol,
we can also revisit the types of our DecoratingLayout's children.
Now here they're required to be the same.
And this works great if they're both UI views but not if we want
to have a CascadingLayout together with a UIView,
like we talked about earlier for composition.
What we really want here is for all of the contents
to have the same type.
So let's update our layouts to reflect that.
We can change our struct to have two different generic type
parameters, one for each of its children.
Then, we can also add a generic constraint to require
that those two types have the same kind of contents.
And this lets us express the exact restrictions that we want.
Our children's content must be the same type.
So this is our finished protocol,
which represents our layout abstraction,
and this is a much better protocol
that what we had earlier.
This now has real meaning.
It's a complete set of operations
that represent what it means to be a part of the layout process.
You can also see our sample app to learn all of the details
about how all this works, including how we also used it
to do layout for rendering images on a background thread.
And one last place that we can also take advantage
of our new layout protocol is in our unit tests.
So we can write a struct that has a frame property
and conforms to our layout protocol
and then we can change our unit test to use this instead
of UI views as the children in our layout.
Now our layouts will just be setting frames
on these simple structs.
And this means that our test is completely isolated from UIView
and only relies on the logic in our own layout and test structs.
So we're unit testing our layout without using the GUI.
I think Crusty would be proud.
So those are some examples of how you can use types
and protocols in ways you might not have expected
in the view layer of your app.
And we also saw some great general Swift techniques
that you can apply anywhere.
First, we looked at how you can improve local reasoning
by using value types.
Then, we saw how you can use generic types
to get better type safety and flexible code.
And we also saw how composition of values is a great tool
for customizing and building up complex behaviors.
Now I'd like to bring Alex back to tell you what we did
with controllers in our app.
Now I'd like to focus on how we can use value types
in the controller layer.
And we're going to talk about this in the context
of our app's Undo functionality.
So we implemented Undo for our list of dreams
and that worked really great, but we noticed
that we had a small bug where Undo doesn't work
for our favorite creature feature.
Now to reproduce this, we can tap the Favorite Creature row.
Right now we have our pink unicorn set
as our favorite creature but let's change
that to be the dragon.
And to finish the change,
we can just tap the Done button at the top right.
All right, so we've modified the favorite creature
but the problem is that if we shake to Undo, nothing happens.
And so that's a bug.
So let's take a look at our code and see why
that actually happened.
So we have two model properties
on our view controller right now: one for our dreams and one
for our favorite creature.
This is a pretty typical arrangement in the UIKit app,
especially as they grow larger and your feature set grows.
And so again we started off with just our dreams functionality
and so we implemented Undo for that
and it worked, which was great.
But after we added the Favorite Creature functionality,
we didn't have that Undo code.
And so the bug was just that we forgot that code.
And so to fix this, we could've added another code path
that implemented Undo for our favorite creature.
That sounds like a maintenance nightmare,
because now every time we add another model property,
we have to add another code path to implement Undo.
And so that doesn't seem good.
So we don't want that.
And so after that we took a step back and wanted to figure
out a solution that would [inaudible] better
as we add more model properties.
And so the solution is to compose these model properties
into a single value, our model struct.
And our undo logic is going to work solely
in terms of that one type.
Note it's really important
that our model still have value semantics and that's
because it's composed of two other values.
And so this approach is really great
because we now have only a single code path
for our two model properties,
and if we add another model property,
we still just have one code path.
And so this is really great.
And so we can accomplish this in code
by moving our two model properties
on our view controller into our new model struct.
And from there, all we have to do is add a new model property
to the view controller.
So that's how we're going to structure it, but now we need
to implement the Undo code.
So how do we do that?
Well, I want to show you first the way that it's commonly done
and we'll see why it's a little buggy.
And so on the left, we have our view controller's current
And on the right, we have our operations and our undo stack.
Now in the original version of our app,
we thought of the undo stack as a sequence of small steps.
Each step was responsible for modifying first the model
and then the view to match.
For example, in the first undo step,
we're going to remove the dream that the user just added
and then we're going to delete the row in that tableView.
And we can continue with the next undo step.
And so in this undo step, we're going to change the model
to be back to a pink unicorn.
And so this approach of mutating individual model properties
and updating our view independently is really easy
to get wrong and that's because you need to match the change
in the model to the change in the view, precisely.
And so failing to do so leads to a lot of inconsistencies
between your model and your view, and so you end
up with bugs like this.
And I'm sure all of you have run into this.
I run into this all the time.
And it's really hard to debug.
But why is it hard to debug?
Well, let's take another look
at our Undo stack that we started with.
Where do these undoable changes actually come from?
Well, each undoable change comes from our view controller
and each of those undoable changes matters for order.
And as we add features to our app,
these opportunities for mistakes spread.
And so there isn't one place in our code where we can reason
about the correspondence between our model and our view updates.
And so this is bad because it's really complicated.
So let's think about a simpler way to handle undo.
What if instead of recording small changes,
each entry in the stack is just a whole new value,
a whole new model and so now performing an undo
on the model is really simple.
Just replace the current model with the one on the stack
and that way we don't even need to worry about order.
And then we just replace the value.
And so now that we have that sorted out with our model,
we need to figure out how we're going to update our UI.
And so in our view controller, whenever a model changes,
we're going to call this modelDidChange method.
Now, I recommend you go download this sample and look
at this method for more information about how it works.
But in that method, we need to find the differences
between the old and the new model values
and update our UI to match.
So for example, we can check to see
if the old model's favorite creature is different
than the new model's favorite creature.
And if it is, we're going to update our table view section
that contains the Favorite Creature row.
And there's a more flushed out implementation in the project,
like I just mentioned, so I recommend you check out more
of the UI that we update in there.
And finally, we can just register the undo logic
where we reset our model to the old value.
Now this is great because now we only need one
or we're only registering the undo in a single place.
So what were the benefits here?
Well, as we saw, we now have a single code path
for updating our UI,
and operations are order independent,
which was not the case before.
This really helped us reason locally about our code,
about our UI update code.
We also saw how values compose very well with other values.
If we have two values as properties of a single value,
that value still has value semantics.
Okay, so we just talked about how to use value types
in the controller layer with our model properties.
And now I want to do the same
with our controller's UI state properties.
And so you've seen this screen before.
It's just our list of dreams.
But this view controller has many, many different states
and so I'd like to show you the state diagram
for our view controller as it relates to a very cool feature
that we have, the ability to share dreams with friends.
So let's take a look.
So this is our basic state diagram.
We're going to start off in the viewing state.
Now we can tap the Share button at the top right, which is going
to take us to the selecting state.
And then we can select dreams to share.
And then we can tap the Done button
to go back to the sharing state.
Then when we're done, we just go back to the viewing state.
And so that all worked well.
But let's go back
to the selecting state again real quick.
You'll notice that we can stop sharing midway
through by tapping the Cancel button at the top left.
And so this moved us back into the viewing state.
And you can see that our navigation bar looks correct
since it shows the Share button again.
But there's actually a subtle UI bug in our app
because of an inconsistent state.
The UI on the left side of the table view is still visible
to allow users to select dreams to share and this is wrong.
So when we went back to debug this code, we saw that some
of our state properties weren't fully cleared
out during a state change.
And so in this case, even though we moved to the viewing state,
we forgot to clear out some of the properties
of the selecting state.
So let's take another look at our state diagram to see
if we can come up with a fix.
Now each state here has a corresponding property.
And those properties are properties
on our view controller.
And the number of state properties
in our view controller can easily explode
as your app's feature set grows.
And so it's important that in this case our properties are
And so when we're viewing, we not sharing.
And when we're sharing, we not selecting.
But the way we've written it, when you set one property,
you need to clear out all the other properties
and this is really error prone.
And so how can we solve this problem?
Well, enums are actually perfect for mutually exclusive values.
And so we turn all of our UI state properties
into cases on an enum value.
And from there, we can just add a state property
to our view controller.
So by using an enum, we can make sure
that our states are mutually exclusive.
And that's great because now the invalid state bug
that we had before isn't even possible
and it's enforced by the type system.
And so this approach also means that our state changes all
at once, without any possibility of any intermediate states.
So we don't to coordinate flipping properties
with implicit timing dependencies.
And as a bonus, having your state all
in one places makes it easier to launch your app
in exactly the same state as the user left it.
So I really recommend you checkout
and download the project again
to see how we implemented state restoration in our application.
Okay, so we covered a lot today, and we started off with the goal
of improving local reasoning in our application
by introducing value types and protocols
into our model view controller based application.
So how'd we do?
Well, we started off by making our model have value semantics,
by making the dream type a struct.
And this makes it easier for us to locally reason about our code
because there's no implicit sharing of our dream variables.
Then Jacob showed you how we built small components,
like DecoratingLayout and CascadingLayout.
These small components took advantage of protocols
in generics to make sure
that the generic components were reusable with views,
SpriteKit nodes, and image rendering.
And all of this led to better local reasoning since each
of these types were small, testable,
and isolated value types.
And then we saw how we can take advantage
of composing model properties
on a view controller into a single type.
This made it easier to implement undo with a single code path,
even as our model type grows to have more properties.
And this approach also gave us one code path
for updating our UI, making it easier
to understand the UI logic
of our view controller in isolation.
And finally, we saw how we can turn our mutually exclusive
state properties into an enum value on our view controller.
Now this reduced the potential for our UI to be an
in inconsistent state.
And so those were the value types that we discussed today.
But if you go and download the sample project,
you'll see that there are actually many more
in the project.
And so pretty much our whole application is built
with value types except
for where we have a controller or view object.
Now these are required to be reference types by UIKit
but we've still moved most
of our functionality into value types.
And so we spoke about a lot today and I want you to go home
with just a few things in your mind to take home.
So the first is customization
through composition instead of inheritance.
The next time you're at your desk
and you're drawing a class diagram to solve some problem,
I want you to think how you can use composition instead
of inheritance to solve that problem, so you get the benefits
of value types that we talked about today.
And the second technique is to use protocols
for generic reusable code.
You can make small reusable components that are easy
to locally reason about and easy to test.
And so I highly recommend you check out how we've done
that in the sample with generic types instead
of having class hierarchies.
We also showed you how to take advantage of value semantics.
The important thing to remember here is
that if you have a value composed of other properties
that are values, the larger value also has value semantics.
And finally, we talked about local reasoning.
Now, local reasoning is actually a very general technique that's
not specific to UI programming, it's not specific
to mobile development, and it's not specific to Swift.
This is a really important aspect
of all programming language, all programming languages.
So when you get back to your desk and you start coding,
I want you to think, regardless of the language, does that code,
how well does that code support local reasoning.
Now it's no accident that Swift emphasizes value types so much
because they're a hugely important aspect for you
to be able to locally reason about your code.
And that's it.
So you can find the sample code
and more relevant resources here.
I highly recommend you do that.
We have some related sessions
that we mentioned throughout the talk
and I recommend you watch them on video.
And thank you for a great WWDC.
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.