Skip to content

Drop support for iOS < 11 #1461

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

Merged
merged 7 commits into from
Apr 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions permission_handler_apple/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 9.4.7

* Increases minimum supported Flutter version to 3.3.0, and removes code only
required for iOS versions prior to iOS 11.

## 9.4.6

* Adds the ability to handle `CNAuthorizationStatusLimited` introduced in ios18
Expand Down
20 changes: 5 additions & 15 deletions permission_handler_apple/ios/Classes/PermissionManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -80,21 +80,11 @@ - (void)requestPermissions:(NSArray *)permissions completion:(PermissionRequestC
}

+ (void)openAppSettings:(FlutterResult)result {
if (@available(iOS 10, *)) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]
options:[[NSDictionary alloc] init]
completionHandler:^(BOOL success) {
result([[NSNumber alloc] initWithBool:success]);
}];
} else if (@available(iOS 8.0, *)) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
BOOL success = [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
result([[NSNumber alloc] initWithBool:success]);
#pragma clang diagnostic pop
} else {
result(@false);
}
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]
options:[[NSDictionary alloc] init]
completionHandler:^(BOOL success) {
result([[NSNumber alloc] initWithBool:success]);
}];
}

+ (id)createPermissionStrategy:(PermissionGroup)permission {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,8 @@
@implementation AssistantPermissionStrategy

- (PermissionStatus)checkPermissionStatus:(PermissionGroup)permission {
if (@available(iOS 10, *)) {
INSiriAuthorizationStatus assistantPermission = [INPreferences siriAuthorizationStatus];
return [AssistantPermissionStrategy parsePermission:assistantPermission];
}

return PermissionStatusGranted;
INSiriAuthorizationStatus assistantPermission = [INPreferences siriAuthorizationStatus];
return [AssistantPermissionStrategy parsePermission:assistantPermission];
}

- (void)checkServiceStatus:(PermissionGroup)permission completionHandler:(ServiceStatusHandler)completionHandler {
Expand All @@ -31,14 +27,10 @@ - (void)requestPermission:(PermissionGroup)permission completionHandler:(Permiss
return;
}

if (@available(iOS 10, *)){
[INPreferences requestSiriAuthorization:^(INSiriAuthorizationStatus status) {
PermissionStatus permissionStatus = [AssistantPermissionStrategy parsePermission:status];
completionHandler(permissionStatus);
}];
} else {
completionHandler(PermissionStatusGranted);
}
[INPreferences requestSiriAuthorization:^(INSiriAuthorizationStatus status) {
PermissionStatus permissionStatus = [AssistantPermissionStrategy parsePermission:status];
completionHandler(permissionStatus);
}];
}

+ (PermissionStatus)parsePermission:(INSiriAuthorizationStatus)assistantPermission API_AVAILABLE(ios(10)){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,7 @@ - (void)checkServiceStatus:(PermissionGroup)permission completionHandler:(Servic
}

- (void)handleCheckServiceStatusCallback:(CBCentralManager *)centralManager {
if (@available(iOS 10, *)) {
ServiceStatus serviceStatus = [centralManager state] == CBManagerStatePoweredOn ? ServiceStatusEnabled : ServiceStatusDisabled;
_serviceStatusHandler(serviceStatus);
}
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
ServiceStatus serviceStatus = [centralManager state] == CBCentralManagerStatePoweredOn ? ServiceStatusEnabled : ServiceStatusDisabled;
ServiceStatus serviceStatus = [centralManager state] == CBManagerStatePoweredOn ? ServiceStatusEnabled : ServiceStatusDisabled;
_serviceStatusHandler(serviceStatus);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,7 @@ - (void)requestPermission:(PermissionGroup)permission completionHandler:(Permiss
return;
}

if (@available(iOS 9.0, *)) {
[ContactPermissionStrategy requestPermissionsFromContactStore:completionHandler];
} else {
[ContactPermissionStrategy requestPermissionsFromAddressBook:completionHandler];
}
[ContactPermissionStrategy requestPermissionsFromContactStore:completionHandler];
}

+ (PermissionStatus)permissionStatus {
Expand All @@ -48,7 +44,7 @@ + (PermissionStatus)permissionStatus {
case CNAuthorizationStatusLimited:
return PermissionStatusLimited;
}
} else if (@available(iOS 9.0, *)) {
} else {
CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];

switch (status) {
Expand All @@ -63,22 +59,6 @@ + (PermissionStatus)permissionStatus {
default:
return PermissionStatusGranted;
}
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus();

switch (status) {
case kABAuthorizationStatusNotDetermined:
return PermissionStatusDenied;
case kABAuthorizationStatusRestricted:
return PermissionStatusRestricted;
case kABAuthorizationStatusDenied:
return PermissionStatusPermanentlyDenied;
case kABAuthorizationStatusAuthorized:
return PermissionStatusGranted;
#pragma clang diagnostic pop
}
}

return PermissionStatusDenied;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,44 +174,36 @@ + (PermissionStatus)permissionStatus:(PermissionGroup)permission {


+ (PermissionStatus)determinePermissionStatus:(PermissionGroup)permission authorizationStatus:(CLAuthorizationStatus)authorizationStatus {
if (@available(iOS 8.0, *)) {
if (permission == PermissionGroupLocationAlways) {
switch (authorizationStatus) {
case kCLAuthorizationStatusNotDetermined:
return PermissionStatusDenied;
case kCLAuthorizationStatusRestricted:
return PermissionStatusRestricted;
case kCLAuthorizationStatusAuthorizedWhenInUse:
case kCLAuthorizationStatusDenied:
return PermissionStatusPermanentlyDenied;
case kCLAuthorizationStatusAuthorizedAlways:
return PermissionStatusGranted;
}
}

if (permission == PermissionGroupLocationAlways) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The diff for this method looks odd because of how the diff is computed. What I actually did is:

  • removed the if (@available(iOS 8.0, *)) { wrapping, and decreased the indent level of the code inside
  • trimmed the last switch to remove all the cases that were handled in the switch above.

The last two switches could be combined together, I just did the minimal change. I'm happy to restructure it if you would prefer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update. It looks like kCLAuthorizationStatusAuthorized is deprecated for iOS but not for macOS (10.6+). Isn't it a better idea to get rid of the deprecation warning by wrapping the code in a compile-time check so that we know when it gets deprecated for macOS?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would change the behavior of this method, and would require fully auditing all codepaths that lead to it to determine whether the deprecated constant is ever passed in as a parameter on iOS, since if it is adding a compile-time check would cause it to return the wrong value on iOS.

Do you want to make that part of this PR, rather than changing behavior in a follow-up?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No let's keep this one simple. Let's do this in another PR.

switch (authorizationStatus) {
case kCLAuthorizationStatusNotDetermined:
return PermissionStatusDenied;
case kCLAuthorizationStatusRestricted:
return PermissionStatusRestricted;
case kCLAuthorizationStatusAuthorizedWhenInUse:
case kCLAuthorizationStatusDenied:
return PermissionStatusPermanentlyDenied;
case kCLAuthorizationStatusAuthorizedWhenInUse:
case kCLAuthorizationStatusAuthorizedAlways:
return PermissionStatusGranted;
}
}

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"

switch (authorizationStatus) {
case kCLAuthorizationStatusNotDetermined:
return PermissionStatusDenied;
case kCLAuthorizationStatusRestricted:
return PermissionStatusRestricted;
case kCLAuthorizationStatusDenied:
return PermissionStatusPermanentlyDenied;
case kCLAuthorizationStatusAuthorizedWhenInUse:
case kCLAuthorizationStatusAuthorizedAlways:
return PermissionStatusGranted;
}

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"

switch (authorizationStatus) {
case kCLAuthorizationStatusAuthorized:
return PermissionStatusGranted;
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,15 @@ - (void)requestPermission:(PermissionGroup)permission completionHandler:(Permiss
return;
}

if (@available(iOS 9.3, *)) {
[MPMediaLibrary requestAuthorization:^(MPMediaLibraryAuthorizationStatus status) {
completionHandler([MediaLibraryPermissionStrategy determinePermissionStatus:status]);
}];
} else {
completionHandler(PermissionStatusPermanentlyDenied);
return;
}
[MPMediaLibrary requestAuthorization:^(MPMediaLibraryAuthorizationStatus status) {
completionHandler([MediaLibraryPermissionStrategy determinePermissionStatus:status]);
}];
}

+ (PermissionStatus)permissionStatus {
if (@available(iOS 9.3, *)) {
MPMediaLibraryAuthorizationStatus status = [MPMediaLibrary authorizationStatus];
MPMediaLibraryAuthorizationStatus status = [MPMediaLibrary authorizationStatus];

return [MediaLibraryPermissionStrategy determinePermissionStatus:status];
}

return PermissionStatusDenied;
return [MediaLibraryPermissionStrategy determinePermissionStatus:status];
}

+ (PermissionStatus)determinePermissionStatus:(MPMediaLibraryAuthorizationStatus)authorizationStatus API_AVAILABLE(ios(9.3)){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,59 +31,39 @@ - (void)requestPermission:(PermissionGroup)permission completionHandler:(Permiss
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
if(@available(iOS 10.0, *)) {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
UNAuthorizationOptions authorizationOptions = 0;
authorizationOptions += UNAuthorizationOptionSound;
authorizationOptions += UNAuthorizationOptionAlert;
authorizationOptions += UNAuthorizationOptionBadge;
[center requestAuthorizationWithOptions:(authorizationOptions) completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (error != nil || !granted) {
completionHandler(PermissionStatusPermanentlyDenied);
return;
}

dispatch_async(dispatch_get_main_queue(), ^{
[[UIApplication sharedApplication] registerForRemoteNotifications];
completionHandler(PermissionStatusGranted);
});
}];

} else {
UIUserNotificationType notificationTypes = 0;
notificationTypes |= UIUserNotificationTypeSound;
notificationTypes |= UIUserNotificationTypeAlert;
notificationTypes |= UIUserNotificationTypeBadge;
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:notificationTypes categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
UNAuthorizationOptions authorizationOptions = 0;
authorizationOptions += UNAuthorizationOptionSound;
authorizationOptions += UNAuthorizationOptionAlert;
authorizationOptions += UNAuthorizationOptionBadge;
[center requestAuthorizationWithOptions:(authorizationOptions) completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (error != nil || !granted) {
completionHandler(PermissionStatusPermanentlyDenied);
return;
}

[[UIApplication sharedApplication] registerForRemoteNotifications];
completionHandler(PermissionStatusGranted);
}
dispatch_async(dispatch_get_main_queue(), ^{
[[UIApplication sharedApplication] registerForRemoteNotifications];
completionHandler(PermissionStatusGranted);
});
}];
});
}

+ (PermissionStatus)permissionStatus {
__block PermissionStatus permissionStatus = PermissionStatusGranted;
if (@available(iOS 10 , *)) {
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
if (@available(iOS 12 , *) && settings.authorizationStatus == UNAuthorizationStatusProvisional) {
permissionStatus = PermissionStatusProvisional;
} else if (settings.authorizationStatus == UNAuthorizationStatusDenied) {
permissionStatus = PermissionStatusPermanentlyDenied;
} else if (settings.authorizationStatus == UNAuthorizationStatusNotDetermined) {
permissionStatus = PermissionStatusDenied;
}
dispatch_semaphore_signal(sem);
}];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
} else if (@available(iOS 8 , *)) {
UIUserNotificationSettings * setting = [[UIApplication sharedApplication] currentUserNotificationSettings];
if (setting.types == UIUserNotificationTypeNone) permissionStatus = PermissionStatusDenied;
} else {
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
if (@available(iOS 12 , *) && settings.authorizationStatus == UNAuthorizationStatusProvisional) {
permissionStatus = PermissionStatusProvisional;
} else if (settings.authorizationStatus == UNAuthorizationStatusDenied) {
permissionStatus = PermissionStatusPermanentlyDenied;
}
} else if (settings.authorizationStatus == UNAuthorizationStatusNotDetermined) {
permissionStatus = PermissionStatusDenied;
}
dispatch_semaphore_signal(sem);
}];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
return permissionStatus;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,10 @@ - (PermissionStatus)checkPermissionStatus:(PermissionGroup)permission {
}

