Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add permission check when adding media #383

Merged
merged 9 commits into from
Mar 23, 2022
33 changes: 2 additions & 31 deletions Pod/Classes/UIViewController+MediaAdditions.m
Original file line number Diff line number Diff line change
@@ -1,40 +1,11 @@
#import "UIViewController+MediaAdditions.h"
#import "WPMediaCollectionDataSource.h"
#import "WPMediaPickerAlertHelper.h"

@implementation UIViewController (MediaAdditions)

- (void)wpm_showAlertWithError:(NSError *)error okActionHandler:(void (^ __nullable)(UIAlertAction *action))handler {
NSString *title = NSLocalizedString(@"Media Library", @"Title for alert when a generic error happened when loading media");
NSString *message = NSLocalizedString(@"There was a problem when trying to access your media. Please try again later.", @"Explaining to the user there was an generic error accesing media.");
NSString *cancelText = NSLocalizedString(@"OK", "");
NSString *otherButtonTitle = nil;
if (error.domain == WPMediaPickerErrorDomain) {
title = NSLocalizedString(@"Media Library", @"Title for alert when access to the media library is not granted by the user");
if (error.code == WPMediaPickerErrorCodePermissionDenied) {
otherButtonTitle = NSLocalizedString(@"Open Settings", @"Go to the settings app");
message = NSLocalizedString(@"This app needs permission to access your device media library in order to add photos and/or video to your posts. Please change the privacy settings if you wish to allow this.",
@"Explaining to the user why the app needs access to the device media library.");
} else if (error.code == WPMediaPickerErrorCodeRestricted) {
message = NSLocalizedString(@"Your app is not authorized to access media library due to active restrictions such as parental controls. Please check your parental control settings in this device.",
@"Explaining to the user why the app needs access to the device media library.");
}
}

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title
message:message
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:cancelText
style:UIAlertActionStyleCancel
handler:handler];
[alertController addAction:okAction];

if (otherButtonTitle) {
UIAlertAction *otherAction = [UIAlertAction actionWithTitle:otherButtonTitle style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
[[UIApplication sharedApplication] openURL:settingsURL options:@{} completionHandler:nil];
}];
[alertController addAction:otherAction];
}
UIAlertController *alertController = [WPMediaPickerAlertHelper buildAlertControllerWithError:error okActionHandler:handler];
[self presentViewController:alertController animated:YES completion:nil];
}

Expand Down
1 change: 1 addition & 0 deletions Pod/Classes/WPMediaPicker.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@
#import "WPMediaCapturePreviewCollectionView.h"
#import "WPVideoPlayerView.h"
#import "WPActionBar.h"
#import "WPMediaPickerAlertHelper.h"

#endif /* _WPMEDIAPICKER_ */
7 changes: 7 additions & 0 deletions Pod/Classes/WPMediaPickerAlertHelper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#import <Foundation/Foundation.h>

@interface WPMediaPickerAlertHelper : NSObject

+ (UIAlertController *)buildAlertControllerWithError:(NSError *)error okActionHandler:(void (^ __nullable)(UIAlertAction *action))handler;

@end
42 changes: 42 additions & 0 deletions Pod/Classes/WPMediaPickerAlertHelper.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#import "WPMediaPickerAlertHelper.h"
#import "WPMediaCollectionDataSource.h"

@implementation WPMediaPickerAlertHelper

+ (UIAlertController *)buildAlertControllerWithError:(NSError *)error okActionHandler:(void (^ __nullable)(UIAlertAction *action))handler {
NSString *title = NSLocalizedString(@"Media Library", @"Title for alert when a generic error happened when loading media");
NSString *message = NSLocalizedString(@"There was a problem when trying to access your media. Please try again later.", @"Explaining to the user there was an generic error accesing media.");
NSString *cancelText = NSLocalizedString(@"OK", "");
NSString *otherButtonTitle = nil;
if (error.domain == WPMediaPickerErrorDomain) {
title = NSLocalizedString(@"Media Library", @"Title for alert when access to the media library is not granted by the user");
if (error.code == WPMediaPickerErrorCodePermissionDenied) {
otherButtonTitle = NSLocalizedString(@"Open Settings", @"Go to the settings app");
message = NSLocalizedString(@"This app needs permission to access your device media library in order to add photos and/or video to your posts. Please change the privacy settings if you wish to allow this.",
@"Explaining to the user why the app needs access to the device media library.");
} else if (error.code == WPMediaPickerErrorCodeRestricted) {
message = NSLocalizedString(@"Your app is not authorized to access media library due to active restrictions such as parental controls. Please check your parental control settings in this device.",
@"Explaining to the user why the app needs access to the device media library.");
}
}

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title
message:message
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:cancelText
style:UIAlertActionStyleCancel
handler:handler];
[alertController addAction:okAction];

if (otherButtonTitle) {
UIAlertAction *otherAction = [UIAlertAction actionWithTitle:otherButtonTitle style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
[[UIApplication sharedApplication] openURL:settingsURL options:@{} completionHandler:nil];
}];
[alertController addAction:otherAction];
}

return alertController;
}

