On Demand Resources Video Tutorial in iOS9 with SpriteKit

On Demand Resources Video Tutorial in iOS9 with SpriteKit
September 5, 2015 Justin at CartoonSmart

On Demand Resources in iOS9 with SpriteKit

As of iOS9, all of us grateful dev-heads can trim the fat from our apps with On-Demand Resources. I know, I know, “more to learn” you’re thinking because it seems like this should be complicated, well think twice folks. On-Demand Resources (ODR for short) is easy to integrate and not only does it improve your own app’s install size (and chances of staying installed), this feature really benefits everybody. Imagine every app being a fraction of it’s current size. ODR does this by allowing your users to only download what they need, when they need it. Or to be exact, when you decide they need it. You determine exactly when you think your users are going to need more content spoon-fed to them and provide it. Sound cool?

Watch a video tutorial about On Demand Resources below, or continue reading below…

Tagging On-Demand Resources

We can drip content to iOS9 users by adding On Demand Resource Tags. Content includes: images, folders of images, movie, sound files, you know, the big stuff.  But you can even tag entire classes. Code is lightweight though, so I wouldn’t sweat the small stuff.  In the image below, you’ll see I’ve selected an image named Map, which is a slightly heftier file size compared to some others. Then in Xcode 7’s Attributes Inspector (circled in the top right), I’m giving it an On Demand Resource Tag name of MapTag.

So that’s how tagging works. Its quick. Simple, and kinda fun actually. Plus Apple is going to host all your tagged content for you. So even though you could host it yourself (I won’t get into that), Apple has made it a no-brainer to tag your content and let them worry about delivering it.

Tagging On Demand Resources iOS9, Xcode 7

And if you recognize that image from the Story Teller’s iOS Starter Kit 2, you’ve got a good eye! The kit now supports ODR, hence me writing this article.

Now that you’ve tagged a bunch of stuff…

Let’s think about when we need to present that content to the user. In Xcode 7 head over to the Resource Tags pane in your project settings (see the image below for reference) and take a look at where that MapTag ended up from the previous image…

Tagging On Demand Resources - Resource Tags Pane iOS9

By default the MapTag was added to the Download Only On Demand fold-down, which is probably where we want it.  You could though drag the tag into the other two options. Let’s talk about all three….

  • Initial Install Tags sounds like exactly what it is:  tags here are for content that will be installed initially by default. I wouldn’t worry about tagging your initial content though, because by NOT tagging it, it will be in the bundle like usual. And let’s assume you always want your app’s users to be able to go back to the intro screen, cover page, whatever and not worry about that content possibly being purged.
  • Prefetched Tag Order is for tagged content you don’t want to include in the initial install of the app, but you want this content to begin downloading immediately after the reader begins using the app / after it is installed (I’m not exactly sure which). The content is downloaded in the same order you list it (from top to bottom). This is a nice option if you want to get your initial binary size below, say, 20MB, but have another 70MB that won’t get used right away. It could still be essential content for the app, but as long as it isn’t needed in the start screen or first few game levels, you could safely exclude it.
  • Download Only On Demand is ideally what you’ll want if you don’t think the user can quickly get to the content after downloading the app. For example, if your puzzle book app has a natural reading order, and the reader can’t simply jump from Page 1 to Page 10. Perhaps they have to unlock pages 2-9 before 10 is even accessible (ahem, something our children’s book iOS kit supports). In this case, you can safely set that content to be downloaded as needed. For example, if the reader gets to Page 8, you might then began downloading tagged content for Page 10 since they are only a couple pages away (and you might want to give them a little buffer time in case their internet connection is slow).

Preloading On Demand Resources

So let’s take a look at some Swift 2 already. Be sure you are using Xcode 7 (or higher) to test.  Since you can load multiple tags at once, let’s create a function that accepts an array as a parameter, which will hold our tags.

Screen Shot 2015-09-02 at 6.12.39 PM

Look good? Okay, here’s the real meat of it…

Preloading On Demand Resources in Xcode7

Cut and paste version of this snippet


func preloadResourcesWithTag(tagArray:<Array>){

let tags = NSSet(array: tagArray)
let resourceRequest:NSBundleResourceRequest = NSBundleResourceRequest(tags: tags as! <Set>)
resourceRequest.beginAccessingResourcesWithCompletionHandler { (error) in

NSOperationQueue.mainQueue().addOperationWithBlock{

guard error == nil else {
print(error!);
return
}

print(“Preloading On-Demand Resources “)

}

}

}

