9

I'm trying to save both a recorded video's file path, and a thumbnail from the video to the documents directory. Then, set those two values to an object using the file paths so I can use the object to populate a collection view. With the code I have currently (below), after I record a video, the video path gets saved to the documents directory, and the video path and thumbnail get set to my Post object, and the thumbnail appears properly in my collection view. All good so far.

However only the video path persists between app re-launches since it's in the directory, and the thumbnail isn't. I'd like to save the thumbnail there too but I don't know how to go about it since it appears you can only write URLs to the directory.

This is my first experience with utilizing the documents directory so any help will be appreciated! How can I write the thumbnail (UIImage) to my documents directory along with the video it's from?

Here's my code so far:

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {

    let mediaType = info[UIImagePickerControllerMediaType] as! NSString
    dismiss(animated: true, completion: nil)

    if mediaType == kUTTypeMovie {
        // Componenets for a unique ID for the video
        var uniqueVideoID = ""
        var videoURL:NSURL? = NSURL()
        var uniqueID = ""
        uniqueID = NSUUID().uuidString

        // Get the path as URL
        videoURL = info[UIImagePickerControllerMediaURL] as? URL as NSURL?
        let myVideoVarData = try! Data(contentsOf: videoURL! as URL)

        // Write the video to the Document Directory at myVideoVarData (and set the video's unique ID)
        let docPaths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
        let documentsDirectory: AnyObject = docPaths[0] as AnyObject
        uniqueVideoID = uniqueID  + "VIDEO.MOV"
        let docDataPath = documentsDirectory.appendingPathComponent(uniqueVideoID) as String
        try? myVideoVarData.write(to: URL(fileURLWithPath: docDataPath), options: [])
        print("docDataPath under picker ", docDataPath)
        print("Video saved to documents directory")

        // Create a thumbnail image from the video (first frame)
        let asset = AVAsset(url: URL(fileURLWithPath: docDataPath))
        let assetImageGenerate = AVAssetImageGenerator(asset: asset)
        assetImageGenerate.appliesPreferredTrackTransform = true
        let time = CMTimeMake(asset.duration.value / 3, asset.duration.timescale)
        if let videoImage = try? assetImageGenerate.copyCGImage(at: time, actualTime: nil) {
            // Add thumbnail & video path to Post object
            let video = Post(pathToVideo: URL(fileURLWithPath: docDataPath), thumbnail: UIImage(cgImage: videoImage))
            posts.append(video)
            print("Video saved to Post object")
        }
    }
}
mfaani
  • 33,269
  • 19
  • 164
  • 293
KingTim
  • 1,281
  • 4
  • 21
  • 29
  • You Are Generating the image locally and saving that to your object, Since image is not saved in document directly or cache in any data base.. You won't get it back.. what you could do is make your cache manager that saves image in `DocumentDirectory` and then you can save that url in the same way as done for video, or save NSData of your image in database and render it back to UIImage.. – Ankit Mar 25 '17 at 13:15

1 Answers1

19

One Suggestion: Save images to Library/Caches if that can be downloaded again as per apple's guide line.


As simple as this:

func saveImageToDocumentDirectory(_ chosenImage: UIImage) -> String {
        let directoryPath =  NSHomeDirectory().appending("/Documents/")
        if !FileManager.default.fileExists(atPath: directoryPath) {
            do {
                try FileManager.default.createDirectory(at: NSURL.fileURL(withPath: directoryPath), withIntermediateDirectories: true, attributes: nil)
            } catch {
                print(error)
            }
        }
        let filename = NSDate().string(withDateFormatter: yyyytoss).appending(".jpg")
        let filepath = directoryPath.appending(filename)
        let url = NSURL.fileURL(withPath: filepath)
        do {
            try UIImageJPEGRepresentation(chosenImage, 1.0)?.write(to: url, options: .atomic)
            return String.init("/Documents/\(filename)")

        } catch {
            print(error)
            print("file cant not be save at path \(filepath), with error : \(error)");
            return filepath
        }
    }

Swift4:

func saveImageToDocumentDirectory(_ chosenImage: UIImage) -> String {
        let directoryPath =  NSHomeDirectory().appending("/Documents/")
        if !FileManager.default.fileExists(atPath: directoryPath) {
            do {
                try FileManager.default.createDirectory(at: NSURL.fileURL(withPath: directoryPath), withIntermediateDirectories: true, attributes: nil)
            } catch {
                print(error)
            }
        }

        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyyMMddhhmmss"

        let filename = dateFormatter.string(from: Date()).appending(".jpg")
        let filepath = directoryPath.appending(filename)
        let url = NSURL.fileURL(withPath: filepath)
        do {
            try chosenImage.jpegData(compressionQuality: 1.0)?.write(to: url, options: .atomic)
            return String.init("/Documents/\(filename)")

        } catch {
            print(error)
            print("file cant not be save at path \(filepath), with error : \(error)");
            return filepath
        }
    }
madlymad
  • 6,367
  • 6
  • 37
  • 68
Ashwin Kanjariya
  • 1,019
  • 2
  • 10
  • 22
  • Thanks, so this is converting it to a string then saving it? It looks like it works but I'm getting an error on the line `let filename = NSDate().string(withDateFormatter: yyyytoss).appending(".jpg")`, the `yyyytoss` doesn't seem to be recognized. – KingTim Mar 25 '17 at 13:26
  • yyyytoss is dateformater formate i.e. "yyyyMMddhhmmss", You can use any but this is best for me – Ashwin Kanjariya Mar 25 '17 at 13:28
  • Also, when I'm saving the thumbnail to my Post object (`let video = Post(pathToVideo: URL(fileURLWithPath: docDataPath), thumbnail: UIImage(cgImage: videoImage))`) I'll need to save it as this file path rather than just a UIImage now, how can I use the returned file path in there? – KingTim Mar 25 '17 at 13:28
  • yes i'm returning file path where our image gets save from document... dont save full path and path prefix can be change. – Ashwin Kanjariya Mar 25 '17 at 13:29
  • So I call `saveImageToDocumentDirectory(UIImage(cgImage: videoImage))` before I save to the object, but I'm wondering what to pass to the object instead of `UIImage(cgImage: videoImage)` now, i.e. how do I reference the file path – KingTim Mar 25 '17 at 13:31
  • And the date formatter keeps giving me errors, it doesn't seem to recognize anything I provide. Maybe it's a change in Swift 3? – KingTim Mar 25 '17 at 13:32
  • Ohh yes as I have used objective-c categories for date class, You can set logic for unique file name. – Ashwin Kanjariya Mar 25 '17 at 13:36
  • I'll have to look up how to format the date in Swift.. in regards to getting the file path, would it be something like `let thumbnailPath = saveImageToDocumentDirectory(UIImage(cgImage: videoImage))`? – KingTim Mar 25 '17 at 13:38
  • I'll have to have the thumbnail as UIImage for when I populate my collection view, so would I save it to my object as a URL (file path) instead of UIImage? In that case, how would I cast it back to UIImage when populating my collection view cells? – KingTim Mar 25 '17 at 13:39
  • yes correct, let thumbnailPath = saveImageToDocumentDirectory(UIImage(cgImage: videoImage)) – Ashwin Kanjariya Mar 25 '17 at 13:40
  • Ok so that saves it as a String, so I'll change thumbnail in my post object to a String. Now I need to populate my collection view image views with the thumbnails so how can I use the UIImage at that file path here `cell.postImage.image = posts[indexPath.row].thumbnail` – KingTim Mar 25 '17 at 13:43