3

I need to save a video file to a temp directory and save the reference to it URL. Currently I have tried using fileManager to create a temp directory and then createFile(atPath: tempDirString, contents: vidData, attributes: nil). But I dont/am not able to save the full reference to the file.

What I have tried:

PHCachingImageManager().requestAVAsset(forVideo: (cell?.assetPH)!, options: nil) { (avAsset, _, _) in
    if let avAsset = avAsset {
        print(avAsset, " the avasset?")
        // vidAVAsset = avAsset

        let avPlayerItem = AVPlayerItem(asset: avAsset)
        let avPlayer = AVPlayer(playerItem: avPlayerItem)
        print(avPlayer, "<-- good?")

        let finalURL = self.urlOfCurrentlyPlayingInPlayer(player: avPlayer)
        print(finalURL, "<-- finalURL YEAH EYAHY YEAH?!?!?")
        // newUserTakenVideo.videoURL = finalURL

        let url = self.addVidToTempURL(vidURL: finalURL!)
        newUserTakenVideo.videoURL = url
        // let newUserTakenVideo = SelectedMedia(videoURL: finalURL, phAsset: (cell?.assetPH)!, thumbnailImg: (cell?.imageView.image)!, uniqueCellID: indexPath)
        // GlobalSharedData.shared.arrayOfCurrentCreateMedia.append(newUserTakenVideo)
    } else {
        print("did noot work")
    }
}

This is the function called:

func addVidToTempURL(vidURL: URL) -> URL? {
    let fileManager = FileManager.default
    let tempDir = fileManager.temporaryDirectory
    let tempDirString = tempDir.path
    do {
        print( "tempDir: \(tempDir)" )
        print( "tempDirString: \(tempDirString)" )
        if fileManager.fileExists(atPath: tempDirString ) {
            print( "tempDir exists" )
            do {
                try fileManager.createDirectory( at: tempDir, withIntermediateDirectories: true, attributes: nil )
                print( "tempDir created" )
                if fileManager.fileExists(atPath: tempDirString ) {
                    print( "tempDir exists" )
                    let vidData = try Data(contentsOf: vidURL)
                    fileManager.createFile(atPath: tempDirString, contents: vidData, attributes: nil)
                    print(tempDirString, " the stringfsdsda")
                    // fileManager.urls(for: fileManager.temporaryDirectory, in: fileManager.)
                    let url = URL(string: tempDirString)
                    return url
                } else {
                    print( "tempDir STILL DOES NOT exist" )
                    return nil
                }
            } catch {
                print( "tempDir NOT created" )
                return nil
            }
        } else {
            print( "tempDir DOES NOT exist" )
            do {
                try fileManager.createDirectory( at: tempDir, withIntermediateDirectories: true, attributes: nil )
                print( "tempDir created" )
                if fileManager.fileExists(atPath: tempDirString ) {
                    print( "tempDir exists" )
                    let vidData = try Data(contentsOf: vidURL)
                    fileManager.createFile(atPath: tempDirString, contents: vidData, attributes: nil)
                    print(tempDirString, " the fsdfdsdfsdfsfsd")
                    let url = URL(string: tempDirString)
                    return url
                } else {
                    print( "tempDir STILL DOES NOT exist" )
                    return nil
                }
            } catch {
                print( "tempDir NOT created" )
                return nil
            }
        }
    }
}

How can I get a URL reference to this files location?

I appreciate any help and can add more information if needed. Thanks, me

Pavel Stepanov
  • 891
  • 8
  • 13
  • As the name says it is temporary so as soon as your method finishes the system might (and will) delete it. So you are responsible for copying/moving your item to a regular folder before returning the url. Don't return the url from a tmp location. You wont find the file there anymore – Leo Dabus May 31 '20 at 04:37
  • So how can I store this video file somewhere to then retrieve later? It is a PHAsset, coming from users cam roll, as such it seems to go away unless saved. @LeoDabus –  May 31 '20 at 04:39
  • You have many options it all depends if you want your file to be accessible to the user or not https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html – Leo Dabus May 31 '20 at 04:39
  • @LeoDabus "depends if you want your file to be accessible to the user or not" No. I will upload the video to firebase –  May 31 '20 at 04:43
  • You can use app support directory https://stackoverflow.com/a/34701970/2303865 – Leo Dabus May 31 '20 at 04:43

1 Answers1

