How to save then retrieve image with specific name in Photo album? - ios

I have tried following ways but that does not save image with given names.
First Attempt: Successful but does not save with given name
var paths = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
var dir = paths[0] as! String
var filePath = "\\(dir)/imageName.jpg"
var saveImage = UIImage(contentsOfFile: filePath)
UIImageWriteToSavedPhotosAlbum(saveImage, self, nil, nil)
2nd Attempt: Failed to save image
var data = UIImageJPEGRepresentation(image, 1.0)
data.writeToFile(filePath, true)
Finally I had saved my Image to NSSearchPathDirectory.DocumentDirectory with specific name. Even when it is not visible, we can search file with specific name and open/show in UIImageView.As far as best of my knowledge, iPhone does not allow you to write or search by name Image to Photo gallery, programmatically. If we can, please tell me how.

Before you use this, its my advice that you please go through the photos framework of iOS.
Create a singleton class for the same -
class CreateCustomPhotoAlbum: NSObject
{
static let albumName = "Your album name"
static let sharedInstance = createCustomPhotoAlbum()
var assetCollection: PHAssetCollection!
override init() {
super.init()
if let assetCollection = fetchAssetCollectionForAlbum()
{
self.assetCollection = assetCollection
return
}
if PHPhotoLibrary.authorizationStatus() != PHAuthorizationStatus.Authorized
{
PHPhotoLibrary.requestAuthorization({ (status: PHAuthorizationStatus) -> Void in
status
})
}
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.Authorized {
self.createAlbum()
} else {
PHPhotoLibrary.requestAuthorization(requestAuthorizationHandler)
}
}
func requestAuthorizationHandler(status: PHAuthorizationStatus)
{
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.Authorized {
// ideally this ensures the creation of the photo album even if authorization wasn't prompted till after init was done
print("trying again to create the album")
self.createAlbum()
} else {
print("should really prompt the user to let them know it's failed")
}
}
func createAlbum() {
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
PHAssetCollectionChangeRequest.creationRequestForAssetCollectionWithTitle(createCustomPhotoAlbum.albumName) // create an asset collection with the album name
}) { success, error in
if success {
self.assetCollection = self.fetchAssetCollectionForAlbum()
} else {
print("error \(error)")
}
}
}
func fetchAssetCollectionForAlbum() -> PHAssetCollection!
{
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %#", createCustomPhotoAlbum.albumName)
let collection = PHAssetCollection.fetchAssetCollectionsWithType(.Album, subtype: .Any, options: fetchOptions)
if let _: AnyObject = collection.firstObject {
return collection.firstObject as! PHAssetCollection
}
return nil
}
func saveImage(image: UIImage)
{
if assetCollection == nil
{
return // if there was an error upstream, skip the save
}
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
let assetChangeRequest = PHAssetChangeRequest.creationRequestForAssetFromImage(image)
let assetPlaceHolder = assetChangeRequest.placeholderForCreatedAsset
let albumChangeRequest = PHAssetCollectionChangeRequest(forAssetCollection: self.assetCollection)
albumChangeRequest!.addAssets([assetPlaceHolder!])
}, completionHandler: nil)
}
}
Save Image
Just simply call this -
createCustomPhotoAlbum.sharedInstance.saveImage(imageName!)

Related

Save photo to a folder in Gallery using Swift

