Skip to content

Instantly share code, notes, and snippets.

@dennislysenko
Last active December 10, 2019 02:54
Show Gist options
  • Save dennislysenko/5388cacb83b754e8983e99bff7fef2d2 to your computer and use it in GitHub Desktop.
Save dennislysenko/5388cacb83b754e8983e99bff7fef2d2 to your computer and use it in GitHub Desktop.
Simplifies saving media on iOS to an app-specific collection in the Photo Library
//
// SimpleMediaSaver.swift
//
// Created by Dennis Lysenko on 27-09-16.
// Copyright © 2016 Dennis Lysenko. All rights reserved.
// https://gist.github.com/dennislysenko/5388cacb83b754e8983e99bff7fef2d2
//
// This gist is licensed under the terms of the MIT license.
//
import Foundation
import Photos
public struct PhotoLibraryError: Error {
let reason: String
var localizedDescription: String {
return "No placeholder on Photo library change request...maybe missing permissions or disk space?\nReason: \(reason)"
}
}
public class SimpleMediaSaver {
public static func getPhotosCollection(withName name: String) throws -> PHAssetCollection {
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %@", name)
let results = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions)
if let collection = results.firstObject {
return collection
} else {
do {
var maybePlaceholder: PHObjectPlaceholder?
try PHPhotoLibrary.shared().performChangesAndWait {
let createRequest = PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: name)
maybePlaceholder = createRequest.placeholderForCreatedAssetCollection
}
guard let placeholder = maybePlaceholder else {
throw PhotoLibraryError(reason: "No placeholder for collection creation request")
}
let collectionFetchRequest = PHAssetCollection.fetchAssetCollections(withLocalIdentifiers: [placeholder.localIdentifier], options: nil)
if let collection = collectionFetchRequest.firstObject {
return collection
} else {
throw PhotoLibraryError(reason: "No created asset")
}
}
}
}
private static func saveImage(_ image: UIImage) throws -> PHObjectPlaceholder {
var blockPlaceholder: PHObjectPlaceholder?
try PHPhotoLibrary.shared().performChangesAndWait {
let changeRequest = PHAssetChangeRequest.creationRequestForAsset(from: image)
changeRequest.creationDate = Date()
blockPlaceholder = changeRequest.placeholderForCreatedAsset
}
guard let placeholder = blockPlaceholder else {
throw PhotoLibraryError(reason: "No placeholder for asset creation request")
}
return placeholder
}
private static func saveVideo(at fileURL: URL) throws -> PHObjectPlaceholder {
var blockPlaceholder: PHObjectPlaceholder?
try PHPhotoLibrary.shared().performChangesAndWait {
let changeRequest = PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: fileURL)
changeRequest?.creationDate = Date()
blockPlaceholder = changeRequest?.placeholderForCreatedAsset
}
guard let placeholder = blockPlaceholder else {
throw PhotoLibraryError(reason: "No placeholder for asset creation request, or no asset request (check if video exists at \(fileURL))")
}
return placeholder
}
public static func saveImage(_ image: UIImage, toCollectionWithName collectionName: String) throws -> String {
let placeholder = try saveImage(image)
let collection = try getPhotosCollection(withName: collectionName)
try PHPhotoLibrary.shared().performChangesAndWait {
let request = PHAssetCollectionChangeRequest(for: collection)
request?.addAssets([placeholder] as NSArray)
}
return placeholder.localIdentifier
}
public static func saveVideo(at fileURL: URL, toCollectionWithName collectionName: String) throws -> String {
let placeholder = try saveVideo(at: fileURL)
let collection = try getPhotosCollection(withName: collectionName)
try PHPhotoLibrary.shared().performChangesAndWait {
let request = PHAssetCollectionChangeRequest(for: collection)
request?.addAssets([placeholder] as NSArray)
}
return placeholder.localIdentifier
}
}
@rromanchuk
Copy link

This is absolutely insane, but this was the only way i could successfully save a complex encoded HEVC I'm honestly at a loss for words hahaha. I don't have the slightest clue, i was using the following asynchronous style methods

PHPhotoLibrary.shared().performChanges({
    PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: url)
}) { saved, error in
    
}

But would only get Error Domain=NSCocoaErrorDomain Code=-1 "(null)"

There has to be something subtle going on in usage that performChangesAndWait changes the outcome.

@dennislysenko
Copy link
Author

@rromanchuk honestly I have been using this in production on tons of projects since I posted it and I'm just as floored as you are that years on it works so consistently and doesn't seem to present issues

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment