Gabriel.Massana Gabriel.Massana - 3 months ago 55
Swift Question

How to stream a video with AVURLAsset and save to disk the cached data

Some days ago I was asked to check how difficult is to play a video while downloading it from Internet. I know it's an easy task because someone told me a while ago. So, I checked and it was super easy.

The problem was that I wanted to save to disk the video to do not force the user to download it again and again.

The problem was to access the buffer and store it to disk.

Many answers in Stackoverflow says it is nor possible. Specially with videos.

My original code to play the video:

import AVFoundation


//MARK: - Accessors

lazy var player: AVPlayer = {

var player: AVPlayer = AVPlayer(playerItem: self.playerItem)

player.actionAtItemEnd = AVPlayerActionAtItemEnd.None

return player

lazy var playerItem: AVPlayerItem = {

var playerItem: AVPlayerItem = AVPlayerItem(asset: self.asset)

return playerItem

lazy var asset: AVURLAsset = {

var asset: AVURLAsset = AVURLAsset(URL: self.url)

return asset

lazy var playerLayer: AVPlayerLayer = {

var playerLayer: AVPlayerLayer = AVPlayerLayer(player: self.player)

playerLayer.frame = UIScreen.mainScreen().bounds
playerLayer.backgroundColor = UIColor.clearColor().CGColor

return playerLayer

var url: NSURL = {

var url = NSURL(string: "")

return url!

//MARK: - ViewLifeCycle

override func viewDidLoad() {




The solution for this problem is to use AVAssetExportSession and AVAssetResourceLoaderDelegate:

First step is to add a notification to know when the video finish. Then we can start saving it to disk.

override func viewDidLoad() {


    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(playerItemDidReachEnd(_:)), name: AVPlayerItemDidPlayToEndTimeNotification, object: nil)


deinit {


The implementation of our function:

func playerItemDidReachEnd(notification: NSNotification) {

    if notification.object as? AVPlayerItem  == player.currentItem {

        let exporter = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality)

        let filename = "filename.mp4"

        let documentsDirectory = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: NSSearchPathDomainMask.UserDomainMask).last!

        let outputURL = documentsDirectory.URLByAppendingPathComponent(filename)

        exporter?.outputURL = outputURL
        exporter?.outputFileType = AVFileTypeMPEG4



Finally we need to make our AVURLAsset delegate of AVAssetResourceLoaderDelegate:

lazy var asset: AVURLAsset = {

    var asset: AVURLAsset = AVURLAsset(URL: self.url)

    asset.resourceLoader.setDelegate(self, queue: dispatch_get_main_queue())

    return asset


extension ViewController : AVAssetResourceLoaderDelegate {


I created a small demo with this code in GitHub.