I am working on an iOS Swift 5 project and want to download image from my Project App to a folder in gallery. Folder name would be same as my App name and if doesn't exist in gallery, then code should first create App folder in gallery then save image to it.
Need help to resolve this.
as i implemented a year ago
got code from stackoverflow
import Photos
class PhotoManager {
static let instance = PhotoManager()
var folder: PHCollectionList?
/// Fetches an existing folder with the specified identifier or creates one with the specified name
func fetchFolderWithIdentifier(_ identifier: String, name: String) {
let fetchResult = PHCollectionList.fetchCollectionLists(withLocalIdentifiers: [identifier], options: nil)
guard let folder = fetchResult.firstObject else {
createFolderWithName(name)
return
}
self.folder = folder
}
/// Creates a folder with the specified name
private func createFolderWithName(_ name: String) {
var placeholder: PHObjectPlaceholder?
PHPhotoLibrary.shared().performChanges({
let changeRequest = PHCollectionListChangeRequest.creationRequestForCollectionList(withTitle: name)
placeholder = changeRequest.placeholderForCreatedCollectionList
}) { (success, error) in
guard let placeholder = placeholder else { return }
let fetchResult = PHCollectionList.fetchCollectionLists(withLocalIdentifiers: [placeholder.localIdentifier], options: nil)
guard let folder = fetchResult.firstObject else { return }
self.folder = folder
}
}
/// Creates an album with the specified name
private func createAlbumWithName(_ name: String, completion: #escaping (PHAssetCollection?) -> Void) {
guard let folder = folder else {
completion(nil)
return
}
var placeholder: PHObjectPlaceholder?
PHPhotoLibrary.shared().performChanges({
let listRequest = PHCollectionListChangeRequest(for: folder)
let createAlbumRequest = PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: name)
listRequest?.addChildCollections([createAlbumRequest.placeholderForCreatedAssetCollection] as NSArray)
placeholder = createAlbumRequest.placeholderForCreatedAssetCollection
}) { (success, error) in
guard let placeholder = placeholder else {
completion(nil)
return
}
let fetchResult = PHAssetCollection.fetchAssetCollections(withLocalIdentifiers: [placeholder.localIdentifier], options: nil)
let album = fetchResult.firstObject
completion(album)
}
}
/// Saves the image to a new album with the specified name
func saveImageToAlbumInRootFolder(_ albumName: String, image: UIImage?, completion: #escaping (Error?) -> Void) {
createAlbumWithName(albumName) { (album) in
guard let album = album else {
return
}
PHPhotoLibrary.shared().performChanges({
let albumChangeRequest = PHAssetCollectionChangeRequest(for: album)
let createAssetRequest = PHAssetChangeRequest.creationRequestForAsset(from: image!)
let photoPlaceholder = createAssetRequest.placeholderForCreatedAsset!
albumChangeRequest?.addAssets([photoPlaceholder] as NSArray)
}, completionHandler: { (success, error) in
if success {
completion(nil)
} else if let error = error {
// Failed with error
} else {
// Failed with no error
}
})
}
}}
currently i am working on a project in office that's why i am not providing you the exact solution but you can figure it out easily from the above class ...
you can use it PhotoManager.instance.saveImageToAlbumInRootFolder(_ albumName: String, image: yourImage) {completion}
import Foundation
import Photos
class CustomPhotoAlbum: NSObject {
static let albumName = "Your App Name"
static let sharedInstance = CustomPhotoAlbum()
var assetCollection: PHAssetCollection!
override init() {
super.init()
if let assetCollection = fetchAssetCollectionForAlbum() {
self.assetCollection = assetCollection
return
}
if PHPhotoLibrary.authorizationStatus() != PHAuthorizationStatus.authorized {
PHPhotoLibrary.requestAuthorization({ (status: PHAuthorizationStatus) -> Void in
()
})
}
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized {
self.createAlbum()
} else {
PHPhotoLibrary.requestAuthorization(requestAuthorizationHandler)
}
}
func requestAuthorizationHandler(status: PHAuthorizationStatus) {
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized {
// ideally this ensures the creation of the photo album even if authorization wasn't prompted till after init was done
print("trying again to create the album")
self.createAlbum()
} else {
print("should really prompt the user to let them know it's failed")
}
}
func createAlbum() {
PHPhotoLibrary.shared().performChanges({
PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: CustomPhotoAlbum.albumName) // create an asset collection with the album name
}) { success, error in
if success {
self.assetCollection = self.fetchAssetCollectionForAlbum()
} else {
print("error \(error)")
}
}
}
func fetchAssetCollectionForAlbum() -> PHAssetCollection? {
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %#", CustomPhotoAlbum.albumName)
let collection = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions)
if let _: AnyObject = collection.firstObject {
return collection.firstObject
}
return nil
}
func save(image: UIImage) {
if assetCollection == nil {
return // if there was an error upstream, skip the save
}
PHPhotoLibrary.shared().performChanges({
let assetChangeRequest = PHAssetChangeRequest.creationRequestForAsset(from: image)
let assetPlaceHolder = assetChangeRequest.placeholderForCreatedAsset
let albumChangeRequest = PHAssetCollectionChangeRequest(for: self.assetCollection)
let enumeration: NSArray = [assetPlaceHolder!]
albumChangeRequest!.addAssets(enumeration)
}, completionHandler: nil)
}
}
how to use it
func haiJeenyKaMaqsadAuronKKamAna() {
CustomPhotoAlbum.sharedInstance.createAlbum()
CustomPhotoAlbum.sharedInstance.save(image: UIImage(named: "help")!)
}
now this is working fine as i copied it from my old project

