肇鑫的技术博客

业精于勤,荒于嬉

Remove the video part from a live photo

Someone may think the process is as easy as getting the video from a live photo, removing the video and saving the other parts back. It is wrong.

We can get the video from a live photo using PHAssetResource's class func assetResources(for livePhoto: PHLivePhoto) -> [PHAssetResource]. But the PHAssetResource it gets contains empty assetLocalIdentifier. So you can't get its asset directly. And you can't remove it separately.

The correct way is to get the photo part, save it and remove the live photo.

Get the photo and save it

We could get the photo in three ways. Two from PHImageManager and one from PHAssetResourceManager. However, only one method is right way.

requestImage(for:targetSize:contentMode:options:resultHandler:)

OS, Mac Catalyst, tvOS
func requestImage(for asset: PHAsset, targetSize: CGSize, contentMode: PHImageContentMode, options: PHImageRequestOptions?, resultHandler: @escaping (UIImage?, [AnyHashable : Any]?) -> Void) -> PHImageRequestID
macOS
func requestImage(for asset: PHAsset, targetSize: CGSize, contentMode: PHImageContentMode, options: PHImageRequestOptions?, resultHandler: @escaping (NSImage?, [AnyHashable : Any]?) -> Void) -> PHImageRequestID

We should not use this method as it returns UIImage/NSImage, according to Apple, those two classes lacks metadata.

A UIImage object does not contain all metadata associated with the image file it was originally loaded from (for example, Exif tags such as geographic location, camera model, and exposure parameters). To ensure such metadata is saved in the Photos library, instead use the creationRequestForAssetFromImage(atFileURL:) method or the PHAssetCreationRequest class. To copy metadata from one file to another, see Image I/O.
creationRequestForAsset(from:)

requestData(for:options:dataReceivedHandler:completionHandler:)

We cannot use requestData(for:options:dataReceivedHandler:completionHandler:) of PHAssetResourceManager either. It does return Data instead of UIImage/NSImage. But the data it returns cannot save to PHPhotoLibrary correctly.

I think it is because of the data it returns contains the same localIdentifier as the live photo, which is allowed to save separately.

requestImageDataAndOrientation(for:options:resultHandler:)

This is the only way we should use to save the photo part from a live photo.

Remove the live photo

This is an easy job. Just use PHAssetChangeRequest.deleteAssets(_ assets: NSFastEnumeration).