If a problem occurs during the preload, you’ll hear about it via that guard statement and the error will get printed out. Otherwise, your tagged content will begin downloading and you’ll read about your success from the print statement. Since we are just preloading content here, there isn’t much else to do. This is about as simple as it gets since our resourceRequest is using the beginAccessingResourcesWithCompletionHandler and that simply downloads the assets. Since this is meant to preload your assets, there really isn’t anything to do with them after a successful download. From what I’ve read, the device isn’t going to purge them unnecessarily, but you can also set a priority to hold onto assets. 1.0 being a high priority, 0.0 being, well, I guess whenever the device feels like it. To do this, use NSBundle.mainBundle.()setPreservationPriority anytime after downloading the assets….

On Demand Resource Tag Priority

In your error statement, you might want to consider running some code to try preloading the content again. Most likely the error is because the user doesn’t have an internet connection at that time. But assuming you want to be sly about preloading content, you might not want to tell the user they are downloading anything in the background, so an unexpected message popping up that basically says “I’m doing something you didn’t know about and need you online” might seem a little odd.  If this were a SpriteKit game, I would probably consider running the code again via an SKAction that waits 30 seconds or a minute and retries.

Conditionally Checking for Already Downloaded Assets

Next lets look at a function that checks to see if the tagged content is already loaded, and if not, it will begin downloading it.  To keep things simple, in the example below we won’t worry about adding any kind of progress text (like 27% Loaded), but in the example source project, I’ve added to the GameScene.sks file an SKLabelNode named Progress that will note the current load progress. In the Xcode Simulator, you’d never see this though as the progress is immediate.

The setup is the same as before, we will pass an array of tags into a function….

iOS9 code snippet

And here’s the function…

Conditionally Loading On Demand Resources in Xcode7

Cut and paste version of this snippet


func loadResourcesWithTag(tagArray: < Array >){

let tags = NSSet(array: tagArray)
let resourceRequest:NSBundleResourceRequest = NSBundleResourceRequest(tags: tags as! < Set >)
resourceRequest.conditionallyBeginAccessingResourcesWithCompletionHandler {(resourcesAvailable: Bool) -> Void in
if resourcesAvailable {

print(“On Demand Resources already available”)

// Do something with the resources

} else {

resourceRequest.beginAccessingResourcesWithCompletionHandler {(err: NSError?) -> Void in
if let error = err {
print(“Error: (error)”)

} else {

print(“On Demand Resources downloaded, will now display them”)
// Do something with the resources

}
}
}
}

}

As you can see it’s pretty similar to preloading our assets.  You get two different flavors of error testing for the .beginAccessingResourcesWithCompletionHandler . Don’t ask me which is better. And the obvious change is we are now using .conditionallyBeginAccessingResourcesWithCompletionHandler and checking the value of resourcesAvailable. If it’s true, we can begin using the assets. Which of course can mean something different to everyone so I won’t expand much on that other than giving you a little hint below…

Resources Available On Demand Resources in Xcode7

Yup, just call a function elsewhere. This way you aren’t stuff a lot of other code inside of the function, and you can call that same function if they aren’t available and need to download the assets.

Stuff to Keep in Mind…

In the Simulator, tagged assets are going to get loaded immediately, because there’s no real downloading needed. You can run tests and enter bad tags to see if things do or don’t load, but if they do load, they load fast, and of course that’s not how things work in the real world. In the example files below, we’ve added a version with some progress text, but don’t expect to see it in the Simulator. If you test on your actual device, you can open the Developer  section of your Settings app, and simulate some slower speed downloading conditions.

For you SpriteKit developers, it is best to preload assets if you’ll be using them in an .SKS file. Meaning, you’ve designed your game, level, app, etc via the .sks file.   So do all your preloading on Level 1, well before Level2 or Level3’s scene file is ever in view (or transitioning).  If you layout your game’s content in an .sks file, then try to run the code above to download tagged textures when the scene is already in view,  your textures will already have come up missing. The scene won’t know to go “oh wait, those textures I was missing for this SKSpriteNode just got loaded, I’ll go back and fix it”. So you are better off preloading and then checking to see if the required assets for a particular scene are already available before the SKTransition to present the incoming scene occurs. You could of course do the transition as a result of the assets loading. Which is what occurs in the Story Tellers iOS Starter Kit.  In the kit, you don’t need to fuss with the code at all. You just need to specify via the property list which tags are required for a particular scene….

Resource Tags via Property List

CartoonSmart subscribers can download the source files for this example, along with a fancier version which includes an SKLabelNode to track the progress from right here…

This content is restricted to buyers of The CartoonSmart Subscription.

Never lose your place.

Sign up for the newsletter to get a free CartoonSmart account and track your progress in every course.

I'm cool. Sign me up!