Save images with phimagemanager to custom album?

Solution 1:

This is how I do:

At the top:

import Photos

var image: UIImage!
var assetCollection: PHAssetCollection!
var albumFound : Bool = false
var photosAsset: PHFetchResult!
var assetThumbnailSize:CGSize!
var collection: PHAssetCollection!
var assetCollectionPlaceholder: PHObjectPlaceholder!

Creating the album:

func createAlbum() {
    //Get PHFetch Options
    let fetchOptions = PHFetchOptions()
    fetchOptions.predicate = NSPredicate(format: "title = %@", "camcam")
    let collection : PHFetchResult = PHAssetCollection.fetchAssetCollectionsWithType(.Album, subtype: .Any, options: fetchOptions)
    //Check return value - If found, then get the first album out
    if let _: AnyObject = collection.firstObject {
        self.albumFound = true
        assetCollection = collection.firstObject as! PHAssetCollection
    } else {
        //If not found - Then create a new album
        PHPhotoLibrary.sharedPhotoLibrary().performChanges({
            let createAlbumRequest : PHAssetCollectionChangeRequest = PHAssetCollectionChangeRequest.creationRequestForAssetCollectionWithTitle("camcam")
            self.assetCollectionPlaceholder = createAlbumRequest.placeholderForCreatedAssetCollection
            }, completionHandler: { success, error in
                self.albumFound = success

                if (success) {
                    let collectionFetchResult = PHAssetCollection.fetchAssetCollectionsWithLocalIdentifiers([self.assetCollectionPlaceholder.localIdentifier], options: nil)
                    print(collectionFetchResult)
                    self.assetCollection = collectionFetchResult.firstObject as! PHAssetCollection
                }
        })
    }
}

When saving the photo:

func saveImage(){
        PHPhotoLibrary.sharedPhotoLibrary().performChanges({
            let assetRequest = PHAssetChangeRequest.creationRequestForAssetFromImage(self.image)
            let assetPlaceholder = assetRequest.placeholderForCreatedAsset
            let albumChangeRequest = PHAssetCollectionChangeRequest(forAssetCollection: self.assetCollection, assets: self.photosAsset)
            albumChangeRequest!.addAssets([assetPlaceholder!])
            }, completionHandler: { success, error in
                print("added image to album")
                print(error)

                self.showImages()
        })
    }

Showing the images from that album:

func showImages() {
        //This will fetch all the assets in the collection

        let assets : PHFetchResult = PHAsset.fetchAssetsInAssetCollection(assetCollection, options: nil)
        print(assets)

        let imageManager = PHCachingImageManager()
        //Enumerating objects to get a chached image - This is to save loading time
        assets.enumerateObjectsUsingBlock{(object: AnyObject!,
            count: Int,
            stop: UnsafeMutablePointer<ObjCBool>) in

            if object is PHAsset {
                let asset = object as! PHAsset
                print(asset)

                let imageSize = CGSize(width: asset.pixelWidth, height: asset.pixelHeight)

                let options = PHImageRequestOptions()
                options.deliveryMode = .FastFormat

                imageManager.requestImageForAsset(asset, targetSize: imageSize, contentMode: .AspectFill, options: options, resultHandler: {(image: UIImage?,
                    info: [NSObject : AnyObject]?) in
                    print(info)
                    print(image)
                })
            }
        }

Solution 2:

Answer in Objective-C and cleaned up a bit.

__block PHFetchResult *photosAsset;
__block PHAssetCollection *collection;
__block PHObjectPlaceholder *placeholder;

// Find the album
PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
fetchOptions.predicate = [NSPredicate predicateWithFormat:@"title = %@", @"YOUR_ALBUM_TITLE"];
collection = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum
                                                      subtype:PHAssetCollectionSubtypeAny
                                                      options:fetchOptions].firstObject;
