Transcript
>>Hi everyone.
I'm Henry Mason.
I'm an Application Engineer on the
Application and Frameworks iOS Group at Apple.
And today we're going talk about future proofing your
applications, which is a pretty simple concept really.
We want applications written today to run
on devices that run iOS in the future.
So it means devices like iPad and iPhone and iPod touch,
we want your apps to run on future versions of those
and any other devices we have running iOS and we want your
applications to run on versions we haven't announced yet.
We work really hard to do this and we know you really
want that to happen and we really want it to happen
so we want a little bit of your help to make
sure this continues to happen in the future.
So what we're briefly going to go over is
some concrete examples of problems we've seen
which have caused compatibility problems.
So this means things that application developers have
done that have caused problems in future versions of iOS.
We're going to go over some examples of how to fix those
and what you can do to prevent those kind of problems.
Then we're going to go over a few tools and techniques
we have to sort of find problems before they happen.
So just to get this out of the way real quick,
one of the major and highly preventable problems
that can cause compatibility problems
is use of non-public API.
And just to be very simple about this, if you
don't find a class method, function, symbol,
in a header or a documentation, you really shouldn't use it.
It's generally not documented or not in a header, not
because we're trying to be malicious or we don't like you
but because it's not ready for your use yet.
If you find something you need and
it's not in a header or documentation,
the correct solution is to file an enhancement request.
Just let us know you need this
feature because it's not there yet
and we'll see what we can do about
adding that as soon as possible.
Keep in mind we do, you know when we get duplicate requests,
usually these are what we've already heard about a problem,
something isn't available, we consider
the number of dupes in scheduling fixes.
So please do let us know when you have a new
use case that we probably haven't thought of yet
and then we can get that maybe added as public API.
And of course even if something is in a
header or is documented there are ways
to misuse API, you're going to cause problems.
So here's an example we've seen in a few applications,
we want to get a view controller and put it
on to a navigation controller and then at the end of it,
after the animation is done, we want to well do something,
you know have an alert, take off a task, whatever.
Well one thing you might realize is that the animation on
one version of iOS always takes the same amount of time.
And this is some very kind of clever reverse engineering
to discover that it takes .4 seconds to do this animation
but this isn't future proof because it is entirely
possible that we'll change that duration in the future.
And even if we didn't change that duration, performs select
their effort delay isn't guaranteed to be precisely timed
so this could end up sort of breaking
the sync of your application.
And in almost all cases where we've seen
this, there are real public ways of doing it.
In this case you can just use the
navigation controller, delegate callback,
to know when the view controller is done
being pushed and then you can do your work.
And this will work much nicer and we'll be able to
change the duration of the animation in the future
and maybe even the style of the animation
and your app will actually require less code.
Another example of this is sort of
going through our view hierarchy.
We've seen a few cases where we have system
classes, in this case a UI video editor controller,
and some applications have needed
some part of that system view.
I've sort of changed the names to protect the
innocent here but this isn't really going to work.
The hierarchy of our views and our view
controllers is very much subject to change.
The way we lay out our views and even the way they
look are almost certain to change between iOS releases.
In particular, the ordering of subviews is not defined.
And of course the contents of the layers of
these views is also not really well defined.
In the vast majority of cases that have done
this, there are now lower level API available
to get the information you're looking for.
In particular, if you really just want to
grab an image off the video capture device,
you're going to look at the new AV foundation,
AVCaptureDevice class, new in iOS 4.
So another technique in Objective C is Categories, which
is a really powerful technique for adding functionality
to system classes and sort of organizing your classes
into multiple segments so you can have, you know,
one part of your class in one file,
one part in another file.
It really isn't a great way for over-riding
the behavior of system classes, however.
Although the runtime and the linker will let
you do it, it's kind of fraught with peril.
For example, if you really want all of your
views to be green, it isn't the right technique
to just over-ride DrawRect for all your
views and make the background green.
Although this might work in one version,
the implementation of DrawRect isn't stable.
You know we could be changing that in the future.
So you don't want to do this.
The correct solution is to you know make your own view
or even just use some other public API we have available.
So you make your own subclass, you know insert that where
you need it and this will prevent you from kind of stomping
on other code like system classes that
depend on behavior or will in the future.
Another little problem with categories is that it's actually
very easy to accidentally over-ride a method for a class
that doesn't exist, for a method that doesn't exist yet.
It sort of sounds weird but let me give you a hypothetical
here, let's say for a convenience you want to have a getter
for the height of a view so you add
height to UIView, well that's okay.
The problem is it's entirely possible that UIView
in the future will actually have a height property
and it might be defined differently
than what your app considers height.
And then all of our system frameworks that are built
on having the height property behave
some way are not going to work.
So you have to kind of use your imagination here
but the basic point is that any system class
with a common name is very likely that,
you have to assume that we're going
to add some method with that name in the future.
So a really easy work around for this is to sort of
name prefix methods that you add to system classes.
So if your app is called my app, just sort of add my app to
the beginning of the method name you add to system classes
and then if we add a height property in the future,
you won't sort of stomp on the system implementation.
And this isn't a purely hypothetical example, by the way.
Back in the early 2000's when there were dinosaurs, there
was also use hidden property on NSView back on the desktop,
on app kit so a number of applications actually broke when
they were adding their own use hidden property to views
and when they would run on the new version, the OS that had
hidden property built into NSView, everything went crazy.
So you don't want to do that.
Something else that's caused some compatibility
problems, you know crashing of applications when run
on new OS versions, is sort of not correctly handling
some of the more surprising API we have in the system.
Exceptions by and large are designed
to catch programmer error.
You shouldn't really rely on exceptions
being catchable and your application
in its normal operation should never throw an exception.
Well that seems good.
The problem is that there are some API that
actually do throw exceptions under sort
of surprising circumstances and this is one of those.
So there's a very subtle bug here but let's say this method
has a user input string so it's some sort of untrusted input
from the user types and we convert it
to a URL, we get the host out of it,
we make a mutable string, we mess
with it, we do with it whatever.
The tricky bit here is that NSURL actually will
return nil if the input is not valid, not a valid URL,
which normally in most of our API is okay.
We're pretty safe about dealing with nil.
The trick is that if you then send that nil object
of URL a host message and then here you pass
that nil objects immutable string with
string, that's actually going to crash.
It's going to throw an exception and it is documented but
it's kind of, this can cause some compatibility problems.
If in one version URL is slightly more
strict about handle how it parcels URL's
or if the user input is somehow
changing so it isn't URL anymore,
this is going crash on one version and
work fine on another version of iOS.
So there's a couple of things you can do
here, one is basically just check to see
that API UCall doesn't throw exceptions
or you handle those cases.
Another is to use slightly more safe non-exception
throwing API, in this case you can use mutable copy
on NSString which is okay with a nil receiver.
Something else I'm sure a lot of people have noticed is
that we don't really have name spaces so we have to deal
with naming conventions for all of our system
classes, which basically means that system classes
and functions have a two letter prefix
like UI or NS or CA or whatever.
You can't really use those.
Those are considered system private.
Don't use two letter prefixes for your classes, much
better to just sort of create your own prefix and use that.
Event handling in UIKit is another
sort of area of framework gotchas.
There's some sort of surprising
behaviors at the way events work in UIKit
that have caused problems with backwards compatibility.
The biggest one is the way some
people like to forward UITouches.
What this means is you get a touch in one view and then you
send it off to another view, you know for whatever reason.
This is technically supported but it is fraught with peril.
Every responder that gets the UITouch
must be your own custom subclass of UIView
or there's going to be crazy, undefined behavior.
And as a result this also means that you can't
insert any of our system classes or system views
into your view hierarchy and have it work correctly.
You can't use some of the more nice touch
handling controls that we've been adding recently.
So again, it's supported but highly discouraged.
And I'll show you there's a much better
solution for that in iOS 3.2 and later.
It's also important to remember
to implement touchesCancelled.
We've seen a lot of cases of people overriding touches
began, moved, ended and forgetting to do cancelled,
which leaves their application in some weird state.
And it's entirely possible through a reasonably
rigorous testing run through through your application,
to not catch this but it really can leave your application
in a weird place and especially now that we're getting more
like Local Notifications and Push Notifications,
there's going to be more and more cases
where your applications will have to handle touches getting
cancelled because of an alert popping up or something.
So it's really critical you implement
and work with touches getting cancelled.
And on a related note, just going back to
UITouch forwarding, in the vast majority of cases
where people were doing it, UI Adjuster
Recognizer is a much better solution.
It's new in 3.2 and will solve a lot of your problems.
Another little UIKit surprising problem
is the way UITableViewCells are views
which necessarily means they have subviews but you
shouldn't be adding views directly to UITableViewCells.
If you want to add something to a TableViewCell, like a
custom view, you actually want to add it to the content view
and this will allow you to get the highlighting
to work correctly and our layout will work better.
So again we can't really catch people actually
doing this but it will cause compatibility problems
and has actually caused a number of
applications to have weird behaviors
in their table views when upgrading to iOS 4.
So another problem, it's actually sort of a more general
purpose problem, is accidental blocking of your UI.
The main thread in iOS applications is primarily designed
for servicing your user's requests, responding to touches
and motion events from the user and
interpreting that sort of thing.
It isn't designed for, you shouldn't
be doing long-running computation
or network access or file reading on your main thread.
And this is actually something of a forward compatibility
problem because something may very well be fast
in one release of the OS but then get slower later.
And in this example we're doing a
loading of a URL into a TableViewCell.
The problem here is, although the
NSString, string with contents of URL,
may very well be fast in one version of the OS.
You know let's say that the URL is a file URL or
something like that and it's very quick to load,
it's entirely possible that in a future
version of the OS, that will get slow.
Maybe that URL will turn into a
web URL or something and it has
to make some long running request
to you know the internet, the cloud.
So you don't want to do this kind of thing.
You want to use either non-blocking API
or move your long running computation
and network access and file access to a background thread.
So this leads to another common mistake
which can cause compatibility problems.
All right you see here, well I don't want to do this in
the main thread so let me do it on a background thread.
Okay well I can use performSelectorInBackground and now my
work runs in the background thread and doesn't block the UI
and my table scrolls nicely and my
image on the TableViewCell just kind
of pops in when it gets loaded and no big deal.
The problem is there's another common gotcha which
is that UIKit classes really should only be used
from the main thread, with a few very specific exceptions.
In particular imageNamed, accessing the image view of
a TableViewCell and changing the image of an imageView,
none of that can be done from a background thread.
It may very well work on one release but then
cause crashes later and you really don't want that.
So to solve this you have this sort of three step process.
First you kick off your work to a background thread.
You do the background safe work, in this case loading the
content of the string content of URL on the background.
And then you message back to the main
thread to do the UIKit stuff there.
So we're doing the imageNamed loading and the
imageView manipulation back on the main thread,
which will be nice and safe for your user and for your app.
iOS 4 actually makes it a bit easier.
You can use libdispatch or the MS operation QAPI
and the new block syntax to handle this nicely.
So this is an equivalent thing that requires less code and
gives you a bit better compile time with error checking.
So you just sort of create a block that does the string
contents loading and then another block that does the work
on the image view and the UIImage on the main thread
and you sort of dispatch those to the queues you need
and requires a little bit less code
and does basically the same thing.
Another case where, another thing that's new in iOS 4, which
kind of goes back to the idea of not using your main thread
to do long running computations, is your
applicationDidEnterBackground method.
When you adopt fast app switching, your application
delegate will receive applicationDidEnterBackground
when the user switches out of your app.
This might seem like a good time to sort
of do some cleanup and it usually is.
The problem is you actually only have about
10 seconds in iOS 4 to respond to this.
If you don't return from that quickly enough,
your application will be forcibly terminated
and you'll effectively be opting out of fast app switching.
So if this dual out of work with object does a
lot of work with this object that takes a minute,
your app is effectively not working with fast
app switching when you implement it this way.
So you can do a similar thing we were doing with
the TableViewCell in the DidEnterBackground,
except when you're editing the background
you need to first request a background task
from the application and then you can start doing your work.
So here we request a background task from the application.
If we get it we do the lots of work on a background
global concurrent queue that won't block the main thread
and then we terminate the task and then
the app can go to sleep and the system,
your app will be able to quickly resume
when the user goes back to your application.
Another cool thing in iOS 4 is that there's a few
more thread safe API, UIGraphics and part of UIImage
and of course the background task identifier
stuff is now accessible off the main thread.
So there's a few new UIKit things you're
now allowed to do in the background,
which is great for not blocking your main thread and not
causing your UI to lock up and also great for, you know,
future compatibility if we get a little
bit more strict about how long you have
to block the main thread before we kill you.
So let's look at a few kind of nasty
assumptions that have caused some kind
of embarrassing problems on applications
between OS releases.
You really should never be hard coating pixel or point
values of our widgets in your applications and views.
The font metrics and the margins of our views
and labels and stuff aren't really stable.
It's possible those can change.
You know we might make slight pixel changes.
The way our buttons and table U cells
look are sort of subject to change.
We might make those a little tighter, a little smaller,
a little bigger so you don't want to just, you know,
sort of reverse engineer again that oh well it's
always 10 pixels here so I'll just hard code
that I'm going to move my drawing over by 10 pixels.
We provide API like in the string editions,
in UIKit to determine how big strings
should be and you really want to use those.
And it isn't just good for compatibility
of the future versions of the OS.
It's also really good for internationalization.
When you want to start supporting languages beyond
English or whatever languages you are on now,
you really need to be able to handle longer and
shorter human readable strings in your applications.
So you know kind of getting started on that early
and making sure the UI is kind of flexible to handle
that sort of thing, is a really good idea.
And of course we are no longer in a 320 by 480 world.
You know just in the last few months we've gone from 320
by 480 to iPad size devices and now the retina display
on iPhone 4 and who knows where we're going from there.
[laughter] So really do make use of
the UIScreen bounds that we provide.
You know if you want to make your
thing full screen, we provide it.
We provide a way to get that.
Just say, you know get the main screen or whatever
screen you're drawing on, if you're using TV out,
get the bounds of it and that gives
you the points you want to use to lay
out your views, which is a really, really good idea.
Another case we saw with the introduction of
the iPad was that we actually had very good luck
with the application compatibility in compatibility mode.
So the vast majority of iPhone applications work very nicely
when we're in an iPad incompatibility mode but a couple
of them, we launched them and nothing happened.
And in a huge proportion of these cases we ended up finding
code in these applications that look something like this.
You should really never be using the model property
of the UIDevice object for almost anything.
It exists sort of just a purely informative thing
to determine, you know maybe the name of the device
to show the user like you know thank you
for running my application on your iPhone.
You should never use it to determine how your UI looks.
So there's two main problems with this code here, the
first is it makes the unfortunate assumption that iPhones
and iPod touches, you know having that model
name determines what capabilities you have,
which is actually already a problem because you can see
that even since last year iPod touches have had microphone
in accessories so if your application
requires a microphone, camera, SMS,
you don't want to be using a device name to determine that.
And of course the bigger problem is, if device model
returns something other than an iPhone or iPod touch,
your application will set up no user interface,
which is what we saw on all these iPad examples.
No UI got set up at all.
So what you do want to use are the
capabilities based API provide.
If you want to know if a microphone is
available, AVAudioSession can tell you.
If you want to know if there's a camera, the ImagePicker
can tell you and if you want to know if you can send SMS,
you can ask the application if it can open an SMS URL.
There's a few other examples in the documentation
but the critical thing is use specific API
for the capability you need and secondly, if the capability
isn't available but you still want to support that device,
just set it up unconditionally, not based on the model name.
So basically, always make sure to set up something.
And of course there's a few new capabilities
in iOS 4 and in iPhone 4 and we have new API
for that so I want to show you a couple of those.
So one is the new message framework
stuff lets you compose SMS's,
text messages so there's a cool new
class method called canSendText,
which lets you know if your device has
been set up for sending text messages.
The event kit system has a new support
for calendar and date based stuff
and you can see what events are
supported in a given calendar.
The gyroscope in iPhone 4 is exposed through core motion.
And this also works for using the excelerometer,
there's new stuff in core motion for that.
So just check to see if the gyro is available on
the CMMotionManager and that will work nicely.
If you want to know if you have a retinal display,
you can check the scale property of a screen.
In iPhone 4 the scale is 2, for
all the other devices it's 1.
But you should handle any scale.
And of course the new cameras, there's great
availability stuff in the image controller
for the new camera capabilities to
determine if there's a front or back camera,
how you can determine which camera has
a flash, if any and you can determine
which camera has a video or still capabilities.
I'm going to switch gears a little bit and
talk about some of the tools we have available
to let you sort of catch problems before they happen.
And the biggest tool is just the new versions of the SDK.
As soon as new betas become available, it is absolutely
critical that everyone download them, install them,
recompile your application with
the new SDK and make sure it work.
If there's any unexpected behavior change, well first
check to see if there's a bug in your code that was exposed
by this but if you can't find a bug in your code, we
really want to hear about it, even if you're not sure,
please file a bug and say my app used to
do this, now it does this, what gives?
We really, really want to hear that.
And in particular, turn on deprecation warnings.
Just go into your project target settings and check the warn
about deprecated functions checkbox and it will tell you
about any API that we aren't supporting anymore.
Deprecated means we're not adding
features to it, we're not fixing bugs in it
and there's a better replacement available.
So in this example on the screen we've got the final
attributes of path traverse link method on File Manager,
which has been deprecated for a little while.
You definitely don't want to use that anymore
and the header can explain some reasons why.
So why is it important that you link
against new versions of the SDK?
Why not just take the binary you had, you
know your old version of your application,
put it on you know the new beta OS and see how it works?
The answer is we employ a technique
we call linked on or after.
Every once in a while we do find
bugs in our system frameworks.
And by and large we try to fix those bugs
but if we have third party applications
that could potentially be inadvertently depending on those
bugs, we have to be very careful about how we fix them.
And our technique is that if there's any chance that
fixing this bug will cause applications to crash,
we try to use what's called a linked on or after
check, which is to say we check to see what version
of the SDK was used to compile the application.
And if it was a version from before the bug was
discovered and fixed, we maintain the old buggy behavior.
So let me give you a little example of this, I'm not
sure if anyone noticed but up until 2.0 or up until 3.0,
in iPhone OS 2, imageNamed actually leaked.
It returned images that were a little bit
over retained, just by 1 but that was enough.
[laughter] You shouldn't, you know, rely on this particular
example of retain count but this just shows the problem.
So image named, it should be returning
something with retained count of effectively 1
but it was actually returning one with 2, obviously
auto released but well when we discovered this problem,
we didn't want to just start returning a retained
count of you know a correctly retained object
because applications could have been inadvertently
depending on the application being over retained.
So even on future versions of iOS, as long
as the app was compiled with an older SDK,
we'll still return an over retained object.
But as soon as you compile with the new SDK's,
we'll return a correctly retained object.
So if any application was inadvertently depending
on that behavior, they'll start to crash.
And we employ this by and large pretty much anywhere we can.
It will cause sort of behavior changes and
accidental memory corruption and stuff like that.
So it is really, really critical to compile with
new versions of the SDK when they become available.
And another great way to find bugs before they
sort of become problems is the static analyzer.
It really is just about the best thing ever.
It catches a lot of errors and the cool thing about
it is that it can actually catch errors on code paths
that you don't test in your normal testing
because for whatever reason your application
doesn't go down that code path yet.
It truly can like walk through functions and methods
and branches and all that stuff and find over-retains
and uses of uninitialized variables and
I can't say enough great things about it.
One really cool new thing in recent versions of Xcode 3
and later, is that you can now turn the Static Analyzer
on immediately after, by default so that every time
you build your application, a Static Analyzer runs.
I would highly advise doing that.
It can catch bugs before they become problems and will
really, really make development much easier and much nicer.
There is one small problem with the Static Analyzer,
which is that because it's built on the new LLVM compiler,
anything it can't compile won't get analyzed
and will just come back with zero bugs,
which means you might not be getting as
much coverage of your code as you think.
So that also means that one thing we
can't solve just yet is coverage of C++,
which actually the Static Analyzer
can't quite cover yet anyway.
But also there are some C and Objective C idioms
we've seen in code, including ours, which GCC accepts
but the new compiler won't like, won't deal with.
So as a result, one way to get better coverage of the
Static Analyzer is to switch to the LLVM compiler.
You don't necessarily have to switch to
it permanently, if you're not quite ready
to use the cutting edge compiler system but just
maybe once in a while switch to the LLVM compiler.
Make sure your app compiles cleanly with it and then
the Static Analyzer will give you much better results.
There's some great new features
in the iPhone Simulator in iOS 4.
The biggest is that we've bought over the new run time.
It has a lot of cool new features but the best part of it is
you can now run newly, you know, iOS 4 compiled applications
on older versions of the SDK and we're
going to support this going forward.
Currently we only support as far back as 3.2 but even with
just that, that's a pretty great thing because that means
for universal app development you can compile with the
iOS 4 SDK and simulate it on the iPhone OS 3.2 system.
So since some of the new stuff in iOS 4 isn't quite
available in the iPad yet, iPad's are still going to run 3.2
for a while, this is a really, really convenient thing.
And again, we're going to continue
to support this in the future.
And obviously it also works the other way.
You can compile something for 3.2 and
then run it on the 4.0 SDK, the Simulator.
The Simulator is also really great to support
sort of unusual configurations that you might not,
you know, consider testing on your devices.
You know test to make sure your UI
is good with the in-call status bar.
You know the double height status bar
which is going to become, you know,
more and more apps are going to use
that sort of thing in the future.
Low memory warnings, in an era of multitasking when
you've got lots of applications on your system,
it's entirely possible that you're going to get more,
you're more likely to get memory
warnings today than you used to be.
And applications are getting bigger and all that stuff.
So make sure to test low memory warnings,
which the simulator can do very easily.
You just go into menu, go to simulate low memory
warning and make sure your application works correctly.
Just do that every once in a while.
But of course the simulator is still
not a replacement for the device.
If you plan to ship on, given on OS version it is critical
that you actually test on that
OS hardware version on a device.
In particular, the performance characteristics are different
and we don't support some of the new background modes
like background location, navigation and
void stuff and the background audio playback.
That stuff isn't correctly stimulated so stick with,
you know, make sure to use the device when you can.
Instruments is great for catching sort of runtime problems.
It's a great idea to run leaks in the
allocations instruments every once in a while,
just you know maybe once a week or something.
And if you notice your application is leaking
memory, what we have typically found is that,
well first of all that's a very bad thing.
You want to fix that.
But you could have been accidentally, you know,
relying on that leak to cover up some other over retain
or over release, which is going to cause crashing later on.
So every once, you know run leaks, fix your
leaks, make sure you're not abandoning memory
and then make sure your app doesn't
crash after you fix those leaks.
If you have a leak and you fix
it and your app starts crashing,
it is not the correct thing to put the leak back in.
[laughter] That means you have a problem you need to fix.
One of the really great things in iOS
4 SDK is the automation instrument,
which lets you sort of automatedly
test your user interface, you know,
and drilling down through the UI
without manually tapping things.
I would make use of that.
It's really great for finding, you know,
behavioral differences between OS versions.
Sort of set up a good test suite of automated tests.
Run it on different versions.
Make sure everything works correctly.
So we're going to kind of switch gears again for a
second and talk about sort of past proofing your app.
A lot of people want to ship their application such that
it takes advantage of new features on new operating systems
but at the same time can still run on old operating
systems that don't support those features, which is noble.
It's a good thing to do.
But you have to do a little bit of
work to make sure that works correctly.
If you want to call a new C function or use
a new C symbol, if you compile against it
with the correct minimal version set but using the new SDK,
when you run on older devices,
that symbol will just be null.
So calling it is going to crash.
So for example if you want to use the new UI Accessibility
VoiceOverRunning C Function and you still want your app,
which is new in iOS 4, but you still want your app to run
on old versions, you need to sort of gracefully fall back
by checking to see if the function is available and not
null and if it is available, you can go ahead and call it,
otherwise sort of fall back to some reasonable behavior.
So just assuming that it's not
running is probably reasonable here.
So that's a nice way of doing that.
There's a new method that's available on Existing
Class that's not available on older versions of the OS.
You can use RespondsToSelector, which
is a class instance method on NSObject.
So for example if you want to see if the
beginBackgroundTask with identifier API is available.
You take an application, probably your
application, the shared application in your process,
you ask if it responds to that method, that selector
and if it does you can use the, you
can call it and use that result.
On old devices you'll just use the
background test unifier invalid,
which is also what happens on older devices running iOS 4.
So for example, on the original iPhone 3G you'll get an
invalid background task identifier anyway so you're going
to handle that regardless but this will work
nicely even on older operation system versions.
If you want to use a new class,
you can use NS Class from String.
You pass the name of the class you want to use,
in this case let's say you want to use UI NIB
for the cool new NIB loading stuff we have in iOS 4.
That will return an object which is a
class, in Objective C classes are objects.
If the class is available you can use it.
If not, you need to create some kind of fall back behavior.
You can use maybe the NS bundles
loading stuff, NIB loading code.
So there is one thing we don't quite support yet.
Let's say you want to subclass a new class, like
let's say you want to subclass UIGestureRecognizer
and have it run on old versions of the OS.
This is technically feasible with our runtime
so that the framework is there but our compiler
and linker don't actually support it just yet.
So if you do have a real need for this, please file
an enhancement request and we'll see what we can do.
So just to go over it one more time, finding compatibility
is not an easy problem but we are committed to it
and we want your apps to work on future versions
of the OS as much as you do and your customers do.
So be vigilant about compiling with new versions of the
SDK, finding bugs, file those bugs and let us know about it
and there's really great tools available to, well
catch these problems before they become real problems.
For more information you can talk to Bill.
Definitely, as always, read the documentation and check out
the Dev Forums, in particular the Coding Guidelines goes
over some of the stuff about how we handle future, you
know future methods and stuff like that in our frameworks
and the Dev Forum's always great
for answering questions you have
about you know what might work
in different versions of the OS.