iOS 9 Swift Programming Cookbook (2015)
Chapter 1. Swift 2.0, Xcode 7 and Interface Builder
1.5 Categorizing and Downloading Assets to Get Smaller Binaries
Problem
You have many assets in your app for various circumstances, and want to save storage space and network usage on each user’s device by shipping the app without the optional assets. Instead, you would want to dynamically download them and use them whenever needed.
Solution
Use Xcode to tag your assets and then use the NSBundleResourceRequest class to download them.
Discussion
For this recipe, I will create 3 packs of assets, each with three images in them. One pack may run for x3 screen scales, another for iPhone 6, and the last for iPhone 6+, for instance. I am taking very tiny clips of screenshots of my desktop to create these images--nothing special. The first pack will be called “level1”, the second “level2”, and the third “level3”.
NOTE
Use the GitHub repo of this book for a quick download of the said resources. Also, for the sake of simplicity, I am assuming that we are going to run this only on x3 scale screens such as iPhone 6+.
Place all nine images (three packs of three images) inside your Assets.xcassets file and name them as shown in Figure 1-1. Then select all the images in your first asset pack and open the attribute inspector. In the “On Demand Resource Tags” section of the inspector, enter “level1” and do the same thing for other levels--but of course bump the number up for each pack.
Figure 1-1. Name your assets as shown
Now, in your UI, place three buttons and three image views, hook the buttons’ actions to the code, and hook the image view references to the code.
@IBOutlet var img1: UIImageView!
@IBOutlet var img2: UIImageView!
@IBOutlet var img3: UIImageView!
var imageViews: [UIImageView]{
return [self.img1, self.img2, self.img3]
}
In order to find out whether the resource pack that you need has already been downloaded, call the conditionallyBeginAccessingResourcesWithCompletionHandler function on your resource request. Don’t blame me! I didn’t name this function. This will return a Boolean oftrue or false to tell you whether you have or don’t have access to the resource. If you don’t have access, you can simply download the resources with a call to the beginAccessingResourcesWithCompletionHandler function. This will return an error if one happens, or nil if everything goes well.
NOTE
We keep a reference to the request that we send for our asset pack so that the next time our buttons are tapped, we don’t have to check their availability again, but release the previously downloaded resources using the endAccessingResources function.
var currentResourcePack: NSBundleResourceRequest?
func displayImagesForResourceTag(tag: String){
NSOperationQueue.mainQueue().addOperationWithBlock{
for n in 0..<self.imageViews.count{
self.imageViews[n].image = UIImage(named: tag + "-\(n+1)")
}
}
}
func useLevel(lvl: UInt32){
let imageViews = [img1, img2, img3]
for img in imageViews{
img.image = nil
}
let tag = "level\(lvl)"
if let req = currentResourcePack{
req.endAccessingResources()
}
currentResourcePack = NSBundleResourceRequest(tags: [tag])
guard let req = currentResourcePack else {
return
}
req.conditionallyBeginAccessingResourcesWithCompletionHandler{available in
if available{
self.displayImagesForResourceTag(tag)
} else {
req.beginAccessingResourcesWithCompletionHandler{error in
guard error == nil else{
//TODO: you can handle the error here
return
}
self.displayImagesForResourceTag(tag)
}
}
}
}
@IBAction func useLevel3(sender: AnyObject) {
useLevel(3)
}
@IBAction func useLevel2(sender: AnyObject) {
useLevel(2)
}
@IBAction func useLevel1(sender: AnyObject) {
useLevel(1)
}
Run the code now in your simulator. When Xcode opens, go to the Debug Navigator (Command+6 key) and then click on the Disk section. You will see something like that shown in Figure .
Note how none of the asset packs are in use. Now in your UI, press the first button to get the first asset pack and watch how the first asset pack’s status will change to “In Use”. Once you switch from that pack to another, the previously chosen pack will be set to “Downloaded” and ready to be purged.
See Also