iOS 9 introduces On Demand Resources, enabling the efficient delivery of rich games and full featured applications using dynamically loaded content. Significantly reduce the time between purchasing and running an app from the App Store, while also decreasing the required storage space by downloading and retaining only content that is necessary. Dive into the latest enhancements in app packaging and learn the APIs to allow your app to acquire only its essential resources.
STEVE LEWALLEN: Well, good afternoon!
And welcome to today's session introducing On Demand Resources.
My name is Steve Lewallen.
So what do we have for you today?
Well, we're going to begin by giving you an overview
of On Demand Resources, or ODR, and we're going
to compare an application built with ODR to one built without.
We will follow that by listing all the features and benefits
to you, the developer, as well as to users of ODR applications.
Then we will deep dive into a bit of the details of ODR,
how an ODR application is structured, how you build one.
We will follow that up by giving you a demo using Xcode
and the new ODR API we have added to Foundation.
And finally, we will wrap things up with a segment
on best practices in using
and building the optimal ODR application.
So let's get started.
So to understand where we are going with ODR,
we need to understand where we are today
with the traditional application.
Now, this application will have an executable segment.
This is your compiled Swift, Objective-C,
C++ as well as some base resources, and the levels
in the game, for example, if this is a game.
When you, the developer, are happy with this game
or application, you upload it to the store,
and when the user comes along and buys the app
or game they get the entire thing and down
to the device it goes.
And there it is not alone.
Here we have all the other compelling applications
and games that you, the developer,
are writing for all of these users.
In fact they are so compelling that they can't get enough
of them and literally this means more
and more the case this is literally true.
There just is not enough space on the device.
So what are we to do?
Well, one thing we can do is make the observation
that not every part of every app is needed at any one time.
I will give an example.
I may be on Level 7 of a game.
I certainly don't need Level 1 any longer
and I probably won't need Level 30 for quite a while.
So it's with that thought that we started
to begin to think about ODR.
So now let's look at an ODR application.
In this case, we start off with the same bits,
we haven't taken anything away, but what we're going
to do is we are going to tease apart the assets
for each level of that game.
And then we are going to upload the entire thing to the store.
Now, when a customer comes along and buys the app,
they get the executable and the base resources
and perhaps the first level in the game.
So no pun intended we are already ahead of the game here
because they were able to arrive at the first level of the game,
the time between buying the app and playing that first level,
much more quickly than if they had
to download the entire app first and they had
to use far less disk space to do it.
Now, the user starts playing this game
and the first level is in use.
And a well-designed ODR application will anticipate
needing, say, the next level in the game.
So it will download that.
The user continues to play, they move on to Level 2,
and at this point, the caching mechanism built
into ODR notices you are no longer using Level 2,
well I'm just going to make a note of that.
It's still there, we haven't done anything with it,
but we're just going to remember it's no longer needed.
So this continues until we get to the point, perhaps,
where there is no more space on the device
for the next level of the game.
We have looked at every other possible place on the device
to free up resources and all we have left is ODR content
from this game.
In this case, the caching mechanism can step in again
and say, you know, I had Level 1, I can free that up
and now the user can continue on with the game
and play the next level.
So this is the basic idea behind ODR;
in the case of a game it's fast to first play from buy to play
and it's always occupying a smaller, more manageable amount
of space on the device.
So now let's talk about all of the features of ODR
that we provide you, the developers.
First of all, obviously this is a dynamically loaded content
system and you have probably used systems sort
of like this before, but we have added some new wrinkles.
First of all we are going to host the content
on the App Store right along with your app.
Second of all, we can download content during app install
as well as by request and in fact we can automate,
we can automate this content download at any time.
And finally, we also include an intelligent caching mechanism
that I mentioned.
So, for example, we can free up space to load something new.
And finally, a traditional app maxes out at 4-gig submission
in the Store, but with ODR your app can now be
up to 20 gigabytes.
Most of this, of course, is ODR content.
So that's what's in it for you, the developer,
but what's in it for the user?
Well, first of all, we can improve the install experience.
A traditional DLC system, a game written using such a technology,
provides an experience something like this:
the user downloads the app, they are all excited
about playing the game.
They launch it, but oh no, the game now needs to go
and download new content.
With ODR we can make sure that that content is on the device
when the app looks like it is installed.
So the experience for the user is much better.
Second, because we are occupying a smaller
and more manageable space on device, the footprint of the app
at any one time, we can have more apps on the device ready
to go and more apps ready to go at any one time
on the device is always a good thing for the user.
And finally, for the user, because, again,
there is more space and we are keeping a lot of the app
or the game in the cloud on the App Store, we can have levels
that are richer and more expansive, for example.
And that is always a good thing for the user as well.
Okay. So now let's dive into some of the details about ODR.
First of all, it's an elementive app thinning in iOS 9
and it's well integrated with app slicing.
In case you missed the details of app slicing earlier
in the conference, app slicing is all
about tailoring the download of an app to a particular device.
So let me give you an example: You write an app
that targets many different iPhone sizes and iPads.
A user with an iPhone comes along and when they buy
that app, app slicing makes sure
that they only get the resources you need for that phone.
In the past they would have gotten all
of the assets including those for an iPad.
So now when we combine app slicing and we join it with ODR,
we double the benefits of both.
The footprint is still even smaller.
We are getting to first play even faster
and the constant steady state of the app
on device is much smaller.
Okay. So what does an ODR app look like?
How is it structured?
It's pretty similar to an app today, but as you recall,
we teased apart the assets for those levels in the game.
We call these asset packs.
And the rest of the app remains your dot app.
Now, you group these together using Xcode with simple tags.
For example, these are all my assets in Level 1
of the game, for example.
It's pretty simple to set up.
And you can tag a single file or a whole folder.
This whole folder is Level 2, for example.
Now, what can you tag?
Well, pretty much anything that can be in a dot app today.
For example, images, sounds, data, scripts,
many games have scripts in them,
you can also have in-app purchased content.
So now you can tie together your in-app purchased receipts
with in-app purchased content
that you are actually downloading via ODR,
and taking advantage of all the other ODR API
to help manage that.
The only thing you can't have is executable content: That's,
again, the compiled Swift, Objective-C, C, C++.
Leave that in your dot app.
So where is this content hosted?
Well, I mentioned one of the locations;
it's obviously the App Store.
We host it in the App Store and we serve it on demand as needed,
but during development, Xcode stands in for the App Store,
whether you are developing
against a device or the simulator.
And it hosts it to your app and delivers it on demand.
Now, another tool in the Xcode tool box is the Xcode Server.
You can set this up in your own department,
such that the app along with the ODR content is available
on that server, for example,
your Q/A engineering group to test your app.
And as you might expect,
TestFlight is fully integrated with ODR.
And finally, if you are deploying an app
into Enterprise, you can actually host ODR content
on a web server inside of your enterprise including behind a
secure login so that not just anyone can get at that content.
So that's where we host ODR content.
So how do you get started?
Well, as a developer, the first thing you need
to do is take a look at all the many assets you have in your app
and start to identify them.
You need to categorize them.
You use this, again, by tagging them with simple strings.
These are all of the assets that are in Level 1 of my game.
These are the assets for Level 2 of my game.
And you know, there are some shared assets between levels?
There they are.
So you can tag assets with multiple tags in order
to indicate the sharing and avoid duplication.
So this is part one of getting ready to use ODR
and that was your job as a developer.
Now, Xcode does its job because it takes all this tagging
and groups these into asset packs.
In this case, we ended up with three asset packs.
We have the asset pack for Level 1, the asset pack for Level 2,
and we ended up with a third asset pack
which is the shared assets between those two levels,
again, avoiding duplication.
Okay. So Xcode has done its job.
Now, it's back to you, the developer.
The first thing you need
to do is basically just request the resources: Hey,
I need everything for Level 1,
and down come the two asset packs, both the dedicated one
to Level 1, and then the one that was sharing assets
between Level 1 and Level 2.
And because we brought down that shared asset pack,
when we request Level 2, we get those assets
and we already have the shared assets on the device.
So this is a high level overview of what ODR is all about as well
as how to build an ODR application.
Now, I would like to invite on stage Tony Parker
to show us how it's done in Xcode with a real API.
TONY PARKER: Thanks, Steve.
So, again, my name is Tony Parker, I'm the manager
of the Foundation team at Apple.
So Steve gave you a basic overview
of how the ODR system works.
Now we are going to dive in to the API that we use
in your application to actually make those requests.
There is really just one piece of API that you need to know.
It's a new class in Foundation called NSBundleResourceRequest.
This class follows the command design pattern.
So what that means is you're going to set up one
of these objects with a set of options,
including of course the set of tags that you are interested
in using, and then you tell it to begin its request.
So you can create as many of these objects as you need.
That's because the system will ref count the tags
under the hood.
So if you have several different parts of your application
that use ODR and perhaps use overlapping tags, you don't need
to create a manager class to keep track
of which ones you have in use at a time.
We will go ahead and do that for you.
The most important point, design point about this class is
that the request is decoupled
from the actual use of the resources.
What this means is that all of the APIs that you use today
in your application and you are already familiar
with like NSBundle's URLForResource,
NSData's dataWithContentsOfURL or UIImage imageNamed,
all of those APIs remain actually as is.
You just need to tell the system in advance using one
of these request objects that you are going
to need those resources to be present.
This object forms a very simple state machine.
So start off in init.
We move it to this requested state
when we anticipate the need for those resources.
And we're going to get back a completion handler callback
which will tell us either the resources are now available
for you to use and you can continue to use those APIs
that we just discussed, or an error occurred.
Now, of course, when you are using ODR there are a few errors
that can occur that we may need to present to the user.
That could include, perhaps, there is no network available
and we need it to download the content,
or maybe there is not enough disk space on the device even
after we tried purging to hold the content.
So in any case, you need to present that to the user.
Perhaps there is something they can do to resolve the issue.
So if the resources were available, then, as I said,
you can continue to use them, and also important,
when you are done with those resources, please tell us
about it, and there are two mechanisms to do that.
The first is to call an explicit API on this class
that tells us you are finished with the content.
The second is to allow the class to be deallocated,
allow the object to be deallocated,
in which case we are going to go ahead
and end the request on your behalf.
So here is what the basic methods look like.
First, an initializer, and you can see it takes a set
Those are the tags that you are interested in requesting.
The method to begin the request is called
and you can see there's the closure
that has the NSError argument.
And finally that method
that tells the system you are finished,
the explicit endAccessingResources call.
So next I would like to go into a demo and show you some
of this API in action.
So here you have our demo app today, it's called iTravel,
and iTravel is called iTravel because it's a travel guide
that gives you all kinds interesting information
about countries whose names begin with the letter I.
As you can see, we support two countries today:
There's Iceland and Italy.
Now, this kind of application is a great candidate
for adopting On Demand Resources.
And that is, the reason is
because when the user buys this app,
they are maybe not interested in getting all the information
about both Iceland and Italy or at least not both
at the same time so we can make the install size
and the download size much smaller by downloading
that content on demand.
So before we adopt ODR I just want
to show you a brief example of how you use this.
First I will go ahead and visit Iceland.
You can see that I get a list here of points of interest.
So I can pick one of these
and there is some high quality pictures, perhaps,
or guide text, and each of these points
of interest has more pictures.
So you can see that this could add up to quite a bit of data.
And Italy, of course, behaves the same way,
but with a different set of content.
So let's look at how this application is built.
We are going to spend most of our time today
in just one class, it's called the AlbumTableViewController.
This is the view controller that controls this view
that we see right here with the list of points of interest.
So let me show you how it works.
When we segue into this view, we are going
to have this function called loadAlbum,
and the argument is going to be which album we are interested
in looking at, either Italy or Iceland.
We set our title and we call this helper function
Now, here in the populateTable function, we are going
to use NSBundle's URLForResource to find a JSON file
which describes all of the points of interest with pictures
to show what the captions are.
We are using NSData's contentsOfURL method
to actually read that JSON file from disk.
We are using NSJSONSerialization to parse it.
We are setting up some more detailed label text here,
and finally we ask the tableView to reload itself,
and the data source
in the tableView is using UIImage.imageNamed
to actually show that, to fetch that picture.
So the important point here is that as we adopt ODR
in this application, nothing
in this populateTable function has to change.
So, again, all of the APIs that you use today
that access contents of files on disk or finds those files
like NSBundles, those remain exactly the same.
So the first thing we are going
to do is actually add some tags to our application.
So to do that, I will bring up the inspector here,
and you can see that I have already organized my application
to have a group called Resources,
and folders that contain some of my content.
So this one contains all of the picture from Iceland, this one,
all of the pictures from Italy, and these JSON files
that I was discussing.
So what I'm going to do is go ahead
and select both the JSON file for Iceland and the folder
and look on the right side here in this inspector.
You see there is a new field, On Demand Resource Tags,
so all I have to do here is start typing
and we will tag our content as Iceland.
And we will do the same for our Italy content.
So next we just, so that, what that does is, of course,
as Steve explained, tell Xcode how to split up your content.
Now, the next part is to tell the system at runtime
that when we anticipate the need
for that content to be available.
So we will do that here in this view controller.
So the first thing I'm going to do is add an I var to my class
that holds the class that we've been talking about,
So what I'm doing is taking advantage of the fact
that when this view controller is torn down,
we maintain pretty tight control over this object.
So we can control its lifetime so when it's torn down we know
that we are done with the request
and we can allow the system to deallocate it
and that will tell the system we are done with the content.
Next in our loadAlbum function before we call populate table,
we are going to create the request, we are passing
in the set of tags, in this case it's a set of one tag
which is either Italy or Iceland.
We call beginAccessingResources WithCompletionHandler (without
space), and when we get a result,
this closure will be called, and it may have an error.
So on the main thread, because this completion handler will be
called on a non-main thread, we are going to first check
to make sure that the error is nil.
If it's non-nil, then an error occurred
and as we discussed it's important to present
that to the user, which I'm doing in a helper function here.
Then we call the exact same populateTable function
that we just went through.
So, again, nothing in there changed.
Let's go ahead and run the application again
and see how this looks.
So now, I'm going to visit Iceland and we will see
that my content is here because we have made a request for it,
and I can choose some of these pictures or points of interest,
and all of my content is available just
as if it had been part of the application from the start.
Now, this is a good opportunity
to show you a new debugging feature in Xcode
that can really help you to understand
if you are using these request objects
in the way you think you are.
And that's a new debug gauge.
So I'm going to bring
up the debug gauge view here and choose disk.
And you'll notice in the middle we have a new section called
On Demand Resources.
It lists all of the tags that are part of your application,
so Iceland and Italy, their size and their current status.
You notice that Iceland is listed as in use,
and that makes sense because we are looking
at the Iceland content right here and it remains in use
as I view some of these pictures.
But if I go back to the main table
of contents its status is changed from in use
to downloaded, and that's because again
that request object was deallocated
and the system now knows we are no longer using that content.
We didn't delete it off disk immediately so if you go back
to Iceland the content is available again
and the status changes back to in use.
Let's see what happens if I visit Italy now.
Now, you notice that it's going
to take some time to show this content.
And the reason for that is that the size
of the Italy tag is actually much larger than the one
for Iceland; it's 130 megabytes here.
Now, there are two things that are important to do here.
The first is to actually reduce the size of the tagged content,
split it up into smaller chunks, and we are going to go
into some more detail on the best practices for that later,
but for now, what I want
to do is show you how you can adopt progress reporting
in your application in conjunction
with NSBundleResourceRequest so that
when you are downloading this content you can display
something to the user so they understand what's going on.
So let's go back to our AlbumTableViewController class.
So what we are going to do is take advantage
of NSBundleResourceRequest support for NSProgress,
which is a foundation class you can use to report progress
and compose progress across your application.
NSProgress supports key value observing, so what we're going
to do is add this view controller as an observer
of the progress and in response to updates, change some UI
on the screen, a UI progress view,
and some detail label text.
So right here after we create the request,
we are going to call addObserver.
This class will be the observer.
The progress property
of the request is the object we are observing.
The key path of the progress is fractionCompleted.
And as always when using KVO it's important
to specify a unique context pointer
which I have defined elsewhere in this file.
Also, we are going to go ahead and unhide a UI progressView
that I have already hooked up in our storyboard.
When we get our response, that's a great time to removeObserver
because after that point we are no longer interested
in updates to the progress.
And then on the main thread we are also going to unhide the --
sorry, rehide the UI progressView
because we don't want our user to be staring
at complete progress bars.
So that adds ourselves as an observer
and then the second part of this is to actually do something
when the value changes,
and we do that in the traditional KVO method,
So here we are first checking to make sure
that this is the update that we are interested in observing
by checking the context pointer and the key path,
and on the main thread, again, because this update will come
on a non-main thread, we are going
to update our UI progressView by setting its progress property,
and we are going to take advantage
of NSProgress's support for an automatic localized description
by using its localizedDescription method.
Let's go ahead and run this again and see how that works.
So this time when I visit Italy, you will notice
that at the bottom our detail label text has been updated
to show a percent completed, and then as the download completes,
our UI progress view is updated to show the user
that something has happened.
Also, again, we didn't delete the content eagerly
so if we go back and visit Italy again,
you notice that the content is immediately available
because it's been cached by the On Demand Resources system.
Let's go back to our slides.
So a few more things about the progress reporting part
of that demo.
So, again, you can find information on the progress
of the request by looking at the progress property.
We also support cancellation, pausing, and resuming
of the request, and those are methods you will find
on the NSProgress property, not the request itself.
Now, actually in iOS 9 and OS X 10.11 we have made a lot
of really great improvements
to the NSProgress class in Foundation.
So to learn more about how to use it, how to incorporate it
from this class and from elsewhere into your application
in a great way, I really recommend you check
out this talk on Friday, Best Practices
for Progress Reporting.
So the beginAccessing method that we used goes ahead
and downloads content if it's not already available.
However, sometimes you may want to only use the content
if it's already available on disk
without triggering a download and you can do
that using what we call a conditional request.
Here's the method:
(without space), and you can see
that the closure here has a Boolean argument,
so the answer is either yes or no,
depending on whether the content is already available or not.
So in the view of our state machine, we start off in init,
we move to this conditionally requested state
by invoking this method.
You will get your response, either it's available or not.
If it's available, then the resources are available
to you just as if you had called the other method.
So, again, be sure to call endAccessingResources
or allow the object to be deallocated.
If it wasn't available, at that point you have a choice:
You can either do nothing
or you can call the beginAccessingResources method
to cause that download to happen.
And finally, there are two kinds of priority APIs on this class
that I want to talk about.
The first is called a loading priority.
This is a double, and it provides ordering
for the outstanding requests in your application.
The value ranges from 0 to 1.
So a value of 1 is the highest priority
and the value of 0 is the lowest.
What we do with these values is provide a hint to the system
about which outstanding request should be prioritized first.
So because the value is not compared across applications
on the system, but only within your app,
you can use these values as you choose
to help order the requests according to your needs.
We do have a constant that you can use here actually;
it's called loading priority urgent, and this is for the case
where the user is doing nothing but waiting for the download
to finish and we will talk a little bit more
about this later.
The second kind of priority is called a preservation priority.
So this method provides ordering of purging for unused tags
in your app, so this is not associated
with a particular request, which is why it's an extension
on NSBundle, but instead with a tag or set of tags.
Again, the value ranges from 0 to 1,
with 0 being the least interesting to keep
and 1 being the most important to keep.
So you can use this to tell the system a hint
about which content is the most important to keep if we run
into disk space pressure.
And the value, again, is compared only
across your own application,
not between applications on the system.
So with that, I would like to bring back Steve to go
over some more best practices about using ODR.
STEVE LEWALLEN: Thank you, Tony.
All right, so now we have an overview of ODR,
we understand what it's about, and we have seen it in use.
So now let's talk about how we build the optimal
Now, to do that, the first thing that you need
to do before you start tagging assets is
to consider your app's behavior,
because this will inform how you need to tag assets.
There are three patterns that I can offer up for you today
as to how you might use ODR content.
The first pattern, this is like Tony's iTravel app.
We couldn't quite anticipate where the user would want to go.
This is random access.
And in a random access app,
especially when the assets are larger, the content in total,
what you want to do is tag more assets and read things
in progressively so that as soon as the user makes a decision,
they can start to see some progress
of that decision in the UI.
The next pattern is called limited prediction.
This is like an open world game in some sense,
where though there may be almost an infinite number
of possibilities, at any one point
in time there's only a limited subset.
An so again what you want
to do is have many tags tagging smaller size content,
progressively read that in, but also be prepared
to discard content that the user has chosen not to view, as,
for example, the player in the game moves around the world
and they have left some location, you should stop using
that indexing resources, getting rid
of the NSBundleResourceRequest object.
Now, the third pattern is the pattern than I've been using
in my example; it's the levels in the game.
This is the linear progression game much
like a first person shooter.
In this case, the biggest task that you have as a developer is
to anticipate well ahead of time what will be needed next,
but you are pretty much assured that all
of the content will be consumed, and so you just want
to start loading that content
within a reasonable amount of time.
Okay. Speaking of time, there is a rhythm to how you use ODR API.
So let's give ourselves a timeline
from the point the app launches until it exits.
Now, the well-written ODR application will anticipate
needing assets well beforehand.
Remember, this is a network-based app.
It's going to have to download it from the Store
or another location we went over earlier,
but that will take some time.
So as soon as you anticipate needing them, assets,
that's when you call beginAccessingResources
and that will kick off the download
if the assets are not yet on the device.
Now, the best-laid plans can hit, well, network problems,
for example, and other things that may result
in you needing the resources before they are actually ready.
And, again, in this case, you want to bring up the progress UI
that Tony mentioned using the progress property
Share your learning screen, allow the download to continue,
and then at some point that will actually finish,
the resources will be available and you can take
down the loading screen, and you can begin to use the resources.
Once you are done using the resources, you absolutely want
to call endAccessingResources
or allow the NSBundleResourceRequest object
to dealloc in order to tell the system, hey, I'm done with this.
And remember that that doesn't mean we go off
and delete that content.
We are just making a note of it.
So this is the basic timeline you need to be aware of,
and you can make multiple these requests simultaneously,
you just need to remember the basic parts of this.
Okay. Now, I had talked about how ODR can benefit the user
by improving the installation experience.
So how is that done?
Well, we need your help to do that.
What you need to do as a developer is
to consider the assets that the user is going to need as soon
as they launch the app.
When you know what those are, say the first level
in the game is a good one, then you want to take the tags
for those assets and put them in Xcode's Initial Install Tags UI.
This will tell the ODR system to make sure all the assets
with those tags are downloaded before the app shows
as being 100% installed.
That way when it does show it's completely installed
and the user taps on it, it's ready to go.
They are ready to play that game.
The size of your app that you see in the App Store is going
to be the size of the dot app plus the size
of the initial ODR content that you have tagged here,
so just to be aware and we will talk
about this point a little bit later as well.
Okay. So now let's talk about automating the installation
of ODR content between the time the app is actually fully
installed, but the user has still not actually launched it,
because you can automate that period of time as well.
You could use this, for example, to make sure the second level
in the game is loading, or, for example, tutorials.
Maybe some customers want to see these tutorials
and others don't, so you are not going to prevent the app
from actually running if the tutorial isn't there,
but you would like to make sure it is there
if you have the opportunity.
And to do this, you can also use Xcode's resource tags UI
and put the tags in the pre-fetch tag order section.
In the actual order that you place them
in this section is the order we will download them.
Now, let's talk about caching.
We mentioned caching earlier
and we showed how the system purged game Level 1
to make space for a new level.
The first thing you need to know is we only purge content
when the system is low on space.
And the only content that is there
to be purged is ODR content.
When we do come to that point, there are several attributes
that come into play to inform us
as to what decision we should make in deleting
which asset pack, for example.
These are pretty obvious.
One is when did you last use the asset.
So, for example, game Level 1 that was played last Tuesday,
that's probably a better candidate to delete
than the level you finished last night.
Also as Tony mentioned you have preservation priority
which is a ranking you can place on tags.
So this is your own ranking but we take
that into account as well.
And finally, we take
into account the running state of the app.
We will not delete any asset packs from a running app
when those assets are actually in use.
Now, there are a few strategies you can use
to help preserve your ODR content.
One of them is to avoid overpurging.
What do I mean by this?
Let me give you an example.
Let's say the system needs 100 megabytes,
and it's gone everywhere else
to find the space, it can't find it.
Through the attributes we discussed just a moment ago,
it's located one of your asset packs,
but that asset pack is 512 megabytes.
We are going to have to purge that asset pack
if it met the conditions so we will have overpurged
by 412 megabytes, so it's better to keep your asset pack smaller.
As I mentioned earlier, this is also good because you want
to progressively download and consume content,
so smaller is better there as well.
But smaller is also better here to avoid overpurging.
Another thing you might be tempted to do is
to tag everything with a 1.0 preservation priority,
but that doesn't really help you.
That just makes more of your assets look equivalent
to the system when it comes to it looking
at purging something from your app.
So you want to use the 1.0 value judiciously.
And finally, again, make sure, because we look
at the last used date that you endAccessingResources as soon
as you are done with the tags
or you allow the NSBundleResourceRequest object
to deallocate as soon as possible.
This helps the system know how you are using the assets
so it can make the best decisions.
Okay. Now, I would be remiss if I didn't talk
about performance in this talk.
One of the things that we have been doing here
in this session is to encourage you to download content
in the background ahead of time.
But we don't want to do that at the expense
of consuming more resources of the system
than your app can tolerate.
So we balance the speed of a download with the resources,
CPU and otherwise, that we consume while we download
and process these asset packs.
That's the default.
But if you get into a situation or you just want
to manage this all on your own and you want
to put full throttle down, then you can set it
with the urgent priority, and we will disregard that balancing,
and we will download the content as quickly as possible.
Again, as Tony mentioned, this is a good time to set this
when you are putting up the loading screen,
but your app actually may be tolerant to the CPU usage,
for example, that we have on the system when we are doing this.
So it's all up to you.
And also, we should talk about performance testing.
You need to do real world testing for an ODR application.
An ODR application is a network-based application.
So when you have your device connected via USB
to your MacBook Pro, for example, running Xcode,
that's not a real world scenario.
That's just far too fast.
So what you want to do is
to test your app using perhaps TestFlight or Xcode Server.
And then also use the Developer Tools Network Link Conditioner
to test out various networking conditions
that might cause issues for your app
in how soon ODR content is downloading.
In fact, I would recommend using this tool for any networking
that you are doing from your app.
It's really great.
If you haven't used it before, I would like to cover
that now just briefly.
So when you attach your device via USB
to your MacBook Pro again, for example, of running Xcode
and you then you go to Settings on your device you are going
to see this entry, a developer entry.
You tap that and you will see numerous numbers
of developer settings that you can use in logging, et cetera,
and in the middle there is the Network Link Conditioner.
So you tap that, and now you see the options that you can use
to cause various conditions to be present
when networking out of your app.
To use this, you enable the Network Link Conditioner,
and then you decide what type of situation you want to create.
Perhaps you want to mimic 100% loss or very slow network,
or perhaps a high latency in DNS lookups, or you just want
to reproduce a really bad network.
So this is a great tool
to use while you test your ODR application.
Okay. Now, speaking of networking issues,
you may encounter a couple of networking errors along the way.
One of them is this one.
Obviously there is no network.
So if your app needed ODR content and there is no network,
you need to be ready to handle this situation.
Another area you may encounter is resource unavailable,
and basically what this means is we thought the ODR content was
going to be in a certain location and it wasn't.
Generally the case or the cause of this will be, for example,
setting up a server inside of your own company
and someone is managing it, moving things around,
and they just weren't ready for the users to use that app.
So just to be ready for that error as well.
Another class of error that you can encounter relates
to storage space.
So ODR will allow your app to have a maximum of 2 gig
in use at any one time.
What this means is your app is running,
you have called beginAccessingResources on tags
that amount to 2 gigabytes,
you haven't called endAccessingResources on any
of those, you haven't allowed the NSBundleResourceRequest
to dealloc for any of those, so that 2 gig is in use.
And then you go and make another request.
That goes over and you are going to get an error
in your request, a callback.
So be aware of that as well.
Also, of course, we just might hit a low-space condition
on local storage on the device.
And you will get this notification.
This can happen if you have initiated a download
or maybe it happens long after you made that request.
So just be prepared to handle that.
To handle it you could do a couple of things;
you could endAccessingResources
on any ODR content you no longer need.
And you can also look at what you are storing
on the local device, perhaps some caches or other documents
that you actually don't need,
and you could free those up and delete those.
So now let's talk about cellular data.
Again, an ODR application is a network application.
So it is governed by the same cellular data switch
that controls the rest of your app's networking.
So if that switch is off, you will not be able
to get any ODR content.
If that switch is on, and you are downloading ODR content,
then any of that data that you use over cellular is going
to be attributed to your app.
So this should encourage you to make sure you ask
for what you need and not any more.
We don't want to run up a user's bill.
And finally, the 100-megabyte cellular download limit still
applies to ODR applications,
so remember where I had the dot app plus the size
of initial ODR adding to up the size
of the app listed in the App Store?
Well, if that's greater than 100 megabytes, your app is not going
to download over cellular, just like a normal app
that was greater than 100 megabytes would not.
So you need to be aware of that limitation as well.
Finally, as we begin to wrap this up,
there is some vital statistics that you need to be aware of.
First of all, although you can have an ODR application have a
size of up to 20 gig in the Store,
a maximum of 2 gig is reserved for your dot app.
So your dot app itself?
Remember when we teased apart the assets from your dot app,
we had the asset packs and then your dot app?
That dot app can be a maximum of 2 gigabytes.
The rest of it can be ODR content up to 20 gig.
And you can have a maximum of 2 gig of initial
and pre-fetched ODR content.
This is the content we set up in Xcode's UI
to download things during the app install
and right afterwards.
You can have a maximum of two gigabytes of that combined,
and as I mentioned just a moment ago, a maximum of two gigabytes
in use at any one time.
And finally, a given asset pack can be a maximum
of 512 megabytes.
So if you take a single tag and tag a bunch of resources,
and that adds up to more than 512 megabytes, Xcode is going
to give you a warning, but it will allow you to continue
to develop your app or game, but when you submit to the Store,
you are going to get a submission failure,
and the submission failure error message will explain why.
Okay. So in summary, On Demand Resources is a dynamically
loaded content system.
Hosted in the App Store.
This can be automated
to download content during app install time and by request.
You can prioritize or order these downloads.
We have an intelligent caching mechanism so that, for example,
we could get rid of that game Level 1 in order to make space
for a following level, and you get 20 gig in the Store.
So for more information we have a great new documentation
on ODR; we also have sample code,
and the developer forums are always a great place to go.
And if you still have questions you can follow
up with our App Frameworks evangelist.
You may have missed a couple of sessions
on other technologies that relate to ODR.
Those will be available via videos on the developer website.
And there is going to be a session as Tony indicated
about progress on Friday as well as a lab tomorrow from 11:00
to 1:30 dedicated to ODR.
Tony and I and the extended ODR engineering team will be there
to answer any of your questions,
get you started using ODR content,
and listen to any suggestions you might have.
Thank you very much!
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.