Frameworks • iOS • 1:01:52
The Model-View-Controller (MVC) design paradigm is pervasive in the Cocoa Touch frameworks. With the arrival of iPad and the need to display that same information (the M in MVC) in a new way, it is more important than ever to understand this fundamental pattern. Learn about the support built into iPhone OS for MVC, the implementation choices open to you based on the needs of your application, and how to use this design pattern to help you deliver code that works great on iPad, iPhone and iPod touch.
Speaker: Ken Kocienda
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript has potential transcription errors. We are working on an improved version.
[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]