iOS save photo in an app specific album

Viewed 20839

I'm creating an iOS 5 app. I want to save a photo to the device.

I want to save the photo to an album specific to my app, so I need to create the album, and then save photos to the album.

I know how to create the album:

ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library addAssetsGroupAlbumWithName:@"MY APP NAME" resultBlock:^(ALAssetsGroup *group) {
    //How to get the album URL?
} failureBlock:^(NSError *error) {
    //Handle the error
}];

I want add photos to the new album now, how do I do so? Sample code is greatly appreciated!

5 Answers

Adaptation of Eddy's answer for Swift 4:

    func saveImageToAlbum(_ image: UIImage, name: String) {

        if let collection = fetchAssetCollection(name) {
            self.saveImageToAssetCollection(image, collection: collection)
        } else {
            // Album does not exist, create it and attempt to save the image
            PHPhotoLibrary.shared().performChanges({
                PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: name)
            }, completionHandler: { (success: Bool, error: Error?) in
                guard success == true && error == nil else {
                    NSLog("Could not create the album")
                    if let err = error {
                        NSLog("Error: \(err)")
                    }
                    return
                }

                if let newCollection = self.fetchAssetCollection(name) {
                    self.saveImageToAssetCollection(image, collection: newCollection)
                }
            })
        }
    }

    func fetchAssetCollection(_ name: String) -> PHAssetCollection? {

        let fetchOption = PHFetchOptions()
        fetchOption.predicate = NSPredicate(format: "title == '" + name + "'")

        let fetchResult = PHAssetCollection.fetchAssetCollections(
            with: PHAssetCollectionType.album,
            subtype: PHAssetCollectionSubtype.albumRegular,
            options: fetchOption)

        return fetchResult.firstObject
    }

    func saveImageToAssetCollection(_ image: UIImage, collection: PHAssetCollection) {

        PHPhotoLibrary.shared().performChanges({

            let creationRequest = PHAssetCreationRequest.creationRequestForAsset(from: image)
            if let request = PHAssetCollectionChangeRequest(for: collection),
                let placeHolder = creationRequest.placeholderForCreatedAsset {
                request.addAssets([placeHolder] as NSFastEnumeration)
            }
        }, completionHandler: { (success: Bool, error: Error?) in
            guard success == true && error == nil else {
                NSLog("Could not save the image")
                if let err = error {
                    NSLog("Error: " + err.localizedDescription)
                }
                return
            }
        })
    }

Improved version on Objective C, using blocks. It creates an album, if it doesn't exist, then saves three types of media items - photos, gifs and videos:

// Types of media, that can be saved to an album
typedef NS_ENUM(NSUInteger, AlbumMediaType) {
    AlbumMediaTypePhoto,
    AlbumMediaTypeGIF,
    AlbumMediaTypeVideo
};

/**
 Creates album if it doesn't exist and returns it in a block
 */
- (void)createCollectionOnComplete:(void (^ _Nonnull)(PHAssetCollection * _Nonnull collection))onComplete
{
    NSString *albumTitle = @"YOUR_ALBUM_TITLE";

    __block PHAssetCollection *collection;
    __block PHObjectPlaceholder *placeholder;

    // Searching for an existing album
    PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
    fetchOptions.predicate = [NSPredicate predicateWithFormat:@"title = %@", albumTitle];
    collection = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum
                                                          subtype:PHAssetCollectionSubtypeAny
                                                          options:fetchOptions].firstObject;
    // If album is not found, we create it
    if (!collection)
    {
        [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
            PHAssetCollectionChangeRequest *createAlbum = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:albumTitle];
            placeholder = [createAlbum placeholderForCreatedAssetCollection];
        } completionHandler:^(BOOL success, NSError *error) {
            if (success)
            {
                PHFetchResult *collectionFetchResult = [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[placeholder.localIdentifier]
                                                                                                            options:nil];
                collection = collectionFetchResult.firstObject;
                // After creating album, we return it
                onComplete(collection);
            }
        }];
    } else {
        // If album already exists, we instantly return it
        onComplete(collection);
    }
}


/**
 Saves an item of a given mediatype, that is located in mediaURL
 */
- (void)saveToAlbumMediaItemFromURL:(NSURL *)mediaURL mediaType:(AlbumMediaType)mediaType
{
    NSData *mediaData = [NSData dataWithContentsOfURL:mediaURL];
    if (!mediaData) {
        OWSFail(@"%@ Could not load data: %@", self.logTag, [self.attachmentStream mediaURL]);
        return;
    }

    [self createCollectionOnComplete:^(PHAssetCollection * _Nonnull collection) {

        [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{

            // We create a PHAsset using creationRequest
            PHAssetCreationRequest *assetRequest;
            switch (mediaType) {
                case AlbumMediaTypePhoto: {
                    assetRequest = [PHAssetCreationRequest creationRequestForAssetFromImage:[UIImage imageWithData:mediaData]];
                    break;
                }
                case AlbumMediaTypeGIF: {
                    assetRequest = [PHAssetCreationRequest creationRequestForAsset];
                    PHAssetResourceCreationOptions *options = [[PHAssetResourceCreationOptions alloc] init];
                    [assetRequest addResourceWithType:PHAssetResourceTypePhoto data:mediaData options:options];
                    break;
                }
                case AlbumMediaTypeVideo: {
                    if ( !UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(mediaURL.path) ) {
                        OWSFail(@"%@ Could not save incompatible video data.", self.logTag);
                        break;
                    }

                    NSString *videoPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:@"file.mov"];
                    [mediaData writeToFile:videoPath atomically:YES];
                    assetRequest = [PHAssetCreationRequest creationRequestForAssetFromVideoAtFileURL:[NSURL fileURLWithPath:videoPath]];
                    break;
                }
                default:
                    break;
            }

            // Creating a request to change an album
            PHAssetCollectionChangeRequest *albumChangeRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:collection];

            // PHAsset is not created yet, so we use a placeholder
            PHObjectPlaceholder *placeholder = [assetRequest placeholderForCreatedAsset];

            // We add a placeholder of a created item to the request of changing album
            if (albumChangeRequest != nil && placeholder != nil) {
                [albumChangeRequest addAssets: @[placeholder]];
            }

        } completionHandler:^(BOOL success, NSError *error) {

            if (success) {
                NSLog(@"Media item saved!");
            } else {
                NSLog(@"Error saving media item - %@", error ? error.localizedDescription : @"");
            }

        }];

    }];
}

We can use these methods to save media items this way:

[self saveToAlbumMediaItemFromURL:[self.attachmentStream mediaURL] mediaType:AlbumMediaTypeGIF];
Related