Transcript
>> Ken Kocienda: Hello!
Welcome! [applause] How are you all doing?
Are you enjoying the conference so far?
[applause] Okay, good, good, good.
Well, welcome.
Today, we're going to be talking about Model-View-Controller
for iPhone OS, or iOS, as it's now called,
writing Essential Design Pattern
for Making Flexible Software.
And I'm Ken Kocienda, and I work on iPhone software.
Well, today, as you might be able to
tell, from the name of the presentation,
this talk is going to be about design patterns.
Well, what are design patterns?
Well, there's a number of them up there on the slide.
These are good solutions for common
problems in software development.
You're probably familiar with them.
Well, I'm going to pick out one of
them today, Model-View-Controller,
and that's going to be the main subject of the talk.
So, why should you care?
Right? Why is this a big deal?
Why are you here?
Well, I'm going to try to answer that question for you.
Because, well, probably you want to make great apps, right?
Yeah! Okay, so, now, how can this all help?
Well, going back to the beginning of iPhone
development, right, you had a small screen.
We have some great documentation in APIs and, you know,
things are a lot simpler than maybe they used to be,
if you're familiar with Desktop application development, and
it was really pretty simple to come up with an organization
to just manage your one screen of content at a time.
Well, then, along comes the iPad,
where the screen is bigger.
You can fit more widgetry on the screen, more
content presented to your users at one time.
Things get a little bit more complex.
And then maybe you want to come up with an application that
will work on both the iPhone and the iPad at the same time,
and it can seem like you're serving
two masters at the same time.
Well, and that can be a little bit complicated, and so what
this talk is about is how Model-View-Controller can help you
to tackle that problem and come up with some good solutions.
Okay, so, now, if we sort of look at things formally,
if some of you studied Computer Science in school,
you're probably familiar with a drawing like this.
Yeah, there'll be a test on Friday, right?
So, study up.
Maybe, right?
And we're going to be talking about this diagram
quite a bit, as we go along, but, really,
what I'm going to try to do is tie that diagram and sort
of the concepts there back to the real world, right?
Because, again, what you want is to make great apps, and
you want your software to stay flexible, easy to change
and whatever, so I'm really going to
try to tie this back to the real world.
And so I was trying to think of, like,
well, how could I present this all to you?
So, when I think about the real world, I think
of like maybe a supermarket tabloid, right?
You go down there, and you see these things all the time.
"The 10 Best Celebrity Weight Loss Tips Ever."
Right? You see these kinds of things all the time.
I agonized on the copy, so I'll leave it on the screen
there for a second, so maybe you can see that there, right?
Okay, so, I figured what I would do is
try to talk about Model-View-Controller
and give you "The 10 Best MVC Tips Ever."
Okay? Again, tie this back to the real world.
And, again, the goal here is to give you some tips
that will help you make your software
flexible and keep it easy to change.
And, again, the goal being for you to
develop great apps, as you have been.
And, of course, we'd like to see more
and more great apps, as time goes by.
Okay, so let's jump right in.
So, #1, the #1 tip is to Learn
Model-View-Controller for iPhone OS, or iOS.
Old habits are going to die hard, so forgive me for that.
So, learn Model-View-Controller for iOS.
And this is all about learning the common conventions, as
they are applied in iOS, and learn about how they're built
up from other design patterns, which, again,
are very, very common throughout the OS.
And so, first of all, I'd like to focus
on the connections between the objects.
So, going back to this diagram, you'll see that,
right, you're going to focus just on those connections,
how things move around, how the updates and changes
circulate around this Model-View-Controller system.
Okay? And the conventions that are used in iOS for that.
And there are several, but, primarily, I'm going to call out
three of them: Target-Action, Notification, and Delegation.
Okay, so what are these?
Target-Action.
Now, a lot of these are to promote
loose coupling among software elements,
to allow you to tap into the functionality made available
by the OS classes in the APIs or that subclassing.
Here is a concrete example.
So, if you have a button in your application, you don't need
to subclass the button to get custom
button-clicking behavior.
You can set a target that implements a method, and basically
that target tells it, "But, hey, when you're tapped,
call on me, and I'll go and take care of that custom
button-clicking behavior action afterwards stuff."
Okay? So, again target-action, something that's very common.
Another thing that's common is the notification.
This is using the keyboard.
When the keyboard comes up onto the screen,
of course, you don't have control over that.
That just happens when the system detects that text
entry is going to happen, and so the keyboard sends
out this notification, so that you can do
things like, perhaps, resize some views
or perhaps initialize some objects, which
might validate some text, things like that.
And so notification is, again, kind of this one-to-many type
communication between objects in the system and your code,
and, primarily, that's NSNotificationCenter
takes care of that.
Okay, I'd like to spend a little bit
of extra time talking about delegation,
because it's going to come out
many, many more times in the talk.
So, let's talk about delegation, another common
pattern which is used throughout the system.
So, when a user taps on the return key
in a text field, the text field goes over
and asks the delegate, "Should I end editing?"
Should I actually, right, take the insertion point
out of the text field and make the keyboard go away?
And the delegate, in this case, says, "Yes."
And this way to link your custom object up to a text
field in the system is done through a UITextFieldDelegate.
Well, many, many classes in UIKit
implemented Delegate to do work of this kind.
Okay? And there's a really good way that you
can detect, even when you're reading code.
Maybe you've downloaded some sample code,
and you're trying to understand how it works,
so that you can sort of implement some of
that same functionality in your program.
These will/did/should methods are sure signs
that you're looking at some delegation going on.
So, an example.
In the UIApplicationDelegate, the application says,
"Oh, I'm about to resign," and it will call out to some
of your custom code, if you implemented
applicationWillResignActive.
Right? The next one is a scroll view.
When it zooms, when you pinch onto a scroll view to
zoom, you'll get a call back on your code, if you wish,
if you implemented this method, scrollViewDidZoom.
And another example, you click on a clear button
in a text field, right, you'll get a call out just
to determine whether or not the text
field should actually clear its contents.
So, again, these will/did/should methods are used really,
really quite a bit throughout UIKit and the UIKit classes.
And, again, the idea for this is to keep
your code flexible and easy to change.
Keep things from getting all balled up into one big
class that does everything, but keep things parceled
out among different classes, so
that you can keep your code flexible
and easy to change This will be a theme I'll be
returning to several more times during the talk.
So, okay. So, a little bit of a look at how these
classes link up with their updates and changes.
Right. So, learn MVC for the iPhone OS.
Of course, there's a lot of great documentation
about those subjects in the iPhone Dev Center online.
So, if you want to learn more, that's the place to do it.
Okay. So, #2, so now Use MVC to Divide Your Work Up.
And so, you know, a lot of times, when you
have your application, you've got a big idea,
and perhaps it's even too big for
you to work on by yourself.
So, you have to sort of divide up the work
and figure out how to make manageable pieces
that will all communicate together to
implement this big idea that you have.
Well, how does MVC help you do this?
And I like to think of it as it
gives you a set of useful buckets.
Right? So, going back to this diagram, again, we'll focus
in on the big boxes, the actual Model-View-Controller.
Right? So, a Model, I like to think of Model as being not
just about data, which is sort of the most traditional way
of thinking about what a model is, but also
algorithms and perhaps also networking,
things that are beneath the level of the user interface.
I like to think of all of that as the Model.
View is all about display event capture,
not event processing, but event capture--
I'll return to that again later-- and also
visual appeal, making your application beautiful,
so that people love to look at it, love to use it,
and they'll tell their friends, and
they'll go download it, as well.
So, that's what the View is really, primarily,
focused on doing, and then the Controller is, again,
all about coordination, things like delegation and odd jobs.
So, let's look at a bit of an example,
at sort of a concrete example
of how these things might actually fit into a real program.
Let's just say that I want to make a
codebreaking machine, take some ciphers in
and try to figure out what the plaintext says.
So, you might imagine that my model would
have features and functionality for dealing
with ciphertext, with plaintext, and cryptography.
And a lot of times, the custom work of your
application, the real app-specific stuff that's going
to differentiate your app from other
applications, is focused in on the model.
That's where a lot of your custom
wonderfulness is going to get added, right?
Now, when you go to the view, you might, you know,
imagine that such an application might show you a bunch
of ciphertexts in a view, might
give you progress indications,
things like that, add and delete buttons, and what all.
Now, in a lot of cases, you don't want to
necessarily focus a lot of your work here.
Instead, what you want to do is lean on UIKit as
much as you can, particularly a program like this.
You probably want to focus in more on that model,
on that crypto behavior and less on how things look.
You probably want your application to
look standard, so people are familiar,
by just looking at it, about how it might work.
So, again, lean on UIKit as much as you
can for common view-related behaviors.
And in your controller, well, your controller does, again,
several different jobs, thinks like startup and shutdown,
if it's an iPhone application, managing the
navigation from one screen to the next, and,
as we've already talked about,
mediating between models and views.
And so your job here is to match the right controllers to
the right job, and I'll talk about that some more later.
Okay. So, trying to think about your application, use
Model-View-Controller to think about your application,
divide out the pieces, so that you can
just manage the big idea that you have.
Okay. So, use MVC to divide your
work, to implement that big idea.
Okay. #3, Don't Fight the Framework.
So, color inside the lines.
I was always bad at coloring inside the
lines, as a kid, even when I wanted to.
But the idea is, you know, to make
the framework work for you.
So much time and effort has gone into making these
frameworks, hopefully, easy to use and feature rich.
So, really try to make that framework work for you.
Leverage it.
And so let's kind of start with the don'ts first, okay?
Don't fight the framework.
Don't misuse framework classes.
Now, one of the most common patterns that you'll see
in iPhone development is the use of
UIViewControllers to manage a screen.
Well, that UIViewController manages
a view, so don't take that view out.
Stick it someplace else in your application and
just sort of forget about the UIViewController.
That's not the way that it's intended to be used.
Keep these.
Let the UIViewController help you
manage the lifecycle of that view.
Right? Don't decouple these and
just use one and forget the other.
Right? Don't misuse the framework classes.
Try to learn how they can help you, and then let them.
Right? Don't re-implement framework classes.
If you need a split view, use UISplitViewController.
If you need a scroll view, use UIScrollView.
Some of these classes can be really,
really tricky to implement,
and you don't want to be spending your time doing that.
You should really be spending your time making your
application differentiated from everybody else's out there.
Make it wonderful.
Don't spend time doing work that we've already done.
And this can sometimes be difficult when
you go and you download an application,
and you see a feature that you like, and you're
not necessarily sure how it was implemented.
If you find yourself spending a whole lot of time
implementing what seems like a core piece of user interface,
a core widget, step back and ask yourself, "Boy, is there
some way that I can lean on UIKit to get this functionality
without doing the work to make it happen?"
So, don't re-implement framework classes,
and don't make trivial UIKit classes.
Use delegates and notifications to try to
learn how you can couple up a piece, you know,
to match up a piece of your custom
code to the classes already in UIKit.
Yes, there are times when you need to
subclass a UIKit class to override a method,
but be careful that you don't cut off the core
functionality by perhaps not calling super and just sort
of taking the work onto yourself to really
make the class work, as it was intended.
And so don't just, again, subclass to make
a trivial subclass, when you can avoid it.
Okay. Again, the idea is to make the framework work for you.
So, what are some positive examples of that?
So, going back to this codebreaker
example, if you've got a model,
and you need to deal with pieces
of text, well, use NSString.
Right? That's what it's there-- I mean, it's
a completely full-featured string class.
Don't re-implement your own.
Don't try to go out and make some kind of special
implementation, because you think you need one.
You probably don't.
In terms of crypto, don't invent your own crypto!
Right? Use the Security framework.
Learn what the framework, learn what the
whole OS and the set of APIs give you, again,
so that you're not spending a lot of time implementing
complicated functionality that already exists.
In terms of the view, I mean, again, you can imagine
use table views to show lists, and use UIViewController,
as the documentation suggests that you should.
Again, don't re-implement.
Don't make custom buttons.
Try to use what the framework gives
you to do those common interface jobs.
And in terms of controllers, for startup and shutdown,
we kind of have this AppController
design pattern, you might say.
I'll talk about that a little bit more later, in
terms of starting up and shutting down your app.
Again, for navigations and transitions,
use NavigationController.
And for mediating between a model and view, really try to
figure out how you can hook your custom controller behavior
in at the right place, calling the right delegate
methods to really mediate between models and views.
And, again, later, I'll talk about that a little bit more.
Okay, so don't fight the framework.
Really lean on it.
Be aware, when you feel yourself starting to invest
a lot of time in a place that isn't really specific
about what makes your application special.
So, Don't Abuse Views, #4.
Views don't own data.
Views are about data display and event capture.
So, this is the time in the talk that, if you're just
a little bit early, maybe you were out, you know,
late last night, and your buddy's falling asleep.
So, you know, give him an elbow.
You know, wake him up, because if you remember one
thing about the talk, it's that views don't own data.
I gave us the double Family Feud strikeout here, for those
of you who are old enough to remember what that might mean.
I'm not going to be kissing anybody,
though, so don't worry about that.
So, but, okay, so you say, okay, views don't own data.
What do I mean by this?
Well, what you might be thinking is that,
well, views display data, don't they?
So, what is this point all about?
Well, the point is that the views don't own the data.
They're not the place that-- the views aren't the
objects that are really responsible, ultimately,
for owning a piece of data in your system.
And so here's the case against-- here's why not.
So, I think it's a slippery slope.
If you put views really in charge of owning a piece
of data, then you might be tempted to put a little bit
of data change methods into that
view, and now you're really--
now your view is also sort of acting
like a controller for that view.
Maybe it's doing a little data
validation, and it's a slippery slope.
I mean, you know, you'll wind up like a drunk on the street.
You know, it's like, "Oh, poor guy.
What happened to him?"
"Well, he put data in his view, and
it was all downhill from there!"
Right? [laughter] Okay.
So, but, you know, the other thing that it does, one of
the things is it locks you into a view implementation.
Sometimes, you might realize that the best way to take
your iPhone app, your iPhone and iPod touch app and move it
to the iPad is to make a different
view that does a similar job.
Well, what if you've got the data owned in this view?
The iPad, do you make a copy of that data?
How do you keep those copies in sync?
It can really, really lock you in and force you to make
some difficult decisions that you don't want to make.
So, keep that ownership of data out of the view,
because, again, it gives you this tight coupling
between data and display, which you want to avoid.
And, again, you want to have models and controllers really,
really acting appropriately in
their roles in your applications.
So, where do they fit in?
Well, they don't fit in anywhere, if your
views are in charge of owning the data.
Another great thing that you can do-- just,
you know, kind of giving some positive advice--
if your views don't own the data, it becomes easier to
implement inspectors, which, of course, is now becoming,
you know, common and really possible on the iPad.
You'll see here that I've got a
piece of text selected and up.
There's a Toolbar, which is telling me the style
of the text, and then there's also a popover view,
an inspector showing me that same piece of information,
the same piece of information three
different places in the application.
This piece of information, this style
information is owned in a model object,
and these views are just getting a little piece
of it to customize their display appropriately.
And, again, you can vary the data
display much, much more easily,
if you're just dealing with a common
reference of that data that lives elsewhere.
You can also vary behavior when the state of a view changes.
Okay, this really talks about this idea of event capture.
When the state of your application changes,
your view captures the event in the same way,
but then it might be passing off the processing of
those captured events to, say, like a GestureRecognizer
to have different behavior when the
state of the view changes, in this case,
from a read-only to an editing mode for a particular view.
Event processing of the view doesn't change, but it's
just the way that the hooked-up GestureRecognizer changes
when the state of the application changes.
So, you can much more easily vary
behavior by just changing an object,
and the view is none the wiser, and it promotes reusability.
I mean, it would be sort of silly to have each one of
those text fields be some trivial, you know, subclass.
Oh, I've got a first name text view, a last name text view.
That's just not the way, really, to do it.
So, it helps to promote reusability, if you
don't have this data hooked up in your view,
when it comes time to maybe add another field.
Because, you know, your marketing department or your
management says you need one, you don't need to then sort
of loading up this view with all of these different pieces
of data that could possibly be represented in the view.
Okay, so keeping the data out of your
view helps to promote reusability.
Okay, so don't abuse views.
It's really about data display and
event capture, not data ownership.
So, #5, Plan for iPhone and iPad.
So, this is about dividing your code out into modules,
and it's really taking kind of this MVC organization,
MVC architecture and kind of kicking
it up to a higher level.
I'll show you what I mean.
Well, let's consider Mail on iPhone.
Of course, this application shipped in
1.0, nice, full-featured Mail application.
It's really, really, you know, great.
Full-featured Mail application you carry around
with you all the time in your pocket, right?
But what we realized pretty soon after we started developing
the system was that there would be other applications
which we also wanted to be able to send mail.
Notes and Photos are an example.
So, each of them have a little button there, which
allows you to send the note or send the photo by email.
So, how do we do this?
Well, we factored out the common
pieces, and here's how we did it.
We divided things up into three basic layers.
Down at the bottom, we have a message framework, which is
all about things beneath the level of the user interface,
beneath the UI, things about networking, and
protocols, and basic mail, objects like messages,
and subjects, and "To:" and "From:," things like that.
Okay? So, all the non-UI elements down at the bottom level.
At the middle level, we have UI elements, things
like Compose, Views, and lists of messages,
and controllers for mediating between
the views and the model objects.
And then all the way up at the top, we've got
the Mail, full-featured Mail application itself.
Now, so, I think you know where this is going.
We could just then extend out those two bottom layers and
implement the Notes and the Photo application up at the top,
right, just having the custom parts in that top
level, which are specific to those applications,
sharing as much as we could at the bottom two levels.
Along comes the iPad.
So, what to do?
Well, the decision was made to make great
new versions of these existing applications.
They're really not re-implementations or sort of a
resizing of the versions that we have on the iPhone.
We really rethought how these applications should
work and how they might work best, given the iPad
and the iPad hardware, and the bigger screen.
And so we also realized that there
were parts that we could really reuse.
There were elements that we could pull out and reuse.
This is, you know, a prime example of real world code
reuse, whereas, you know, you look at that Compose,
and you can really see that it is
the same, only a little bit larger
and shown in a popover, instead
of taking over the full screen.
Okay. So, now, taking this iPhone architecture,
moving it to the iPad, well, what we did was this.
All right, so I'll kind of do that again.
So, we have the saints.
Look at those two bottom layers.
The bottom level, the message framework,
stays absolutely the same!
It's not involved in anything that
would need to change for the iPad.
Mail protocols don't change.
Right? We still need mail messages, and
message bodies, and things like that.
Stepping up to the second level, you'll see that,
yeah, there needed to be some iPad additions.
We had a couple of new user interface elements,
a couple of different kinds of interactions.
So, yeah, we did need to make some additions there
for the iPad, but most of it stayed the same.
Again, like the Compose view is a great example of that.
That didn't change.
That class didn't need to change.
It needed to be resized, but it didn't need to change.
And then, of course, at the top, we re-implemented
full new versions of these applications for the iPad.
And so, if you're thinking about doing this,
if you've got an iPhone application now,
and you're trying to ask yourself,
"Well, how can I move this to the iPad?"
or you're planning a new application, and you want to
target both of these form factors, iPhone and iPod touch
and the iPad, this is a pretty good way to start
thinking about how you might structure your code.
Again, this is real world advice.
This is how we did it, and it took
some back and forth to really come
up with this architecture, and it
really wound up working for us.
So, now, you may be asking yourself, "Real world?
I want to make a framework."
Well, there are no third party frameworks.
You can't make your own frameworks.
Right? Well, there are still ways to sort of
implement this same model, this same concept,
but you would just simply have to come up
with an implementation strategy that works--
static libraries and some very simple code sharing.
By code sharing, I don't really mean a copy.
I mean, it's just really sort of an Xcode reference
to the same code file in two different targets.
Okay? So, if that's total gibberish to you, I'll even just
say right now that I'm going be down in the lab session
after this session ends, and we can talk about how
you might actually do this, if you're interested.
Okay? All right, so, planning for iPhone and iPad, dividing
your code up and coming up with this higher-level design
that will help you keep things parceled out in a good way.
So, #6, Strive for Loose Coupling.
And, again, this is all about keeping your code
flexible and minimizing mutual dependencies.
In some way, you know, I'm kind of
standing up here now thinking about it.
I mean, really, if I had to choose out what would be the
second most important thing, this really might be it.
I'm constantly, constantly worried about
keeping the code flexible and easy to change.
If I think of one thing I think about more
than anything else, when I'm writing code,
it's how can I keep it flexible,
so that it remains easy to change?
Okay, so now, what is this loose coupling all about?
Okay, so if you look at this architecture, sort of
a little reimagining of the architecture diagram
from before-- and, of course, this is in concept.
Now, if we were to take, like, a real application,
and sketch out what all the pieces might look like,
it would wind up looking somewhat more like this, right?
It's pretty complicated, right?
Now, just think about if you wanted to pick
out a box, pick out which box looks, you know,
like the nicest one to you, and
think about changing that box.
Every arrow that's going into or out of that box
might need to be looked at, if you're going to change
that object to perhaps add a feature or fix a bug.
There's lots of implications.
The more arrows coming in and out of
the box, the more difficult that box,
the object represented by that
box, is going to be to change.
Okay, so here's some strategies for taming all
of those arrows, so, striving for loose coupling.
First of all, don't skip layers.
Use controllers to coordinate messages from models to views.
Right? That's one way to tame things, one
way to keep things more manageable, again,
using features like delegation, notifications, and so forth.
Don't message directly from the
top level to the bottom level.
Don't mix MVC roles in one object.
Again, this is primarily about views don't own data.
That's where you might be most sorely tempted to do it.
Avoid gathering too much work in one place.
I mean, in some ways, if you think about it, if the work
is inside of all one box, you wouldn't have any arrows!
But that wouldn't be really a good idea either,
because then changing that one object
becomes all that much more complicated.
That's almost sort of like hidden complexity
that you would need to worry about.
That doesn't show up on such a diagram.
And, finally, you know, don't declare
model data in your view classes.
I think, yeah, I think we've been over that, right?
Okay. So, some strategies for taming this.
Here's an idea I like to think about a lot.
I mean, if we focus in just on sort of a piece of your
model that may need to communicate with another piece
of your model, I like to avoid this bidirectional messaging.
A lot of times, I try to think about
a model object as being the one sort
of in charge of talking to another model object.
So, this model object might push data over.
It might call, this object might
call a set method on this one.
And if data needs to get from this
object over to this object,
I'll have this one call a get accessor and pull it over.
Again, so now, if I want to change
this object, and put another one in,
I sort of know where all the touch points are.
I know that this object is going
to need to be the one to change.
Right? If I want to then maybe change a different object,
I know that probably this one doesn't need to change,
because it's just having methods called on it.
I don't have to worry about it, if this one goes away.
Does that make sense?
I hope so.
So, avoid bidirectional messaging.
Something else you may need to
do is support multiple updates.
Like, if you think back to that inspector example, right,
a single change to a piece of model data might need
to be reflected in several places in the user interface,
and key-value observing (KVO) is a great way to do that.
I could talk for a whole hour on KVO,
but, of course, there's not time for that.
But, basically, what this does is it sort of breaks these
connections and makes a little transmitter and receiver.
Again, so, now, this direct connection is
broken, and now you have a much looser coupling.
Right? Where removing one won't
necessarily mean that you need to worry
about that connection being fully broken for everybody.
Next, look at view and controller.
Lean on delegates and target-action,
as I've already talked about, right?
A good example of this is that if you have a view
that's a control, you'll get a didChange method
or perhaps a UIControlEventValueChanged notification.
And, really, what that will help you to do-- I mean, this is
going to be kind of a small change to the slide, but that,
really, that blue arrow from the view to
the controller really sort of goes away,
because it really then becomes more sort of system managed.
It's not a piece, not a kind of communication now that you
really need to worry about, kind of in a first class way;
again, sort of lessening the burden that you have in trying
to keep this entire message graph
managed and sort of in your head.
Okay, and the last one in this little section is sort of
limiting the number of connections to and from controllers.
Again, I'm urging you to use controllers to keep things
all managed, but you really kind of want to try, also,
at the same time, to limit the number
of connections to a specific controller.
And the big challenge here is to decompose
controller work into the right number of controllers
and types of controllers, and I'll talk
to you about that a little bit later.
So, now, if you employ some of these strategies
that I just talked about, you could take a graph,
with all of these arrows flying around here, and make
it something like this, which is more manageable,
easier to change, more flexible, more loosely coupled.
So strive for loose coupling in your program.
Lean on delegates, KVO, technologies like KVO.
Avoid this bidirectional messaging, and try to limit
the number of mutual dependencies between objects.
And so that's #6.
#7, Choose the Right Data Model.
And so the iPhone,OS, iOS, gives you many, many options,
and so your job is to really find the right fit.
Find the right way to represent your data in your programs.
And so I'll just give you a minute to read that over.
That's a lot of text for a slide, isn't it?
Okay, I mean, that's kind of important, right?
I mean, if you're, certainly, if you're into the deep
data, but I mean, is it really sixth normal form?
Really? I mean, is that something that
you're all going to be worried about?
I mean, again, some part of the real world, you know, is
interested in modeling data and all of the theory behind it,
but, really, mostly, I think, what you're going to be
worried about is taking your collection of objects,
not really worrying about sixth normal form quite so much,
worrying, instead, about how you got this graph of objects
at runtime, how you're going to get them saved out, and then
how you're going to get them back into your program later,
if your user launches your program again,
or the device restarts, what have you.
So, this is what you're really worried about, I think,
probably, in kind of the real world sort of way.
So, let's talk about the different data model concerns,
the different things that you may need to think about,
while you're trying to balance these
tradeoffs between the different kinds
of the different choices that you
have for modeling your data.
Well, there are lots of them!
Things like, maybe, if are there modeling tools available?
Am I going to be using SQL?
Do I need transactions for complicated
model transformations?
What about versioning?
Maybe you already know.
You're trying to maybe get an application
out there, a quick 1.0 version,
but you already know what's going to be in the 2.0 version.
So, think about how to transform that
data from one version to the next.
Perhaps you're bringing a program
over from a different platform,
so perhaps you might have a large
collection of legacy data already.
And there's speed, and I/O, and
scaling, and all kinds of concerns.
So, I'm going to try to help you, give you some
tips for sifting through the options that the APIs
and the OS gives you to find the best choice for you.
So, here are six different options.
Property lists, archives, custom files,
using Server/Cloud, SQLite, and CoreData.
These are all, depending on your situation, any one of
these or some combination might be the right choice.
But I'll tell you what is not the right choice,
most times, is using defaults or preferences.
It's almost always the wrong tool for the job.
Well, when is it the right tool?
Well, if you can think about it, say, look at the Settings
Panel test, if you've got a piece of data in your program
that you think the best place to expose that piece of
data is in settings, if you have an advanced on/off,
if you have some feature that might be
on or off, then probably user defaults
and preferences is the right place for it.
If you can't put that piece of data in
your settings panel, then user defaults
and preferences is almost certainly the wrong choice.
So, what are some of the right choices?
Property lists.
Property lists might be the right choice.
One of their great virtues is that they're really,
really simple to use, particularly if you've got--
if your data is expressed in strings, numbers,
arrays, dictionaries, the small number of core classes
and data types that can be written
out directly to a property list.
If this is what you have, then property
lists might be a great choice for you.
I mean, just thinking, like, if you had a Twitter client,
for instance, sort of user name, you know, server,
perhaps the people that you're following.
Yeah, all of that data can be expressed pretty
directly using strings, numbers, arrays, dictionaries,
so forth, and they're very, very simple to use.
Archives are also very, very simple to use.
Let's say you have a custom object,
and it's, maybe it's a collection.
It's a composition of several different data types.
Maybe some of them are custom.
Some of them aren't.
Perhaps it's your object is the head
of a more complicated object graph,
and you just want to get that object written out to disc.
Well, archives is a great way to do it.
You implement these two methods, and you can
read that object out, and read it back in.
One of the great advantages of using
archives is that it supports versioning.
So, if you already know that you need to add a
feature later, you need to add a data element later,
archives can help you support that and
have your data be sort of both forward
and backwards compatible, and it
also supports transient data.
So, if you have data in a particular object that doesn't
need to be saved out, you also have control over that.
Custom files.
Of course, writing applications for iOS,
you get a little slice of disc space
where you can write your application
data, so you could just open up that file.
And perhaps if you already have some legacy code
or data, again, if you're bringing an application
over from another platform, you might have
already done most or all of this work.
This is really probably-- custom files
are really almost best when that's true,
when you're just going to be tapping
into work that you've already done.
And so your job in bringing that code over to the iOS
is probably going to be building an NSObject-based graph
out of those objects, which may, perhaps it might be
easier, if it's a Mac application that you're bringing over.
But, if not, again, that's going to be your main
challenge there, building up that NSObject-based graph.
So, you could also use the Server/Cloud.
I think a really good example of this
is if you're implementing a game,
and you've got a high score list that you want to save.
A great example of using Server/Cloud--
we do give you some help to implement this
with NSURL classes, other lower-level networking classes.
Of course, the server is up to you.
There's not going to be much we can do.
Even if you come to the lab later, you can plead with me,
but I don't know how much we're going to be able to help,
when it comes time to implement the
other side of this data model strategy.
Okay, so, coming back onto the
platform fully, SQLite is a really,
really great library that we've got available for you.
And it's really great, if you're familiar
with SQL, you can just go in there
and start using the features available in this engine.
Your challenge almost certainly is going to be coming up
with a strategy for doing an object/relational mapping.
Now, I'll have to admit, yes, my name
is Ken Kocienda, and I have a problem.
I've implemented my own object/relational
mapping strategies.
I've written languages to do it, and I've used-- right.
I mean, I'm sorry.
I mean, it's like the first step in the 12-step program.
I don't know how many of you out there have done that, too.
And this can be tricky.
You can wind up spending a lot of time doing this.
It can wind up working great.
You can come up with custom caching
strategies, custom object fault strategies.
You can do all sorts of really, really neat things, if you
invest the time and effort to write this mapping layer.
Moving on to CoreData.
CoreData is great, because it gives you this wealth of
features, and you can use SQLite as the backend data store
for CoreData, and it really does solve this problem
of making an object/relational mapping strategy.
The work is already done for you.
Now, the thing about using CoreData is it's going to take a
little bit of investment to learn how to use it and to find
out about all the great features that there are.
But even so, I strongly consider that you invest this
time, particularly, perhaps, if you're already familiar
with the platform and, perhaps, you're looking for a
way to maybe bring, you know, take your next application
to the next level, or you already know that your
next application is going to be using a lot of data.
I really, really strongly urge you to consider CoreData.
So, why? What are some of these features?
There's modeling tools and very,
very simple saving/restoring.
You could query to get portions of
your object graph returned to you.
There's support for undo, which is
really, really terrific to support.
Again, for the partial object graphs,
reading in a portion of an object.
Perhaps you had a blob object that refers to a large image.
You want to show a list of images.
Well, you don't need to read in all
of the large data for the image.
You can just read in the image, metadata and only fault
in the large image, when it comes time to display it.
So, there's many, many wonderful features
available in CoreData, if you learn how to use them.
So, again, I hope that gave you some
idea about the choices available.
And, again, if you have more questions, based on this, I'll
be in the lab afterward to answer any questions you may have
about choosing the right data model and all of the options.
Okay, so, #8, Decompose Controller Work.
This is all about coming up with the right number of
controllers for your system and learning something
about the special iPhone OS or iOS-- again,
old habits die hard-- iOS controllers.
So, again, going back to this graphic.
But now look at what's happening.
So, now this really ties into that typical iPhone type
navigation from one screen to the next and then back again.
Now, a lot of times, I mean, the easiest way to
implement that is having one UIViewController per screen.
You have kind of like this little sandwich with a view
on the top that's being managed by a UIViewController,
and then the model data to support the
data that's being shown in that view,
and that you have one of those per screen.
Now, if you think about taking that over to the iPad,
you might be tempted to make just one controller,
because now all those views can
fit on the screen at the same time!
And I think you know where I'm going with this.
Don't do that!
Avoid making an everything controller!
Right? You know, super.
You know it's like when you start thinking of a name.
What am I going to call this?
A super king daddy everything controller!
No. [laughter] Right?
That's a bad, bad idea.
That's a sign.
Right? When you can't think of a name for the thing,
and it starts getting into this manager, super manager,
that's a sign that you're coupling things too tightly,
gathering too much functionality into one place.
So, let's look at a concrete example.
There's a real world implication
of what that might be to kind
of start gathering all of this
work into a single controller.
So, let's see, we've got these two views, and
they're both pointing to the same controller.
So, now I'm going to change the views
to actually specific view types.
One of them is the scroll view,
and one of them is a table view.
Now, table view is a scroll view.
Table view is a subclass of scroll view, so now,
if you had a scrollViewDidScroll method implemented
in this one controller, you would probably
need to write a line of code like this.
Okay, well, all right.
So, if the scroll view is the table view, then I need
to do the table stuff, and if it's the scroll view,
then I need to do the scroll stuff, scroll view stuff.
Basically, I mean, I almost think that
we should, like, have a certain, like,
a special feature of the OS, maybe a new Xcode feature.
There's all these wonderful new features in Xcode.
Maybe I'll write about for the next version.
It's like when you type this kind of question, there'd be
like a little shock coming back from the keyboard, right?
[laughter] This kind of class is
almost never the right choice, right?
It should probably complete autocomplete
in red or something like that.
I don't know.
So, when you do this, don't have
class checks in your delegate methods.
Another variation of this is that if you have two table
views, you might actually be checking the identity
of the view that was passed in, while if
it's the foo view, do the, table view,
do this; if it's the bar table view, do that.
Again, that's perhaps too tight of a coupling.
You might want to make two different
controllers, one per table view.
And absolutely avoid class checks in your delegate methods.
Obviously, if you were to add another table view,
this would probably fail, and you'd need another layer
of "if" statements inside of that first block.
Again, that's almost never the
right way to structure your code.
Keep your work parceled out, again, like this, even on iPad,
even if there's enough room on the
screen to fit all of your views.
But now there's controllers play other roles in the system.
I mean, kind of to show you, going back to this, I'm
talking a whole lot about how the controller sits
between a view and a model and the relationships there.
But, again, these controllers,
they also play different roles.
So let's take a look here.
This is my coolest slide, so watch this one.
Isn't that cool?
I want to do that again, because I like it.
So, here's how you've got the application controller.
So now focusing in, seriously, what's going on
here is if you create a new project in Xcode,
and if you use one of the custom templates-- excuse me--
one of the default templates for an iPhone or iPad project,
you're going to get, as part of
the template, one of these objects.
The name of this project is Bugspray, so I get a
BugsprayAppDelegate class given to me by the template.
And so this is the application controller.
And so what is the job of this object?
Well, it's the delegate of the UIApplication, because,
most likely, you don't need a subclass of UIApplication.
You can tie into the delegate methods that UIApplication
will call on its delegate to do important work,
like starting up and shutting down your application.
You might also think about it as the app controller
is the root object in your entire object graph.
If you were to follow the parent link, starting from
any object in your system and follow who's the parent,
who's the parent, who's the parent, who's the
parent, most likely, you're going to get back
to the application controller as
the root object in your system.
And so its job is to basically get things kicked off
and to turn over control to the appropriate controller,
perhaps like if you have a view on your iPhone
application, it's probably going to turn over control
to a navigation controller and a UIViewController, something
like that, that's a very common pattern, very typical.
So, again, this application controller
has this special role in your system.
It's not really hooked up in a one-to-one way
to a single model and a single view, although,
now here's one thing that it sort of might be hooked
up to through an object like NSManagedObjectContext.
If you do decide to use CoreData, if you follow
my advice, if you already are using CoreData,
you're familiar with this object, NSManagedObjectContext,
and it's responsible for saving objects out to storage
to write fetches, to retrieve objects, and
create and delete objects, and so forth.
And so, again, it's a controller
that really just manages a model.
It doesn't have a connection to a view.
I'll have to make a little digression here.
I noticed there's a typo in the slide.
The joke is that this a model for a nuclear
power plant, and I misspelled electricity.
I hope that light doesn't cause a
meltdown or something like that.
I tell you, I hope not.
Probably want to keep me as far
away from nuclear power as possible.
Okay. So, #8, Decompose Controller Work.
Again, this is about coming up with
the right number of controllers.--
don't gather too much work into too few controllers--
and to learn about these special iPhone OS controllers
to help you tap into specialized functionality.
Okay, #9, Taking Charge-- Take Charge of Your Object Graph.
This one's real simple.
It's about ownership and lifecycle of your objects.
And so what I do, when I write
applications, is I come up with a set of rules
that seem appropriate for the app that I'm writing.
I'll give you an example of what a rule might be.
Well, if an object creates another,
if this object creates this object,
well then it's this object's responsibility to release it.
Very simple rule.
Another rule is children don't outlive their parents.
Sort of odd, if you were to match that up to the real
world, but inside programs, it makes perfect sense, right?
So, this object creates this one.
This object isn't going to go away,
while this one is still around, right?
This object is the parent.
This one is the child.
Right? This one is going to have
to go away at the same time.
Factory objects transfer ownership.
Sometimes you might have an object which is
responsible for building or creating objects, perhaps,
as a result of maybe some networking
that's gone on or whatever.
But it's probably a good idea to transfer the
ownership of those objects to a different one,
which in some ways is an exception to the first rule
that I just said, or actually the first two rules.
But, again, that's another rule that I might make.
But for those Perl programmers out there--
how many Perl programmers out there?
So, this should just give you a little
warm feeling in your heart, right?
TMTOWTDI, right?
There's more than one way to do it.
My set of rules for my application may not be
the same that you would use in your application.
Again, it's sort of part of your job is to understand
what makes sense for the app that you're writing.
And so here's kind of a closer
look at one of those sample rules.
Again, this idea that the model might create a
controller, and then the model owns that controller.
And so then, if it comes time for the model to go away,
well then they both need to go away at the same time.
That controller is not going to be around,
perhaps, with dangling pointers back to the model.
All right?
If the model is to go away, right, everything goes away.
So now expanding out that rule a little bit further.
So model creates a controller.
The controller might make the view.
The view might have some subviews.
Another rule that I might have is that models never
own views directly, except through controllers,
and that views never own model or controllers.
It's always that direction which is going from the
model to the view, that sort of ownership direction goes
from one side to the other and never back.
And, again, that might make sense for my application.
So, now, here's a kind of an interesting
question for a little bit of detail.
Delegates are unretained.
I mean, if you go and, particularly, if you're new to
the platform, and you discover this, and you see this,
you might say, "Well, isn't this a problem?"
I mean, maybe I'm going to have crashes in my system,
when these unretained pointers get
used after the objects go away.
Well, here's the strategy for dealing with that.
Here's why that's not a problem.
Because the owner is in charge.
The owner object is in charge.
So, now, if we go back to this object, and we say
that the controller is the delegate for that view,
if the controller says, "I'm your delegate," so now
the view, its delegate points back to the controller.
It's unretained.
What did you just do?
Well, you just avoided making a reference cycle, right?
And this is not a problem, because that view is
not going to live longer than the controller.
The controller is going to be around for the lifetime of
that view, so there's never going to be an opportunity
for the view to use that delegate
pointer after its gone stale.
Right? It's not a problem.
If you come up with these set of rules,
then think through their implications.
So, what if you have Nibs?
Again, the advice here is don't fight the framework.
So, some rules might UIView controllers own their views.
Split views own their master and
detail controller, view controllers.
Views own their subviews.
And UITextViews do not own their text.
I know that UITextView has a property on
it, but that's not where the text lives.
Make sure that that text lives in some model
object somewhere and that the text view,
when it's holding onto that text, is just displaying it.
So, a set of rules for taking charge of your object graph.
Again, you might need to think about sort of custom ways,
custom rules that make the most
sense for how your application works.
#10, Coordinate State Changes.
So, this is all about updating model after user
actions and updating views after models change.
Again, sort of, kind of going back through
this diagram that I referred to before.
Oops. So, going back to this diagram and focusing in on
those arrows, how those updates and changes-- wait a minute.
There we go.
Oh, it's just jumping straight to the next one.
Okay, well, focusing in on those arrows.
Well, of course, now the thing that you don't want
to do is have those arrows go directly
between the model and the view.
That's the wrong way.
Do not cut out the controller.
Well, why not?
Well, because your controller might
actually do some important work there.
A change might come in.
A user might interact with a view and create
a change that would go into your controller.
The controller might have some code, custom code to
reject, or delay, or validate a particular change.
Here's an example.
If you look at the Stocks application, that
text view, I just typed Apple in there.
The text view has no knowledge about what's going to
happen next, that there's going to be some network access,
that a table is going to be populated with a set of multiple
choices, and that the user is going to choose one of them.
That's going to be the responsibility of the
controller to then turn around and take that change
and commit it back to the model, as shown here.
It's the controller's job to take that change
and then go and tell the model about it,
that the user has actually made a choice.
So, that's handling changes the right way.
So, now, going the other way.
Let's say that-- let's go a different way.
This is all about, now, handling multiple changes.
Individual data element change, is there now maybe more
than one aspect of the user interface needs to be updated?
Again, KVO is a great way to actually make this work.
And KVC, key-value coding and key-value observing.
If you have more questions about this, we'll find
people who can give you some answers and some pointers.
Basically, this is all about using strings, rather
than direct method calls to go and change your model,
and you can imagine how those strings can be
varied at runtime programmatically, right?
That's sort of the basis of this technology.
But now, kind of showing a real
example, going back to this complex view.
So, I've got this Body 2, everywhere.
Let's go and say that I change this to
Heading 2, and now those other two places,
after that change, need to be updated appropriately.
Again, so this might start in the
inspector, flow into one controller.
That controller is going to tell the model.
It's going to then percolate these changes around, and
that controller at the top is actually going to know,
once the model told it, that it's going to change, it's
like, "No, no, I'm the one who originated the change,
so I'm not going to actually now start this infinite loop
of changes going all around," which can be a problem.
I mean, look, I've had bugs like that in my programs, too.
So, this is a way to sort of master that and prevent
this unintended set of consequences from happening.
So, MVC is the way to go to manage that.
So, coordinate state changes, updating
things as changes occur.
And so there we go.
There are the 10 best tips ever.
And so going over them again, learn MVC, use it to divide
your work, don't fight the framework, don't abuse views--
I'll know, right-- and plan for iPhone and iPad,
loose coupling, choosing the right data model,
controllers and object graphs, and state changes.
And it's all about keeping your code flexible and easy
to change, because we want you to make great apps.
So, here, there's a whole set of related sessions.
Fortunately, only two of them would
require you to time travel at this point.
So, which is nice, a couple of nice sessions on
CoreData and understanding Foundation later today.
And this session will actually be repeated.
If you liked it, or you want to tell your friends
about it, I'm going to be doing the same show again.
My devil twin will be on hand to give the repeat
performance tomorrow at 2:00 p.m. in this room.
That's all.
Thank you all for coming.
[applause]