6

More specifically, how can you know whether a PHAsset has current version of the underlying asset different than the original?

My user should only need to choose between the current or original asset when necessary. And then I need their answer for PHImageRequestOptions.version.

Clay Bridges
  • 11,602
  • 10
  • 68
  • 118
Tomasz Bąk
  • 6,124
  • 3
  • 34
  • 48

4 Answers4

9

As of iOS 16, PHAsset has a hasAdjustments property which indicates if the asset has been edited.

For previous releases, you can get an array of data resources for a given asset via PHAssetResource API - it will have an adjustment data resource if that asset has been edited.

let isEdited = PHAssetResource.assetResources(for: asset).contains(where: { $0.type == .adjustmentData })

Note that if you want to actually work with a resource file, you have to fetch its data using a PHAssetResourceManager API. Also note that this method returns right away - there's no waiting for an async network request, unlike other answers here.

Jordan H
  • 52,571
  • 37
  • 201
  • 351
4

I have found two ways of checking PHAsset for modifications.

- (void)tb_checkForModificationsWithEditingInputMethodCompletion:(void (^)(BOOL))completion {
    PHContentEditingInputRequestOptions *options = [PHContentEditingInputRequestOptions new];
    options.canHandleAdjustmentData = ^BOOL(PHAdjustmentData *adjustmentData) { return YES; };
    
    [self requestContentEditingInputWithOptions:options completionHandler:^(PHContentEditingInput *contentEditingInput, NSDictionary *info) {
        if (completion) completion(contentEditingInput.adjustmentData != nil);
    }];
}
 
- (void)tb_checkForModificationsWithAssetPathMethodCompletion:(void (^)(BOOL))completion {
    PHVideoRequestOptions *options = [PHVideoRequestOptions new];
    options.deliveryMode = PHVideoRequestOptionsDeliveryModeFastFormat;
    
    [[PHImageManager defaultManager] requestAVAssetForVideo:self options:options resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) {
        if (completion) completion([[asset description] containsString:@"/Mutations/"]);
    }];
}

EDIT: I was at the point where I needed the same functionality for PHAsset with an image. I used this:

- (void)tb_checkForModificationsWithAssetPathMethodCompletion:(void (^)(BOOL))completion {
    [self requestContentEditingInputWithOptions:nil completionHandler:^(PHContentEditingInput *contentEditingInput, NSDictionary *info) {
        NSString *path = (contentEditingInput.avAsset) ? [contentEditingInput.avAsset description] : contentEditingInput.fullSizeImageURL.path;
        completion([path containsString:@"/Mutations/"]);
     }];
}
Tomasz Bąk
  • 6,124
  • 3
  • 34
  • 48
  • These solutions depend on implementation details of the Photos framework. Testing for the asset's `adjustmentData` as outlined in the other answer is a way to query for modifications directly - that method should be used instead. – Theo Oct 14 '19 at 09:22
3

Take a look at PHImageRequestOptionsVersion

PHImageRequestOptionsVersionCurrent

Request the most recent version of the image asset (the one that reflects all edits). The resulting image is the rendered output from all previously made adjustments.

PHImageRequestOptionsVersionUnadjusted

Request a version of the image asset without adjustments.
If the asset has been edited, the resulting image reflects the state of the asset before any edits were performed.

PHImageRequestOptionsVersionOriginal

Request the original, highest-fidelity version of the image asset. The resulting image is originally captured or imported version of the asset, regardless of any edits made.

If you ask user before retrieving assets, you know which version user specified. If you get a phasset from elsewhere, you can do a revertAssetContentToOriginal to get the original asset. And PHAsset has modificationDate and creationDate properties, you can use this to tell if a PHAsset is modified.

gabbler
  • 13,626
  • 4
  • 32
  • 44
  • I've updated the question, I need the information to create *PHImageRequestOptions* correctly. – Tomasz Bąk Oct 30 '14 at 13:13
  • [Documentation](https://developer.apple.com/library/IOs/documentation/Photos/Reference/PHAsset_Class/index.html#//apple_ref/occ/instp/PHAsset/modificationDate): `Photos updates the modification date when an asset’s image or video content or metadata changes.` So modification date will change also when you favourite a picture. – Tomasz Bąk Oct 30 '14 at 13:17
  • 1
    After test I've found out, that favouriting a picture doesn't change `modificationDate`, but reverting to a original image after modifications doesn't revert `modificationDate` to `creationDate`, so comparing dates isn't the solution. – Tomasz Bąk Oct 30 '14 at 13:34
0

I found this code the only one working for now, and it handles most of the edge cases. It may not be the fastest one but works well for most images types. It takes the smallest possible original and modified image and compare their data content.

@implementation PHAsset (Utilities)
- (void)checkEditingHistoryCompletion:(void (^)(BOOL edited))completion
{
    PHImageManager *manager = [PHImageManager defaultManager];

    CGSize compareSize = CGSizeMake(64, 48);

    PHImageRequestOptions *requestOptions = [PHImageRequestOptions new];
    requestOptions.synchronous = YES;
    requestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeFastFormat;
    requestOptions.version = PHImageRequestOptionsVersionOriginal;

    [manager requestImageForAsset:self
                       targetSize:compareSize
                      contentMode:PHImageContentModeAspectFit
                          options:requestOptions
                    resultHandler:^(UIImage *originalResult, NSDictionary *info) {

                        UIImage *currentImage = originalResult;

                        requestOptions.version = PHImageRequestOptionsVersionCurrent;

                        [manager requestImageForAsset:self
                                           targetSize:currentImage.size
                                          contentMode:PHImageContentModeAspectFit
                                              options:requestOptions
                                        resultHandler:^(UIImage *currentResult, NSDictionary *info) {

                                            NSData *currData = UIImageJPEGRepresentation(currentResult, 0.1);
                                            NSData *orgData = UIImageJPEGRepresentation(currentImage, 0.1);

                                            if (completion) {

                                                //handle case when both images cannot be retrived it also mean no edition
                                                if ((currData == nil) && (orgData == nil)) {
                                                    completion(NO);
                                                    return;
                                                }

                                                completion(([currData isEqualToData:orgData] == NO));
                                            }
                                        }];
                    }];
}

@end
Marcin Małysz
  • 733
  • 8
  • 12
  • Your solution works only for images. Second code block (the one after EDIT) from accepted answer doesn't work for you? – Tomasz Bąk Feb 04 '15 at 12:59
  • Unfortunately no :/ I'm receiving regular path even after modification e.g: path NSString * @"/var/mobile/Media/DCIM/100APPLE/IMG_0736.PNG" 0x15d683d0 – Marcin Małysz Feb 04 '15 at 14:18