Unable to Save UIImage to Camera Roll in Swift 5 XCode 11.6

I am trying to save generated UIImage to Camera Roll.
I have necessary privacy string in Info.plist.
I tried couple of methods;
The error I am facing for PHPhotoLibrary is
Error Domain=com.apple.accounts Code=7 "(null)
The codes I have tried;
UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil);
The other one is;
PHPhotoLibrary.shared().savePhoto(image: image!, albumName: "AlbumName", completion: { (asset) in
if asset != nil
{
print("error")
}
})
func savePhoto(image:UIImage, albumName:String, completion:((PHAsset?)->())? = nil) {
func save() {
if let album = PHPhotoLibrary.shared().findAlbum(albumName: albumName) {
PHPhotoLibrary.shared().saveImage(image: image, album: album, completion: completion)
} else {
PHPhotoLibrary.shared().createAlbum(albumName: albumName, completion: { (collection) in
if let collection = collection {
PHPhotoLibrary.shared().saveImage(image: image, album: collection, completion: completion)
} else {
completion?(nil)
}
})
}
}
if PHPhotoLibrary.authorizationStatus() == .authorized {
save()
} else {
PHPhotoLibrary.requestAuthorization({ (status) in
if status == .authorized {
save()
}
})
}
}
// MARK: - Private
fileprivate func findAlbum(albumName: String) -> PHAssetCollection? {
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %#", albumName)
let fetchResult : PHFetchResult = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions)
guard let photoAlbum = fetchResult.firstObject else {
return nil
}
return photoAlbum
}
fileprivate func createAlbum(albumName: String, completion: #escaping (PHAssetCollection?)->()) {
var albumPlaceholder: PHObjectPlaceholder?
PHPhotoLibrary.shared().performChanges({
let createAlbumRequest = PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: albumName)
albumPlaceholder = createAlbumRequest.placeholderForCreatedAssetCollection
}, completionHandler: { success, error in
if success {
guard let placeholder = albumPlaceholder else {
completion(nil)
return
}
let fetchResult = PHAssetCollection.fetchAssetCollections(withLocalIdentifiers: [placeholder.localIdentifier], options: nil)
guard let album = fetchResult.firstObject else {
completion(nil)
return
}
completion(album)
} else {
completion(nil)
}
})
}
fileprivate func saveImage(image: UIImage, album: PHAssetCollection, completion:((PHAsset?)->())? = nil) {
var placeholder: PHObjectPlaceholder?
PHPhotoLibrary.shared().performChanges({
let createAssetRequest = PHAssetChangeRequest.creationRequestForAsset(from: image)
guard let albumChangeRequest = PHAssetCollectionChangeRequest(for: album),
let photoPlaceholder = createAssetRequest.placeholderForCreatedAsset else { return }
placeholder = photoPlaceholder
let fastEnumeration = NSArray(array: [photoPlaceholder] as [PHObjectPlaceholder])
albumChangeRequest.addAssets(fastEnumeration)
}, completionHandler: { success, error in
guard let placeholder = placeholder else {
completion?(nil)
return
}
if success {
let assets:PHFetchResult<PHAsset> = PHAsset.fetchAssets(withLocalIdentifiers: [placeholder.localIdentifier], options: nil)
let asset:PHAsset? = assets.firstObject
completion?(asset)
} else {
completion?(nil)
}
})
}
Could you please help me on figuring out this?
BR,
Erdem
I have figured it out.
The problem is I have been creating QR Image that is created with CIFilter is CIImage.
It returns error code = -1
You need to create UIImage from CGImage not CIImage
if let output = filter.outputImage?.transformed(by: transform) {
let context = CIContext()
guard let cgImage = context.createCGImage(output, from: output.extent) else { return nil }
return UIImage(cgImage: cgImage)
}

PHAssetResource.assetResources returns zero results with valid PHAsset