- (void)checkServiceStatus:(PermissionGroup)permission completionHandler:(ServiceStatusHandler)completionHandler {
if (@available(iOS 11.0, *)) {
completionHandler([CMMotionActivityManager isActivityAvailable]
? ServiceStatusEnabled
: ServiceStatusDisabled
);
}

completionHandler(ServiceStatusDisabled);
completionHandler([CMMotionActivityManager isActivityAvailable]
? ServiceStatusEnabled
: ServiceStatusDisabled
);
}

- (void)requestPermission:(PermissionGroup)permission completionHandler:(PermissionStatusHandler)completionHandler errorHandler:(PermissionErrorHandler)errorHandler {
Expand All @@ -31,46 +27,38 @@ - (void)requestPermission:(PermissionGroup)permission completionHandler:(Permiss
return;
}

if (@available(iOS 11.0, *)) {
CMMotionActivityManager *motionManager = [[CMMotionActivityManager alloc] init];

NSDate *today = [NSDate new];
[motionManager queryActivityStartingFromDate:today toDate:today toQueue:[NSOperationQueue mainQueue] withHandler:^(NSArray<CMMotionActivity *> *__nullable activities, NSError *__nullable error) {
PermissionStatus status = [SensorPermissionStrategy permissionStatus];
completionHandler(status);
}];
} else {
completionHandler(PermissionStatusDenied);
}
CMMotionActivityManager *motionManager = [[CMMotionActivityManager alloc] init];

NSDate *today = [NSDate new];
[motionManager queryActivityStartingFromDate:today toDate:today toQueue:[NSOperationQueue mainQueue] withHandler:^(NSArray<CMMotionActivity *> *__nullable activities, NSError *__nullable error) {
PermissionStatus status = [SensorPermissionStrategy permissionStatus];
completionHandler(status);
}];

}

