Swift 3 or 4 Saving to custum album creates duplicate images

1k views Asked by At

I am saving images to a custom album after either select or camera completion. Obviously, after camera completion, there is only one image, but when a user selects images in the gallery picker, in the completion handler, when I save that image to the custom album, a duplicate is ALWAYS created. Both in the gallery as well as the root photoAlbum. Everywhere, it seems. I cannot reference the ID to see if it was created before, because the ID is being newly created with the placeholder.

Is there a way to get the base image reference ID so that I can associate EVERY image to the original? As I understand it, IOS (I hate ios btw), saves only one actual image and the rest are just pointers to the original image object. If that is the case, I would expect there is a way to get a solid reference to the original Image and from there, I can easily manage assets created from that base image.

public static func addNewImage(_ image:UIImage, toAlbum albumName:String,imageID:String?,onSuccess success:@escaping(String)->Void, onFailure failure:@escaping(Error?)->Void) {
        guard let album = self.getAlbum(withName: albumName) else {
            failure(SDPhotosHelper.albumNotFoundError)
            return
        }

        var localIdentifier = String();

        if(imageID != nil){
            if(self.hasImageInAlbum(withIdentifier: imageID!, fromAlbum: albumName)){
                failure(SDPhotosHelper.albumNotFoundError)
                return;
                }
        }

        PHPhotoLibrary.shared().performChanges({
                let albumChangeRequest = PHAssetCollectionChangeRequest(for: album)

            let assetCreationRequest = PHAssetChangeRequest.creationRequestForAsset(from: image)
            //assetCreationRequest.location = "";
            let placeHolder = assetCreationRequest.placeholderForCreatedAsset
            albumChangeRequest?.addAssets([placeHolder!] as NSArray)
            if placeHolder != nil {
                localIdentifier = (placeHolder?.localIdentifier)!
            }

        }) { (didSucceed, error) in
            OperationQueue.main.addOperation({
                didSucceed ? success(localIdentifier) : failure(error)
            })
        }
    }
1

There are 1 answers

0
Aryan Duntley On

No one bothered to assist with this. Luckily, I was able to find the solution. For any who come across this or the similar one that was also sitting around with the crickets: Choosing a picture causes resave to camera roll here is a solution.

The code I have is to CREATE A NEW ASSET. It is useful only for the saving the image to your custom album after the user has taken a picture with the camera. It is for brand new assets.

However, for existing assets, you do not want to create a new asset. Instead, you want to add the existing asset to the custom album. To do this, you need a different method. Here is the code I created and it seems to be working. Keep in mind that you will have to get the asset ID FIRST, so that you can send it to your method and access the existing asset.

So, in your imagePickerController, you have to determine whether the user chose an existing image or whether the method is being called from a new camera action.

let pickerSource = picker.sourceType;
switch(pickerSource){
 case .savedPhotosAlbum, .photoLibrary:
  if(let url = info[UIIMagePickerControllerReferenceURL] as? NSURL{
   let refURLString = refURL?.absoluteString;
   /* value for refURLString looks something like assets-library://asset/asset.JPG?id=82A6E75C-EA55-4C3A-A988-4BF8C7F3F8F5&ext=JPG */
   let refID = {function here to extract the id query param from the url string}
   /*above gets you the asset ID, you can get the asset directly, but it is only 
     available in ios 11+.
   */
   MYPHOTOHELPERCLASS.transferImage(toAlbum: "myalbumname", withID: refID!, ...)

 }
 break;
 case .camera:
 ...
 break;
}

Now, in your photohelper class (or in any function anywhere, whatever), to EDIT the asset instead of create a new one, this is what I have. I am assuming the changeRequest variable can be ommitted. I was just playing around until I got this right. Going through the completely ridiculous apple docs I was able to at least notice that there were other methods to play with. I found that the NSFastEnumeration parameter can be an NSArray of PHAssets, and not just placeholder PHObjectPlaceholder objects.

public static func transferImage(toAlbum albumName:String, withID imageID:String, onSuccess success:@escaping(String)->Void, onFailure failure:@escaping(Error?)->Void){

  guard let album = self.getAlbum(withName: albumName) else{
    ... failure here, albumNotFoundError
    return;
  }

  if(self.hasImageInAlbum(withIdentifier: imageID, fromAlbum: albunName)){
    ... failure here, image already exists in the album, do not make another
    return;
  }

  let theAsset = self.getExistingAsset(withLocalIdentifier: imageID);
  if(theAsset == nil){
    ... failure, no asset for asset id
    return;
  }

    PHPhotoLibrary.shared().performChanges({
      let albumChangeRequest = PHAssetCollectionChangeRequest(for: album);
      let changeRequest = PHAssetChangeRequest.init(for: theAsset!);
      let enumeration:NSArray = [theAsset!];
      let cnt = album.estimatedAssetCount;
      if(cnt == 0){
          albumChangeRequest?.addAssets(enumeration);
      }else{
          albumChangeRequest?.inserAssets(enumeration, at: [0]);
      }
    }){didSucceed, error) in
       OperationQueue.main.addOperation({
         didSucceed ? success(imageID) : failure(error);
       })
    }

}

So, it is pretty much the same, except instead of creating an Asset Creation Request and generating a placeholder for the created asset, you instead just use the existing asset ID to fetch an existing asset and add the existing asset to the addasset/insertasset NSArray parameter instead of a newly created asset