I'm trying to get the asset resources for a PHAsset and am having a little trouble. I'm able to fetch the PHAsset with no problems, yet the resources for the asset is always empty. This is not happening with every photo, but one in particular has been giving me troubles. Curious if there's something I'm missing and this is expected behaviour in certain contexts.
import UIKit
import Photos
import PhotosUI
class ViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
requestPhotoLibraryAuthorization() { success in
let identifier = "E218D471-E616-4E39-99C3-624D13D9B661/L0/001"
let assets = self.fetchPHAssets(identifiers: [identifier])
guard assets.count > 0 else { return }
let imageOptions = PHImageRequestOptions()
imageOptions.deliveryMode = .highQualityFormat
imageOptions.isNetworkAccessAllowed = true
PHImageManager.default().requestImage(for: assets[0], targetSize: PHImageManagerMaximumSize, contentMode: .default, options: imageOptions) { (image, info) in
print(String(describing: info)) // never called
}
self.saveAssetToDisk(asset: assets[0]) { image in
guard let image = image else { return }
self.imageView.image = image
}
}
}
func fetchPHAssets(identifiers: [String]) -> [PHAsset] {
let options = PHFetchOptions()
options.includeAllBurstAssets = false
options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
options.fetchLimit = identifiers.count
options.includeAssetSourceTypes = [.typeCloudShared, .typeiTunesSynced, .typeUserLibrary]
let assetResult = PHAsset.fetchAssets(withLocalIdentifiers: identifiers, options: options)
var assetArray:[PHAsset] = []
for i in 0..<assetResult.count {
let asset = assetResult[i]
assetArray.append(asset)
}
return assetArray
}
func saveAssetToDisk(asset: PHAsset, onComplete: #escaping (UIImage?)->()) {
let resources = PHAssetResource.assetResources(for: asset)
guard resources.count > 0 else { return }
let resource = resources[0]
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentUrl = urls[0]
let resourceUrl = documentUrl.appendingPathComponent(resource.originalFilename, isDirectory: false)
// write image
let options = PHAssetResourceRequestOptions()
options.isNetworkAccessAllowed = true
if !FileManager.default.fileExists(atPath: resourceUrl.path) {
PHAssetResourceManager.default().writeData(for: resource, toFile: resourceUrl, options: options) { error in
if error == nil {
guard let image = UIImage(contentsOfFile: resourceUrl.path) else { return }
onComplete(image)
}
}
} else {
onComplete(UIImage(contentsOfFile: resourceUrl.path))
}
}
let requestPhotoLibraryKey = "hasRequestedPhotoAccess"
func requestPhotoLibraryAuthorization(onComplete: #escaping (Bool) -> ()) {
let status = PHPhotoLibrary.authorizationStatus()
if status == .notDetermined {
PHPhotoLibrary.requestAuthorization { status in
if status == PHAuthorizationStatus.authorized {
UserDefaults.standard.set(true, forKey: self.requestPhotoLibraryKey)
onComplete(true)
}
}
} else if status == .authorized {
UserDefaults.standard.set(true, forKey: requestPhotoLibraryKey)
onComplete(true)
} else if status == .denied {
onComplete(false)
}
}
}
Was not seeing the error message describe in Photos Framework requestImageDataForAsset occasionally fails, but I was able to track down that the image in question was apart of an iCloud shared album. Doing as they suggests fixed my issue.

Get Specific Photo from Specific Photo Album with PHAssetCollection in Swift 4