+ (PermissionStatus)permissionStatus {
if (@available(iOS 11.0, *)) {
CMAuthorizationStatus status = [CMMotionActivityManager authorizationStatus];
PermissionStatus permissionStatus;

switch (status) {
case CMAuthorizationStatusNotDetermined:
permissionStatus = PermissionStatusDenied;
break;
case CMAuthorizationStatusRestricted:
permissionStatus = PermissionStatusRestricted;
break;
case CMAuthorizationStatusDenied:
permissionStatus = PermissionStatusPermanentlyDenied;
break;
case CMAuthorizationStatusAuthorized:
permissionStatus = PermissionStatusGranted;
break;
default:
permissionStatus = PermissionStatusGranted;
}

return permissionStatus;
CMAuthorizationStatus status = [CMMotionActivityManager authorizationStatus];
PermissionStatus permissionStatus;

switch (status) {
case CMAuthorizationStatusNotDetermined:
permissionStatus = PermissionStatusDenied;
break;
case CMAuthorizationStatusRestricted:
permissionStatus = PermissionStatusRestricted;
break;
case CMAuthorizationStatusDenied:
permissionStatus = PermissionStatusPermanentlyDenied;
break;
case CMAuthorizationStatusAuthorized:
permissionStatus = PermissionStatusGranted;
break;
default:
permissionStatus = PermissionStatusGranted;
}

return PermissionStatusDenied;
return permissionStatus;
}

@end
Expand Down
Loading