6

Since you don't seem to want your file to be visible to users or persisted between app launches, the Temporary directory sounds perfectly fine for your use case:

var tempVideoFileUrl: URL {
    return FileManager.default.temporaryDirectory.appendingPathComponent("my_video_name")
}

func storeVideoToTemporaryFolder(videoData: Data) {
    guard !FileManager.default.fileExists(atPath: tempVideoFileUrl.path) else {
        return
    }
    do {
        try videoData.write(to: tempVideoFileUrl)
    }
    catch {
        fatalError()
    }
}

func loadVideoFromTemporaryFolder() -> Data? {
    if let data = try? Data(contentsOf: tempVideoFileUrl) {
        return data
    }
    return nil
}

Worth mentioning though, the system may (and most likely will) purge this directory after the app is exited. It's recommended that you remove any temporary directories/files after they're no longer needed.

So in your case, you can simply remove it once you finished uploading to Firebase Storage:

func deleteVideoFromTemporaryFolder() {
    do {
        try FileManager.default.removeItem(at: videoFileUrl)
    }
    catch {
        fatalError()
    }
}

If you prefer to keep your file around between app launches though, you could use Application Support directory. But since Application Support and Documents directories gets automatically backed up, you may want to exclude your file from iCloud backup by setting its URL's isExcludedFromBackupKey key:

var applicationSupportVideoFileUrl: URL {
    let applicationSupportFolderUrl = try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
    return applicationSupportFolderUrl.appendingPathComponent("my_video_name")
}

func excludeFromCloudBackup(url: URL) {
    var targetUrl = url
    var isAlreadyExcludedFromBackup: Bool
    do {
        let storedRessourceValues = try targetUrl.resourceValues(forKeys: [URLResourceKey.isExcludedFromBackupKey])
        isAlreadyExcludedFromBackup = storedRessourceValues.isExcludedFromBackup ?? false
    }
    catch {
        fatalError()
    }
    guard !isAlreadyExcludedFromBackup else {
        return
    }
    var ressourceValues = URLResourceValues()
    ressourceValues.isExcludedFromBackup = true
    do {
        try targetUrl.setResourceValues(ressourceValues)
    }
    catch {
        fatalError()
    }
}

Edit: To get the data from your PHAsset, this should work:

import Photos

func loadVideoData(phAsset: PHAsset, completion: @escaping (Data?)->()) {
    guard phAsset.mediaType == .video else {
        return completion(nil)
    }
    let options = PHVideoRequestOptions()
    options.isNetworkAccessAllowed = true
    options.deliveryMode = .highQualityFormat
    PHCachingImageManager().requestAVAsset(forVideo: phAsset, options: options) { (avAsset, _, _) in
        guard let avUrlAsset = avAsset as? AVURLAsset else {
            return
        }
        var videoData: Data?
        do {
            videoData = try Data(contentsOf: avUrlAsset.url)
        } catch {
            fatalError()
        }
        DispatchQueue.main.async {
            completion(videoData)
        }
    }
}

Then simply call this method and store your video in the Temporary folder:

loadVideoData(phAsset: yourPhAsset) { [weak self] videoData in
    guard let strongSelf = self else { return }
    guard let videoData = videoData else {
        return
    }
    strongSelf.storeVideoToTemporaryFolder(videoData: videoData)
}
  • I tried the first one and it still does not work: let data = NSData(contentsOf: finalURL!) let newData = Data(referencing: data!) self.storeVideoToTemporaryFolder(videoData: newData) newUserTakenVideo.videoURL = self.tempVideoFileUrl –  May 31 '20 at 23:24
  • Can you please give me more details about what exactly doesn't work? The code that I posted to read/write from/to the temporary folder is working just fine, so there must be a different problem in your code, let's find out what it is! –  May 31 '20 at 23:50
  • Also, instead of using NSData, simply use Data(contentsOf: yourUrl) –  May 31 '20 at 23:55
  • Well I run the code I just sent you right bellow let finalURL = self in PHCachingImageManager().requestAVAsset code you can find in my Q above. I then run, pick a video then press next , where the video is supposed to play in a view. Instead however there is a crash claiming the video URL is nil –  May 31 '20 at 23:56
  • I just edited my answer to show you a working example on how you would get the data from the PHAsset, then store it in the Temp folder, let me know how it goes :) –  Jun 01 '20 at 01:16