I am storing the photo with the use of following code. But now I have to get the photos from storage using name.
First Question: How can I store the photos using name ? Should I store it in Core Data ?
Second Question: How can I get that photo using the name I have given it previously ?
Please Help me. Thank you for all replies ...
class CustomPhotoAlbum: NSObject {
static let albumName = "PhotoAlbumName"
static let sharedInstance = CustomPhotoAlbum()
var assetCollection: PHAssetCollection!
override init() {
super.init()
if let assetCollection = fetchAssetCollectionForAlbum() {
self.assetCollection = assetCollection
return
}
if PHPhotoLibrary.authorizationStatus() != PHAuthorizationStatus.authorized {
PHPhotoLibrary.requestAuthorization({ (status: PHAuthorizationStatus) -> Void in
()
})
}
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized {
self.createAlbum()
} else {
PHPhotoLibrary.requestAuthorization(requestAuthorizationHandler)
}
}
func requestAuthorizationHandler(status: PHAuthorizationStatus) {
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized {
// ideally this ensures the creation of the photo album even if authorization wasn't prompted till after init was done
print("trying again to create the album")
self.createAlbum()
} else {
print("should really prompt the user to let them know it's failed")
}
}
func createAlbum() {
PHPhotoLibrary.shared().performChanges({
PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: CustomPhotoAlbum.albumName) // create an asset collection with the album name
}) { success, error in
if success {
self.assetCollection = self.fetchAssetCollectionForAlbum()
} else {
print("error \(String(describing: error))")
}
}
}
func fetchAssetCollectionForAlbum() -> PHAssetCollection? {
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %#", CustomPhotoAlbum.albumName)
let collection = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions)
if let _: AnyObject = collection.firstObject {
return collection.firstObject
}
return nil
}
func save(image: UIImage) {
if assetCollection == nil {
return // if there was an error upstream, skip the save
}
PHPhotoLibrary.shared().performChanges({
let assetChangeRequest = PHAssetChangeRequest.creationRequestForAsset(from: image)
let assetPlaceHolder = assetChangeRequest.placeholderForCreatedAsset
let albumChangeRequest = PHAssetCollectionChangeRequest(for: self.assetCollection)
let enumeration: NSArray = [assetPlaceHolder!]
albumChangeRequest!.addAssets(enumeration)
}, completionHandler: nil)
}
}
You can use this class .
import Foundation
import Photos
class FetchPhotos{
var images:[UIImage] = []
var imageUrl:String!
func fetchPhotos() -> String{
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate", ascending: false)]
// Fetch the image assets
let fetchResult: PHFetchResult = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: fetchOptions)
// If the fetch result isn't empty,
// proceed with the image request
if fetchResult.count > 0 {
let totalImageCountNeeded = 1 // <-- The number of images to fetch
fetchPhotoAtIndex(0, totalImageCountNeeded, fetchResult)
}
return imageUrl
}
// Repeatedly call the following method while incrementing
// the index until all the photos are fetched
func fetchPhotoAtIndex(_ index:Int, _ totalImageCountNeeded: Int, _ fetchResult: PHFetchResult<PHAsset>) {
let albumName = "CustonAlbum"
// Note that if the request is not set to synchronous
// the requestImageForAsset will return both the image
// and thumbnail; by setting synchronous to true it
// will return just the thumbnail
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = true
let collection: PHFetchResult =
PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: nil)
for k in 0 ..< collection.count {
let obj:AnyObject! = collection.object(at: k)
if obj.title == albumName {
print("Yeap!")
// Perform the image request
PHImageManager.default().requestImage(for: fetchResult.object(at: index) as PHAsset, targetSize: CGSize(width: 1000, height: 1000), contentMode: PHImageContentMode.aspectFill, options: requestOptions, resultHandler: { (image, info) in
if let image = image {
// Add the returned image to your array
self.images += [image]
}
// If you haven't already reached the first
// index of the fetch result and if you haven't
// already stored all of the images you need,
// perform the fetch request again with an
// incremented index
if index + 1 < fetchResult.count && self.images.count < totalImageCountNeeded {
self.fetchPhotoAtIndex(index + 1, totalImageCountNeeded, fetchResult)
} else {
// Else you have completed creating your array
print("Completed array: \(self.images[0])")
print("Image URL \(String(describing: info!["PHImageFileURLKey"]))")
self.imageUrl = "\(info!["PHImageFileURLKey"]!)"
}
})
}}
}
}

Create folder in custom photo album

