Transcript
>> Chris Parker: Welcome to Session
111, the iPad Development Overview.
My name is Chris Parker.
I work on UIKit and this is, basically, we are going to
talk about how to take an iPhone application all the way
through to become a fully universal iPhone and iPad app.
So you have probably spent a bunch of time working on
your application, getting it really well tuned to work
on an iPhone 3GS. The screen size lends
itself to a particular style of application.
It tends more toward the navbar
style information navigation system.
You know, you push on something.
You push another view controller, you sort of drill
down into your data and you drill back out of your data,
and then we went and did something kind of crazy and came
out with this iPad thing which has a much larger screen,
and it lends itself to a different set of
behaviors for navigating user's information.
It has a much larger screen.
Those are actually proportionally correct, right.
So the iPad is, let us call it, you
know, sort of roughly 4 iPhones maybe.
But it does change both the way the user will
interact with the information in your application,
and some of the design considerations in trying
to share code for a universal application.
We'll talk today about some of the new UI
considerations of going from the iPhone to the iPad.
It comes down to things like the initial appearance.
What does your application look like when it first
launches, covering some things about rotation, transitions,
and managing sort of the information density and
presentation, and two of the things that we have introduced
for that are PopoverController and SplitViewController,
and we'll go into some detail on those.
The main thing that we're working on here is
going to be universal applications and the things
that you'll do in order to write for multiple SDKs.
So you'll make some changes in Xcode.
There are some API things to take into consideration
and you may have to think about symbol availability.
So when we talk about what symbols are available
across the SDKs, we'll get to that in a bit.
What does need to change in your project?
You'll need to make some Xcode project settings.
So the Base SDK, the deployment target,
the targeted device family will all have
to change and we'll see that in a minute.
You'll probably have to refactor some code, right.
It's very simple in some ways to wind up
with a design where you're taking advantage
of the fact you know everything that's on a navigation
stack and sort of crawl up and down that stack
to be able to find the view controller you want.
We'll talk about refactoring your code to try and make
it easier to share things when you start using things
like split view controllers, and the emphasis there
is going to be on controllers which send actions,
and then you're also probably going
to need to change your resources just
like with the iPhone OS and iOS 4, the iPhone 4.
You know, your designer is probably going to have to help
you out with getting some new resources either things
for a larger screen or new artwork
that fits the larger form factor.
So let's start out.
I just want to talk a little bit about
Popovers and Split View Controllers.
A UIPopoverController is not a UIViewController.
It's its own managing controller.
It's going to host a content view controller.
That content view controller has a view that it manages and
that content view controller, that is where you are going
to get the viewWillAppear, viewDidAppear,
messages, and things like that.
That popover controller is usually owned by some object.
It might be your application delegate.
It might be another view controller.
You know, it is something that is going to manage
the popover lifetime itself and you create these just
by calling initWithContentViewController.
For those of you who are in the Cocoa Touch
section, this slide probably looks familiar.
ContentViewController allows you to change
which content view controller is present.
popoverContentSize allows you to change the popover content
size while it's up so you can animate size changes for that
and the passthroughViews is a way you can use a list of
views that you'll pass this where the user can interact
with those views while the popover is up.
You present a popover controller
using presentPopoverFromRect:inView:
permittedArrowDirections animation.
That rect that you pass in is in the
coordinate system of the containing view.
So if you find that the popover is suddenly showing up
in some funny location, you probably want to make sure
that you've passed the correct thing and in a
lot of times, you'll be passing view bounds.
If you pass view frame, you'll get
some really funny, funny results.
PopoverArrowDirections allows you to
control where the thing gets laid out.
There is an algorithm in UIKit where we try to maximize
the area of the popover controller you're displaying,
and we'll figure out whether it
belongs on the left or the right,
or the top or the bottom depending
on what the content size is.
If you need to display this sort of bar button item,
we know that you can have the toolbars up on top
of the UIViewControllers at the top of the view, so you may
want to be able to display a popover from bar button item.
You do that just with presentPopoverFromBarButtonItem.
When you dismiss these things, you
will call the dismissPopoverAnimated.
We don't send the delegate methods
because those are for the user controls.
The delegate methods are popoverShouldDismissPopover
and DidDismiss, and the should is really both I'm
about to dismiss this notification and your opportunity
to decide whether or not you want to actually dismiss it.
I have just a little bright red square here.
That bright red square is actually
something that I can drag and it will return
to its center point, and let me take a look at that.
This is all of the code for that.
It's called an anchored view and all I'm doing
here is using a pan gesture recognizer to find
out where I've tapped in the view so I can drag it.
And as the gesture recognizer moves
through its states, I'm-- when I state--
when I get UIGestureRecognizerStateBegan for
the pan gesture recognizer, I am going ahead
and just setting its translation
and view to its own anchor point.
Its center, basically and this is just
set, it's sort of initializing the value,
and then in UIGestureRecognizerStateChanged,
I just set this view center to the--
the center here is the gesture recognizer,
translation view, self superview just basically says,
set my center to a translation that corresponds to
the self superviews that I move along with the finger.
You can do this in TouchHasBegun, TouchHasEnded,
TouchHasMoved, but you have to sort of compensate
for the location of the point difference between
the center and where the touch went down.
This is just sort of interesting
use of a pan gesture recognizer
that Josh Shaffer suggested that is working out pretty well.
So that is one of these anchored views here and these
3 buttons here, they all say pop on them are all set
to just popup a straight popover controller.
So if I could just take a quick look at that.
This is the people picker from address book so I have
just taken a stack pop navigation controller and put it
in to this popover, and as I click in different places,
you will see that the arrow winds
up pointing in the right place.
We make room for it depending on what's going on and right
now, I have all four of these on: up, down, left, and right.
Those correspond to the directions
in which the arrow was pointing.
If you look here for pop, right.
I could have possibly put the popover up over the button,
but the algorithm is showing us that we can lay it
out with just a few more pixels
all the way, top to bottom there,
and we also take care of maintaining an
appropriate margin around the outside.
So if you do set something that is really large or if your
view controller is very tall, we are going to shrink it.
The content size is more a recommendation
than anything else,
but for the most part, we're able to honor those sizes.
If I turn these off so, for instance,
let's turn off left and right.
Remember what happened when I tapped
in the sky at the bottom last time.
The arrow was pointing left, but if I
tap on it now, it is going to point down
and we actually move in to the target rect.
The target rect in this case is-the rect that
I'm presenting from is this popover view.
So when I do click around though, I can't grab this view
while the popover is up and that can be a possible problem.
I'm just setting up here in the viewDidLoad.
I just set up all of my little buttons and
everything, but down here in display popover,
I have a line here that I'm going to erase the
comment from where I'm setting the passthrough views
to this anchored view that I've set up in my
view controller, and now if I run this guy,
what I find is that I can pop up any of these popovers
and, you know, while I am popped up if I tap some place,
it dismisses except if I tap in this guy
here, and now I can drag that view out from
under the popover, and see what is there.
So you can imagine this might be a few that has content
in it like, for instance, the contents of a message,
and you may be selecting something to try and
have the user take an action using the popover,
but they can't really see what's going on underneath it.
So they might want to draw it out.
You can grab that and draw that view
out, and then when you let the mouse,
when you release the touch, it will go back to the center.
So those are sort of the basics of popovers.
So for UISplitViewController, SplitViewController
is a full screen view element.
It is itself a UIViewController subclass, right.
So this means that all of its containing views,
view controllers actually help change the
rotation behavior and things like that.
So a SplitViewController has a delegate.
It is going to inform that delegate about events
that happened as the device is rotating mainly.
The object at index 0 is generally the master
view controller and the object at index 1,
this is the view controller's property for split view
is generally going to be the detail view controller
and in this case, our application delegate
will be the owner of the SplitViewController.
This will be very common.
SplitViewControllers are typically the top level item.
You usually create these things from nibs, right.
You'll have a nib set up for your iPad.
You will drag out a SplitViewController into it and you
will get a full screen window that has everything in it.
If you want to create one programmatically,
you can call alloc init and you can also get
at the view controllers property and the delegate.
And again, the delegate tells you about what's
happening when the SplitViewController rotates.
So, I have downloaded this so we'll talk
about at the end of the talk but again,
these 3 delegate methods are
splitVewController, willHideViewController,
withBarButtonItem:forPopoverController.
This is the landscape-to-portrait rotation, right.
So when you go from landscape to portrait,
we're going to hide one of the view controllers,
that guy on the left at object at index 0.
And we're also going to give you a UIBarButtonItem
that you can put in your interface somewhere,
typically in the toolbar that's at the top, and
that will be what the user taps on in order to bring
down the popover controller that will have that
master view controller in it, pre-populate it.
And the popover controller, you'll get a reference
of that so if you need to dismiss it you can.
splitViewController:willShow
ViewController:invalidatingBarButtonItem,
that is going the other way.
That is going portrait to landscape and that means that the
view controller in the left side is about to become visible
and that BarButtonItem we gave you before,
yeah, get rid of that, it's no good to you now.
And splitViewController:popoverController:
willPresentViewController is
where you basically get told the users tapped on the
button, we're about to present the popover controller.
If you need to take other action when you're going to
present that then that's your opportunity to do so.
So we're going to use the CoreData Books example.
We have modified it slightly for this talk.
This is if you type pretty quickly you will be able
to get to the URL here, but you can search for it
on the sample code site as well and
we will have an updated version soon.
But the CoreData Books sample looks like this.
This is just a very basic application that
demos CoreData to manage a list of books.
If we run this, here is our iPhone
Simulator here and we have a list of books.
If I tap on a particular book I get a
view controller that gets pushed in.
It is a full screen transition.
You know, there is not much space on the iPhone
so we tend to write these kinds of interfaces.
And then when we tap on an author card we get another
view controller that gets pushed in, all right,
and the navigation controller is
keeping track of where I have been.
So if we run this app actually in the 3.2 Simulator
we get a small version of the CoreData Books app.
Actually, this is totally expected.
We're running this in compatibility mode so when you
run an iPad, when you run an iPhone app on an iPad,
we are going to go ahead and run that in a small
window and you know, you can interact with it
and you still get the same full screen transitions.
And then you can also zoom this up to 2x and you get the
bigger version but it's still essentially an iPhone app.
We are not really taking advantage of any of the things
that even scaled up here at 2x of any of the things
that really make it, the iPad a
really fantastic device to use.
One of the things that we are going to have
to do is start converting this project, right.
So, there as some Xcode project modifications we will need
to make, and there are some code refactoring we will do
and then we are also going to have
to come up with some new resources.
And the first thing we will do actually
here is work on converting the project.
So if we take a look at the resources and we look at
our info plist, there are a number of things here.
So we have the various keys that make up the info plist.
This is how you tell the application as we launch, this
is how you tell UIKit the various things about your app.
So this will include the main nib file base
name which in this case is called main window,
and that corresponds this MainWindow.xib file here.
The application requires whether or not it requires
the iPhone environment, that kind of thing.
So, one of the things that we will do is
take a look at the raw keys and values.
These are the actual info property list keys,
and if you have been editing these things
for a while, it should be very familiar.
The NSMainNibFile though is interesting
because if we pop up this menu here in Xcode,
we see there is also NSMainNibFile~iPad
and NSMainNibFile~iPhone.
And this is your opportunity to be
able to specify which nib you want
to load depending on what device you are going to support.
So to be able to pick a nib for
your iPad and nib for the iPhone
and when the application fires
up, we will pick the right one.
We could go changing this but there are
a lot of things we'd also have to change.
So this is the CoreData Books project build
info here and actually we could look at it.
Actually, let me look at the target
because that will be better.
What we see is the Base SDK here is iPhone device 4.0.
And the Base SDK when you are working with Xcode
for these things is basically the latest version
of the operating system that you would like to run on.
And if we go look for the deployment target,
we'll see the deployment target here is set
for OS 3.2 which is good for our purposes.
On a fresh project this might actually
have been set for iPhone OS 4.0.
And then if we look at the targeted device
family, right now this is set for iPhone.
And setting this for iPhone means it is only
ever going to run in the iPhone and if it runs
on an iPad it will run in compatibility mode.
And what we want to wind up doing
is setting that to be iPhone/iPad.
Let us make sure that this is all set up here.
And then rather than do all of this editing by
hand because the first time I did all of this
by hand I managed to keep missing things.
Xcode 3.2 has this nice feature where if we control click
on the target we can upgrade the current target for iPad.
And look at the big sheet that drops down and it asks that
we can either upgrade the current target for universal app,
runs on both iPhone or iPad, or we can
create a separate target for the iPad
and the device specific iPhone target gets left as is.
So you can either have 2 targets, 1 for
iPhone, 1 for iPad or 1 universal application.
We are going to go ahead and choose 1 universal application.
So, now a bunch of things happen sort of behind the scenes.
The first is if we go look at our info plist we
will see that we now have a main nib file base name
with main window in it and one that has MainWindow-iPad.
And in fact, Xcode went ahead and
created a resources iPad group
and in this group it is now also
thrown in a MainWindow-iPad.xib.
And this MainWindow-iPad is basically just a
big version of our original MainWindow nib.
So it has done a bunch of conversion for us.
If I look at the Base SDK I should see-- yup,
sure enough iPhone OS, iPhone device 4.0.
If I go ahead and look at targeted device family,
we will see iPhone and iPad and just underneath
that the iPhone OS deployment target is iPhone OS 3.2.
What happens if I run this?
Here is the iPhone Simulator 4.
That is not so bad, that is unsurprising.
It is sort of the same application.
Let us actually go ahead and run that in the
3.2 Simulator and we get a big iPhone app.
But it is really big and some of it doesn't work.
So you know, you click out here and you tap out here and you
can't do anything and you happen to get into the right spot
and these big sloshing full screen transitions happen.
Do not ship this.
[ Laughter ]
Don't do this.
What we'll do instead is start the work of sort of moving
everything into a 4.0 and 3.2 combined application.
So, one of the things that we need to do
is to refactor the application delegate
to really effectively take advantage of the iPad.
So when we created the new nibs, right, there is an
outlet inside the nib for your app delegate and you get
to choose what the class of the application delegate is.
But this system is going to choose a nib for
us based on what we have in our info plists.
So depending on what we tell the nib to
use for the application delegate class,
it will automatically pick either an
iPad-specific or an iPhone-specific app delegate.
Oh, kind of cagey.
The application delegate here for CoreData Books
has 5 things in it that are common pretty much
to every single application we
write on an iPad or an iPhone.
It has got the managedObjectModel,
it has got the ObjectContext,
it has got the persistentStoreCoordinator,
the thing that does all the real saving.
It has the applicationDocumentsDirectory which is
where in this case we are putting the
CoreData database and it has a window.
And also it's an outlet for navigation controller which is
great for the iPhone but it is not so great for the iPad
because that means we'd only be able to connect to one
of those 2 things and we actually need both of them
in order to write an effective application.
So what we are going to do is actually
hoist all of that stuff
up into a shared super class, an app delegate shared class.
And then we will go ahead and have a separate
app delegate iPhone and app delegate iPad class
and one of them will have an outlet
for the navigation controller.
And in the other, we'll have an
outlet for the split view controller.
And so now we have a class that gets automatically
chosen and instantiated for us at application launch
and this is an opportunity to start dividing
some of the UILogic in your application, right.
So we are going to move some of
that controller logic around.
It is really hard to avoid some of
these tightly coupled designs, right.
So on iPhone application, you have got the stack
view controllers on a navigation controller.
Sometimes everybody winds up kind
of knowing about everybody else.
And pretty soon you want to be able to reuse one of
these view controllers and you realize that reusing one
of these view controllers means I also
have to figure out how to reuse all
of those 6 other view controllers that it knows about.
And now I can't quite share code as effectively.
One of the things that we would like you to do is
think about intent, think about what is happening
as the user maneuvers through your application.
They are taking actions.
You are going to admit those actions and
you are going to respond to those actions.
So that target action paradigm that we used in UIKit and on
the desktop in the app kit is actually something interesting
to consider here whenever you are looking at
refactoring your code or even just writing new code.
And you really want to talk about trying to choose
your action receivers with some level of care, right.
If I have to have peers that know about each other,
sometimes that gets to be one of
those really tightly coupled designs.
So you want to try and choose the closest object in
your hierarchy which knows about all the players.
So, which in this case, we are going to use
our app delegate to manage some of this logic.
But the responder chain will be another
appropriate hierarchy for that, right.
So sort of 2 different parallel hierarchies, one has
to do with views and view controllers and one has
to do with just who is the first responder.
So in this instance, we are going to think
about using a view controller where when you tap
on a particular book we send a show book message to the
iPhone app delegate and the delegate is the one that is
in charge of creating a new detail view controller and then
filling that view controller in with the appropriate book,
and then we go ahead and push that on
to the navigation controller stack.
Okay, that works pretty well for a phone application.
What does it look like for an iPad app?
Well, we have got a split view controller
that is owned by the iPad app delegate.
We go ahead and tap on a particular book.
We send the same show book message to an app delegate.
It just happens to be different
app delegate in this case, right.
And then that app delegate turns around and sends set book
to the detail view controller that it knows is in place.
So rather than creating a new one we just go ahead
and set that on the receiver in the view controller.
So we are going to reuse this view controller and
rather than creating a new one and setting at each time.
And let me show you a little bit about
what some of that refactoring looks like.
So here we have our CoreData Books
that is running in the simulator.
We have gone through, we have our resources,
iPad and if I take a look at the main window here
for the iPad resources, we have the split view controller.
The split view controller has a navigation controller in it.
It also has a detailed view controller.
That is the guy on the right, let us open that up.
And you know, the navigation controller has
the normal nav bar and all that other business.
And we have got the root view controller is our
root view controller class here, which is this guy.
Sample code, sample code, sample code, but it is in charge
of basically handling the viewDidLoad
business and things like that.
And this actually is part way to
getting through the refactoring.
If I run this though I have not really made any code
changes but we see that if I try to rotate this it works
but I have got the view controllers so you
know, the guy on the left is not quite right
because the Edit button is not going to function
properly the way we want given an iPad interface.
This you know, well, I am still stuck in this.
So there are still some work to do here
but if you get to this point you know,
you are on your way, you are on your way.
I am actually going to jump to our fully
recooked universal CoreData Books example.
Nice high resolution large version here.
It still does the full screen transitions,
and if we run the 3.2 version here.
What we have is our rotation code here is all working.
We have got the various larger
resources as we tap on things.
We are only 1 tap away from the various
bits of information that we want to see.
And we also have a little popover here
for the author information which is kind
of appropriate for this kind of use, right.
It is a little transient popover controller for
just displaying some information, getting rid of it
but we don't have any full screen transitions kicking
around here, and I will show you how that works in a minute.
Let's take a look at the info plist here.
UI supported interface orientations here, one of them, the
fallback here is for UI interface orientation portrait.
For the iPad version, we've got all 4 of them here
and that's what allows the iPad version
to launch in all 4 orientations.
Actually, what happens if I go ahead and delete that?
If I actually delete that and then say
rotate the portrait, let's go home here.
So that ends the process.
If I build and run this again, suddenly the thing works
to portrait and shows up in this other orientation.
You actually want your application to
show up in all possible orientations.
So you want to make sure that your supported
interface orientations are all in your info plist.
The other thing you will want to make sure you do is your
view controllers all have to implement the rotation code,
shouldAutorotateToInterfaceOrientation.
So remember when you are working with the
UISplitViewController, the split view controller is going
to go ahead and ask its children whether or
not a particular orientation is supported.
If it is supported, return yes.
So for most of your iPad work you are
going to want to return yes all the time.
SplitViewController willHideViewController withBarButtonItem.
This is the delegate method I was talking about before.
What you will do here is just grab the title,
grab the bar button item that we hand you,
fill it in with the title you would like and go ahead and
make a copy of the toolbar items probably of the toolbar
that is in the view controller, the detail view controller.
Insert that object at index 0 and
that will be the first item.
In our case, that view controller has a spacer
in there in order to separate things out.
The set items for toolbar items animated yes, this
just goes ahead and puts that in a nice cross fade
and then we release the toolbar items here.
And we're also grabbing a reference
to the popover controller
so that we can dismiss it if we need to ourselves.
And we are going the other way, we are going to go ahead
and this is a splitViewControllerwillShowView
ControllerinvalidatingBarButtonItem.
We will grab the toolbar items here, that same
mutable copy with the object at index 0, there we go.
And set the items, go ahead and
release it and that's what enables all
of the button management in split view controllers.
It is something you do have to handle
yourself because we just don't know
where you want to put it based on your interface.
And then if we look at the detail view controller
here, again we also have our popover controller
and this guy just creates a popover controller and presents
it right from here it is authored text field bounds, right.
If I pass author text field frame that
would make it again wind up in a funny spot.
But all of this uses sort of the refactored model
of being able to send messages to the app delegate
and have the app delegate do the work of
positioning all of the various bits of interface.
So what changed?
Well, in the Xcode project we changed the Base SDK and
the deployment target and the targeted device family.
Actually, we didn't change it, we
let Xcode go ahead and change that.
For the refactored code, we're hoisting a
bunch of stuff in the application delegates.
We're using some of these actions sending controllers
so that we're not actually having these tight
relationships between UIViewControllers, you know.
Too many things knowing too much about each other,
there are some software design principles there.
And we actually had a number of different resources
get changed to nibs, images, stuff like that.
What didn't change?
Well, I didn't touch a single line of
code that had to do with the model.
And that is actually pretty significant.
If you're writing a universal application, you probably
don't want to be messing around with the model especially
if you're sharing the model between
the desktop and your devices.
The model, we make a big deal in all of our
documentation and all of our design and we try
to make our design principles follow this
with the model view controller paradigm.
By not, you know, by having all of this logic
up in the controllers in the view hierarchy.
We're really minimizing the amount of code, we're really
going to have to change in order to get our stuff to work.
So if you have too much tight control, tight
knowledge between the model and your views or the model
in the view controllers, that is going to be a problem too.
Ken Kocienda is giving a great talk,
Model-View-Controller for iPhone OS,
that's in Russian Hill tomorrow at 10:15.
He is going to go over a lot of the things that you can do
using model view controller and some advanced techniques
for managing how the different things work on iPhone.
So it will be a great talk if you need to brush up on that
or if you want to know some things that are particular
to designing your code and your software for the iPhone.
When we're working with SDKs and symbols, we sometimes
wind up in situations where you want to use a symbol
and it is not just available, or you want to
try and subclass something that does not exist.
So here are actually 3 examples of some
symbols that are available on the iPad,
on iPhone OS 3.2 and the iPhone on iOS 4.
So UIPopoverController, these symbols
are present everywhere, right.
But on iPhone OS 3.2 you can actually
use and create a popover controller.
But if we find that you are running on iOS 4 you can
instantiate one but will actually throw an exception
when that happens because the iPhone, the iOS
4 model on the iPhone, it is a smaller screen,
the presentation doesn't lend itself particularly
well to popovers or split view controllers.
And that is really not the way that the iPad really
can present your information to its best advantage.
So UISplitViewControllers also, when you create one of those
we go ahead and throw an exception if you are on the phone.
And gesture recognizer is actually usable in both places.
So you know, if you have been carrying
a bunch of code to do all kinds
of gesture recognition yourself,
you certainly can continue that.
You can fold that into a UIGestureRecognizer subclass which
is actually a great way to do it because then you can work
in conjunction with our UIGestureRecognizers.
You also may want to know in line what the device idiom is.
We have introduced some UI, API rather on UIDevice.
UIDevice.h has this user interface idiom, enum.
And the idiom is basically a way that we describe
what kind of user experience we're offering.
And in this case it's 2 enums.
The phone one includes iPod touches, right,
they're basically the same form factor.
They have the same kind of screen, weight, et cetera.
And the iPhone UIUserInterfaceIdiomPad.
The way you typically use it is you could
just check to see, hey we're on an iPad,
we have got the iPad user interface idiom, we'll
create a UIPopoverController and go ahead and use it.
Or if we're not on an iPad, an idiom pad type device,
we'll go ahead and just create an author view controller
and initialize it with an author and
push that on to the UI navigation stack.
This is a different approach.
Typically, you're, you know, probably
going to use the subclassing approach.
It's a little bit more flexible.
And if you find yourself sprinkling a bunch of if
user, UIUserInterfaceIdioms all the way around,
that is probably a hint that you want to
think about hoisting some of that logic
up into a shared controller with separate subclasses, right.
This is a macro by the way.
If you look in the UIDevice.h header the
UIUserInterfaceIdiom macro does a UIDevice response
to selector for the access or for this idiom and then calls
that and it returns that answer, and if it doesn't respond
to the selector, it goes ahead and just returns idiom phone.
And that's actually a really good technique also
in terms of dealing with symbol availability.
You're going to want to use NSClass from string to
find out if a particular class exists on a device.
You're going to want to use response to selector
to figure out whether or not a particular instance,
you can actually send that message to that instance.
So there is a sense of being able to test
for capability and not for version, right.
The interesting bit isn't the fact
that you're running on iPhone OS 3.2.
The interesting bit is that the particular
facility you are looking for is available.
So what do you guys need to do?
Well, you'll need to do some refactoring probably,
and that refactoring is going to involve some
of that hoisting of functionality up into superclasses.
It's going to involve some maneuvering of code so that you
have got sort of don't repeat yourself thing going, right.
Do not have multiple copies of the same code in one place.
And really try and take advantage of some of the things
that the application loading system is doing for you
and being able to change, choose nibs and things like that.
You're going to want to change up your resources, right.
A lot of the interface that we saw on the initial passes at
the universal version of the CoreData Books app, you know,
there are full screen and it is a big table view
that spreads across the detail view controller.
And what you really want is that nicer presentation
and you want to try and bring a lot of the information
on the iPad a couple of taps closer to the user, right.
If they had to keep drilling down even on
the iPad, there is probably something there
about how you've got your data
organized that isn't quite optimal.
And we do really encourage you
to write universal applications.
If you offer a universal application to your
users as an upgrade from an existing application,
a lot of that data is going to come along for free.
The user's data will probably get just pulled
up when they upgrade to the new version.
We also highlight universal applications
differently in the store, right.
So when you search for an application in the
store, the universal applications are listed first.
They also get a little plus indicator on their
price that says yes, this is universal application.
So, we have got Mr. Bill Dudney is our
Application Frameworks Evangelist, so thank you.
[ Applause ]