Downloading and Preparing Remote Media for Playback - Multimedia - iOS 9 Swift Programming Cookbook (2015)

iOS 9 Swift Programming Cookbook (2015)

Chapter 12. Multimedia

12.2 Downloading and Preparing Remote Media for Playback

Problem

You have some remote assets such as sound files and would like to download them, even if in the background. Along the way, you wnat to provide real-time feedback of the download process.

Solution

Follow these steps:

1. Create an instance of AVURLAsset with the URL to your asset.

2. Use the backgroundSessionConfigurationWithIdentifier(_:) class method on NSURLSessionConfiguration to create a background session configuration.

3. Create a session of type AVAssetDownloadURLSession and pass your configuration to it.

4. Construct the URL where your asset has to be downloaded onto the disk.

5. Use the assetDownloadTaskWithURLAsset(_:destinationURL:options) method of your session to create a download task of type AVAssetDownloadTask.

6. Call the resume() method on your task to start the task.

7. Conform to the AVAssetDownloadDelegate protocol to get events from your task.

NOTE

All the classes I discussed whose names start with “AV” are in the AVFoundation framework, so make sure to import it.

Discussion

Let’s imagine that you have an mp4 file that you want to download and play back in your app. Firstt set up your vc:

import UIKit

import AVFoundation

class ViewController: UIViewController, AVAssetDownloadDelegate {

let url = NSURL(string: "http://localhost:8888/video.mp4")!

let sessionId = "com.mycompany.background"

let queue = NSOperationQueue()

var task: AVAssetDownloadTask?

var session: AVAssetDownloadURLSession?

...

NOTE

I am using MAMP to start a local server on my machine and host the file video.mp4 on my own computer; hence the URL that you are seeing. You can and probably should change this URL to a valid media file that AVFoundation can handle, like mov or mp4.

Now define some of the delegate methods defined in AVAssetDownloadDelegate and NSURLSessionTaskDelegate:

func URLSession(session: NSURLSession, task: NSURLSessionTask,

didCompleteWithError error: NSError?) {

//code this

}

func URLSession(session: NSURLSession,

assetDownloadTask: AVAssetDownloadTask,

didLoadTimeRange timeRange: CMTimeRange,

totalTimeRangesLoaded loadedTimeRanges: [NSValue],

timeRangeExpectedToLoad: CMTimeRange) {

//code this

}

func URLSession(session: NSURLSession,

assetDownloadTask: AVAssetDownloadTask,

didResolveMediaSelection resolvedMediaSelection: AVMediaSelection) {

}

Next, create an asset by its URL. At the same time, tell the system that you don’t want cross-site references to be resolved using a dictionary with a key equal to AVURLAssetReferenceRestrictionsKey and value ofAVAssetReferenceRestrictions.ForbidCrossSiteReference:

let options = [AVURLAssetReferenceRestrictionsKey :

AVAssetReferenceRestrictions.ForbidCrossSiteReference.rawValue]

let asset = AVURLAsset(URL: url, options: options)

Now it’s time to create the configuration object of type NSURLSessionConfiguration:

let config = NSURLSessionConfiguration

.backgroundSessionConfigurationWithIdentifier(sessionId)

Create the session of type AVAssetDownloadURLSession:

let session = AVAssetDownloadURLSession(configuration: config,

assetDownloadDelegate: self, delegateQueue: queue)

self.session = session

NOTE

You must have noticed that I keep a reference to the session and the task that we are going to create soon. This is so that we can refer to them later and cancel or reuse them if necessary.

Now construct the URL where you want the video to be downloaded on disk, using NSFileManager:

let fm = NSFileManager()

let destinationUrl = try! fm.URLForDirectory(.CachesDirectory,

inDomain: .UserDomainMask, appropriateForURL: url, create: true)

.URLByAppendingPathComponent("file.mp4")

And last but not least, construct the task and start it:

guard let task = session.assetDownloadTaskWithURLAsset(asset,

destinationURL: destinationUrl, options: nil) else {

print("Could not create the task")

return

}

self.task = task

task.resume()

See Also