I have a custom album in which my app saves picture from camera. I am wondering if there is a way I can create folders inside my album so I can stack certain images inside it?
ANSWER
As fellow members mentioned, sadly there is no way you can create a folder inside album.
You should try code below. It's Swift 3.0 syntaxe. :)
import Foundation
import Photos
class CustomPhotoAlbum: NSObject {
static let albumName = "Album Name"
static let sharedInstance = CustomPhotoAlbum()
var assetCollection: PHAssetCollection!
override init() {
super.init()
if let assetCollection = fetchAssetCollectionForAlbum() {
self.assetCollection = assetCollection
return
}
if PHPhotoLibrary.authorizationStatus() != PHAuthorizationStatus.authorized {
PHPhotoLibrary.requestAuthorization({ (status: PHAuthorizationStatus) -> Void in
()
})
}
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized {
self.createAlbum()
} else {
PHPhotoLibrary.requestAuthorization(requestAuthorizationHandler)
}
}
func requestAuthorizationHandler(status: PHAuthorizationStatus) {
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized {
// ideally this ensures the creation of the photo album even if authorization wasn't prompted till after init was done
print("trying again to create the album")
self.createAlbum()
} else {
print("should really prompt the user to let them know it's failed")
}
}
func createAlbum() {
PHPhotoLibrary.shared().performChanges({
PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: CustomPhotoAlbum.albumName) // create an asset collection with the album name
}) { success, error in
if success {
self.assetCollection = self.fetchAssetCollectionForAlbum()
} else {
print("error \(error)")
}
}
}
func fetchAssetCollectionForAlbum() -> PHAssetCollection? {
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %#", CustomPhotoAlbum.albumName)
let collection = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: fetchOptions)
if let _: AnyObject = collection.firstObject {
return collection.firstObject
}
return nil
}
func save(image: UIImage) {
if assetCollection == nil {
return // if there was an error upstream, skip the save
}
PHPhotoLibrary.shared().performChanges({
let assetChangeRequest = PHAssetChangeRequest.creationRequestForAsset(from: image)
let assetPlaceHolder = assetChangeRequest.placeholderForCreatedAsset
let albumChangeRequest = PHAssetCollectionChangeRequest(for: self.assetCollection)
let enumeration: NSArray = [assetPlaceHolder!]
albumChangeRequest!.addAssets(enumeration)
}, completionHandler: nil)
}
}
You can add albums within folders in the user's Photos Albums. I just figured out how to do it, today:
import Photos
class PhotoManager {
static let instance = PhotoManager()
var folder: PHCollectionList?
/// Fetches an existing folder with the specified identifier or creates one with the specified name
func fetchFolderWithIdentifier(_ identifier: String, name: String) {
let fetchResult = PHCollectionList.fetchCollectionLists(withLocalIdentifiers: [identifier], options: nil)
guard let folder = fetchResult.firstObject else {
createFolderWithName(name)
return
}
self.folder = folder
}
/// Creates a folder with the specified name
private func createFolderWithName(_ name: String) {
var placeholder: PHObjectPlaceholder?
PHPhotoLibrary.shared().performChanges({
let changeRequest = PHCollectionListChangeRequest.creationRequestForCollectionList(withTitle: name)
placeholder = changeRequest.placeholderForCreatedCollectionList
}) { (success, error) in
guard let placeholder = placeholder else { return }
let fetchResult = PHCollectionList.fetchCollectionLists(withLocalIdentifiers: [placeholder.localIdentifier], options: nil)
guard let folder = fetchResult.firstObject else { return }
self.folder = folder
}
}
/// Creates an album with the specified name
private func createAlbumWithName(_ name: String, completion: #escaping (PHAssetCollection?) -> Void) {
guard let folder = folder else {
completion(nil)
return
}
var placeholder: PHObjectPlaceholder?
PHPhotoLibrary.shared().performChanges({
let listRequest = PHCollectionListChangeRequest(for: folder)
let createAlbumRequest = PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: name)
listRequest?.addChildCollections([createAlbumRequest.placeholderForCreatedAssetCollection] as NSArray)
placeholder = createAlbumRequest.placeholderForCreatedAssetCollection
}) { (success, error) in
guard let placeholder = placeholder else {
completion(nil)
return
}
let fetchResult = PHAssetCollection.fetchAssetCollections(withLocalIdentifiers: [placeholder.localIdentifier], options: nil)
let album = fetchResult.firstObject
completion(album)
}
}
/// Saves the image to a new album with the specified name
func saveImageToAlbumInRootFolder(_ albumName: String, image: UIImage?, completion: #escaping (Error?) -> Void) {
createAlbumWithName(albumName) { (album) in
guard let album = album else {
return
}
PHPhotoLibrary.shared().performChanges({
let albumChangeRequest = PHAssetCollectionChangeRequest(for: album)
let createAssetRequest = PHAssetChangeRequest.creationRequestForAsset(from: image!)
let photoPlaceholder = createAssetRequest.placeholderForCreatedAsset!
albumChangeRequest?.addAssets([photoPlaceholder] as NSArray)
}, completionHandler: { (success, error) in
if success {
completion(nil)
} else if let error = error {
// Failed with error
} else {
// Failed with no error
}
})
}
}
}
This allows you to do something like this:
let defaults = UserDefaults.standard
let identifier = defaults.string(forKey: "myFolder")!
PhotoManager.instance.fetchFolderWithIdentifier(identifier, name: "My Folder")
PhotoManager.instance.saveImageToAlbumInRootFolder("My Album", image: UIImage(named: "my_image")) { (error) in
// Handle error
}

Resources