// Create the album
if (!collection)
{
    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
        PHAssetCollectionChangeRequest *createAlbum = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:@"YOUR_ALBUM_TITLE"];
        placeholder = [createAlbum placeholderForCreatedAssetCollection];
    } completionHandler:^(BOOL success, NSError *error) {
        if (success)
        {
            PHFetchResult *collectionFetchResult = [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[placeholder.localIdentifier]
                                                                                                            options:nil];
            collection = collectionFetchResult.firstObject;
        }
    }];
}

// Save to the album
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
    PHAssetChangeRequest *assetRequest = [PHAssetChangeRequest creationRequestForAssetFromImage:[UIImage imageWithData:imageData]];
    placeholder = [assetRequest placeholderForCreatedAsset];
    photosAsset = [PHAsset fetchAssetsInAssetCollection:collection options:nil];
    PHAssetCollectionChangeRequest *albumChangeRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:collection
                                                                                                                  assets:photosAsset];
    [albumChangeRequest addAssets:@[placeholder]];
} completionHandler:^(BOOL success, NSError *error) {
    if (success)
    {
        NSString *UUID = [placeholder.localIdentifier substringToIndex:36];
        self.photo.assetURL = [NSString stringWithFormat:@"assets-library://asset/asset.PNG?id=%@&ext=JPG", UUID];
        [self savePhoto];
    }
    else
    {
        NSLog(@"%@", error);
    }
}];

The bit at the end with the UUID was something I found on another StackOverflow thread for creating a replacement for AssetURL property from an ALAsset.

Note: See chris' comment below for more complete answer.

Solution 3:

I like to reuse the code I write so I decided to create an extension for PHPhotoLibrary where it is possible to use it like:

PHPhotoLibrary.saveImage(photo, albumName: "Trip") { asset in
    guard let asset = asset else {
        assert(false, "Asset is nil")
        return
    }
    PHPhotoLibrary.loadThumbnailFromAsset(asset) { thumbnail in
        print(thumbnail)
    }
}

Here is the code: https://gist.github.com/ricardopereira/636ccd0a3c8a327c43d42e7cbca4d041

Solution 4:

As updated for Swift 2.1+ and for Video in case you are trying to do that and ended up here. Compare to the other answers for slight differences (such as using for Images rather than Video)

var photosAsset: PHFetchResult!
var collection: PHAssetCollection!
var assetCollectionPlaceholder: PHObjectPlaceholder!

//Make sure we have custom album for this app if haven't already
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "title = %@", "MY_APP_ALBUM_NAME")
self.collection = PHAssetCollection.fetchAssetCollectionsWithType(.Album, subtype: .Any, options: fetchOptions).firstObject as! PHAssetCollection

//if we don't have a special album for this app yet then make one
if self.collection == nil {
     PHPhotoLibrary.sharedPhotoLibrary().performChanges({
        let createAlbumRequest : PHAssetCollectionChangeRequest = PHAssetCollectionChangeRequest.creationRequestForAssetCollectionWithTitle("MY_APP_ALBUM_NAME")
        self.assetCollectionPlaceholder = createAlbumRequest.placeholderForCreatedAssetCollection
             }, completionHandler: { success, error in
                if success {
                    let collectionFetchResult = PHAssetCollection.fetchAssetCollectionsWithLocalIdentifiers([self.assetCollectionPlaceholder.localIdentifier], options: nil)
                    print(collectionFetchResult)
                    self.collection = collectionFetchResult.firstObject as! PHAssetCollection
                }
            })
 }

//save the video to Photos                     
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
     let assetRequest = PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL(self.VIDEO_URL_FOR_VIDEO_YOU_MADE!)
     let assetPlaceholder = assetRequest!.placeholderForCreatedAsset
     self.photosAsset = PHAsset.fetchAssetsInAssetCollection(self.collection, options: nil)
     let albumChangeRequest = PHAssetCollectionChangeRequest(forAssetCollection: self.collection, assets: self.photosAsset)
     albumChangeRequest!.addAssets([assetPlaceholder!])
         }, completionHandler: { success, error in
               if success {
                  print("added video to album")
               }else if error != nil{
                  print("handle error since couldn't save video")
               }
         }
 })