Greg
Greg

Reputation: 1842

iOS10 UIImageWriteToSavedPhotosAlbum TCC__CRASHING_DUE_TO_PRIVACY_VIOLATION

I want to create and save a UIImage in iOS 10. I can create it either by taking a UIView snapshot or using UIGraphicsImageRenderer (shown below).

- (UIView *)createIcon
{
    UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:CGSizeMake(200, 200)];

    UIImage *image = [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull context) {
        [[UIColor darkGrayColor] setStroke];
        [context strokeRect:renderer.format.bounds];
        [[UIColor colorWithRed:158/255.0 green:215/255.0 blue:245/255.0 alpha:0.5] setFill];
        [context fillRect:CGRectMake(30, 30, 140, 140)];
        [[UIColor colorWithRed:243/255.0 green:122/255.0 blue:216/255.0 alpha:0.3] setFill];
        CGContextFillEllipseInRect(context.CGContext, CGRectMake(30, 30, 120, 120));
    }];

    UIImageView *showImage = [[UIImageView alloc] initWithImage:image];

    UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);

    return showImage;
}

However, when I try to save using UIImageWriteToSavedPhotosAlbum by including the second last statement, Xcode goes into Debug with the following error report

(lldb) bt
* thread #5, queue = 'com.apple.root.default-qos', stop reason = signal SIGABRT
  * frame #0: 0x00000001101611a6 libsystem_kernel.dylib`__abort_with_payload + 10
    frame #1: 0x000000011015b86e libsystem_kernel.dylib`abort_with_payload_wrapper_internal + 89
    frame #2: 0x000000011015b89b libsystem_kernel.dylib`abort_with_payload + 9
    frame #3: 0x0000000108ab3af7 TCC`__CRASHING_DUE_TO_PRIVACY_VIOLATION__ + 182
    frame #4: 0x0000000108ab3a41 TCC`__TCCAccessRequest_block_invoke.77 + 665
    frame #5: 0x0000000108ab7273 TCC`__tccd_send_block_invoke + 274

So even though I didn’t use a camera to create the image, saving it to the photo album is a violation of privacy (hiss, boo)! I know it is possible to set up privacy keys in the plist but is there a more sensible alternative?

CLARIFICATION

I hope to use rendered images (or view snapshots) as assets in the app. So instead of is there a more sensible alternative ?

perhaps I should have asked

is there an alternative place to save images (i.e. as PNG or JPG files) so privacy is not an issue?

Upvotes: 0

Views: 1513

Answers (2)

Upholder Of Truth
Upholder Of Truth

Reputation: 4711

You can save any UIImage to the photo album but first you must ask the user for permission to do as this as it is indeed a privacy issue. If they don't give you access then you can't save the image at all.

The most sensible approach is to add the required privacy key to the info.plist.

This is the info.plist xml definition although it's easier to add the keys in the property list:

<key>NSPhotoLibraryAddUsageDescription</key>
<string>Message requesting the ability to add to the photo library</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Message requestion the ability to access the photo library</string>

If you add these then when you first try to access or add to the photo library a popup will display with your message allowing the user to decide if they want your app to have access.

One good reason to put it in the info.plist file is that all the requests for access are then in a single easily visible place instead of somewhere random in your project.

EDIT

Here is how to save the image in documents which does not raise and privacy issues:

NSData *imageData = UIImagePNGRepresentation(image);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0]; //Get the docs directory
NSString *filePath = [documentsPath stringByAppendingPathComponent:@"image.png"]; //Add the file name
[imageData writeToFile:filePath atomically:YES]; //Write the file

If you want a jpg instead of a png use this instead:

NSData *imageData = UIImageJPEGRepresentation(image, 0.9); // Use whatever compression ratio you want instead of 0.9

Upvotes: 2

kalpesh satasiya
kalpesh satasiya

Reputation: 817

Follow this code:

#import <Photos/Photos.h>

pragma mark - Save Image in photo album

- (void)addImageToCameraRoll:(UIImage *)image {
NSString *albumName = @"Your app name";

void (^saveBlock)(PHAssetCollection *assetCollection) = ^void(PHAssetCollection *assetCollection) {
    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
        PHAssetChangeRequest *assetChangeRequest = [PHAssetChangeRequest creationRequestForAssetFromImage:image];
        PHAssetCollectionChangeRequest *assetCollectionChangeRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:assetCollection];
        [assetCollectionChangeRequest addAssets:@[[assetChangeRequest placeholderForCreatedAsset]]];

    } completionHandler:^(BOOL success, NSError *error) {
        if (!success) {
            NSLog(@"Error creating asset: %@", error);
        }
    }];
};

PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
fetchOptions.predicate = [NSPredicate predicateWithFormat:@"localizedTitle = %@", albumName];
PHFetchResult *fetchResult = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAny options:fetchOptions];
if (fetchResult.count > 0) {
    saveBlock(fetchResult.firstObject);
} else {
    __block PHObjectPlaceholder *albumPlaceholder;
    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
        PHAssetCollectionChangeRequest *changeRequest = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:albumName];
        albumPlaceholder = changeRequest.placeholderForCreatedAssetCollection;

    } completionHandler:^(BOOL success, NSError *error) {
        if (success) {
            PHFetchResult *fetchResult = [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[albumPlaceholder.localIdentifier] options:nil];
            if (fetchResult.count > 0) {
                saveBlock(fetchResult.firstObject);
            }
        } else {
            NSLog(@"Error creating album: %@", error);
        }
    }];
}
}

Upvotes: 2

Related Questions