@end
1 change: 1 addition & 0 deletions Pod/Classes/WPMediaPickerViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -1193,6 +1193,7 @@ - (void)processMediaCaptured:(NSDictionary *)info
WPMediaAddedBlock completionBlock = ^(id<WPMediaAsset> media, NSError *error) {
if (error || !media) {
NSLog(@"Adding media failed: %@", [error localizedDescription]);
[self showError:error];
return;
}
[self addMedia:media animated:YES];
Expand Down
120 changes: 84 additions & 36 deletions Pod/Classes/WPPHAssetDataSource.m
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,7 @@ - (void)loadDataWithOptions:(WPMediaLoadOptions)options
{
[self checkPermissionStatus:^(PHAuthorizationStatus status) {

/// Starting from iOS 15.2 we should do the registration
/// after asking user for permission
/// Solution proposed here - https://developer.apple.com/forums/thread/696804
///
[[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:self];
[self registerPHChangeObserver];

switch (status) {
case PHAuthorizationStatusRestricted:
Expand Down Expand Up @@ -450,40 +446,82 @@ - (void)addAssetWithChangeRequest:(PHAssetChangeRequest *(^)(void))changeRequest
{
NSParameterAssert(changeRequestBlock);
__block NSString * assetIdentifier = nil;
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
// Request creating an asset from the image.
PHAssetChangeRequest *createAssetRequest = changeRequestBlock();
PHObjectPlaceholder *assetPlaceholder = [createAssetRequest placeholderForCreatedAsset];
assetIdentifier = [assetPlaceholder localIdentifier];
if ([self.activeAssetsCollection canPerformEditOperation:PHCollectionEditOperationAddContent]) {
// Request editing the album.
PHAssetCollectionChangeRequest *albumChangeRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:self.activeAssetsCollection];
[albumChangeRequest addAssets:@[ assetPlaceholder ]];
}
} completionHandler:^(BOOL success, NSError *error) {
if (!success) {
if (completionBlock){
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(nil, error);
});

[self checkPermissionStatus:^(PHAuthorizationStatus status) {

[self registerPHChangeObserver];

switch (status) {
case PHAuthorizationStatusRestricted:
{
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
NSError *error = [NSError errorWithDomain:WPMediaPickerErrorDomain code:WPMediaPickerErrorCodeRestricted userInfo:nil];
completionBlock(nil, error);
});
}
return;
}
return;
}
PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
fetchOptions.predicate = [NSPredicate predicateWithFormat:@"(localIdentifier == %@)", assetIdentifier];
PHFetchResult * result = [PHAsset fetchAssetsWithOptions:fetchOptions];
if (result.count < 1){
if (completionBlock){
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(nil, error);
case PHAuthorizationStatusDenied:
case PHAuthorizationStatusLimited:
{
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
NSError *error = [NSError errorWithDomain:WPMediaPickerErrorDomain code:WPMediaPickerErrorCodePermissionDenied userInfo:nil];
completionBlock(nil, error);
});
}
return;
}
case PHAuthorizationStatusNotDetermined:
{
[self checkPermissionStatus:^(PHAuthorizationStatus status) {
[self addAssetWithChangeRequest:changeRequestBlock completionBlock:completionBlock];
}];
return;
}
case PHAuthorizationStatusAuthorized:
{
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
// Request creating an asset from the image.
PHAssetChangeRequest *createAssetRequest = changeRequestBlock();
PHObjectPlaceholder *assetPlaceholder = [createAssetRequest placeholderForCreatedAsset];
assetIdentifier = [assetPlaceholder localIdentifier];
if ([self.activeAssetsCollection canPerformEditOperation:PHCollectionEditOperationAddContent]) {
// Request editing the album.
PHAssetCollectionChangeRequest *albumChangeRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:self.activeAssetsCollection];
[albumChangeRequest addAssets:@[ assetPlaceholder ]];
}
} completionHandler:^(BOOL success, NSError *error) {
if (!success) {
if (completionBlock){
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(nil, error);
});
}
return;
}
PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
fetchOptions.predicate = [NSPredicate predicateWithFormat:@"(localIdentifier == %@)", assetIdentifier];
PHFetchResult * result = [PHAsset fetchAssetsWithOptions:fetchOptions];
if (result.count < 1){
if (completionBlock){
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(nil, error);
});
}
return;
}
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock([result firstObject], nil);
});
}
}];
});
return;
}
return;
}
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock([result firstObject], nil);
});
}
}];
}
Expand All @@ -495,6 +533,16 @@ - (void)setMediaTypeFilter:(WPMediaType)filter
_refreshGroups = YES;
}

- (void)registerPHChangeObserver
{

/// Starting from iOS 15.2 we should do the registration
/// after asking user for permission
/// Solution proposed here - https://developer.apple.com/forums/thread/696804
///
[[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:self];
}

@end

#pragma mark - WPPHAssetMedia
Expand Down
2 changes: 1 addition & 1 deletion WPMediaPicker.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Pod::Spec.new do |s|
s.name = 'WPMediaPicker'
s.version = '1.8.2'
s.version = '1.8.3-beta.2'

s.summary = 'WPMediaPicker is an iOS controller that allows capture and picking of media assets.'
s.description = <<-DESC
Expand Down