Permission Prompts for the Apple SDK

Request additional system permissions (e.g., location) from users using Opt-in Actions.

The Airship SDK automatically handles push notification permissions. For additional permissions like location, you can use Opt-in Actions to prompt users using native permission prompts.

Opt-in Actions are a special type of ActionA configurable behavior that occurs when a user interacts with your message, e.g., opening a web page. that are handled by PermissionsManager. For an overview of all supported actions and where they are available, see the Actions guide.

Supported Opt-in Types

  • Push — Handled automatically by the SDK (no implementation needed)
  • Location — Requires implementing a custom PermissionDelegate

Implementing Location Opt-in

To implement Location Opt-in, create a custom PermissionDelegate and register it with PermissionsManager to handle location permissions.

Create a Location Permission Delegate

Creating a location permission delegate

import Foundation
import CoreLocation
import AirshipCore
import Combine

class LocationPermissionDelegate: AirshipPermissionDelegate {
    let locationManager = CLLocationManager()

    @MainActor
    func checkPermissionStatus() async -> AirshipCore.AirshipPermissionStatus {
        return self.status
    }

    @MainActor
    func requestPermission() async -> AirshipCore.AirshipPermissionStatus {
        guard (self.status == .notDetermined) else {
            return self.status
        }

        guard (AppStateTracker.shared.state == .active) else {
            return .notDetermined
        }

        locationManager.requestAlwaysAuthorization()
        await waitActive()
        return self.status
    }


    var status: AirshipPermissionStatus {
        switch(locationManager.authorizationStatus) {
        case .notDetermined:
            return .notDetermined
        case .restricted:
            return .denied
        case .denied:
            return .denied
        case .authorizedAlways:
            return .granted
        case .authorizedWhenInUse:
            return .granted
        @unknown default:
            return .notDetermined
        }
    }
}


@MainActor
private func waitActive() async {
    var subscription: AnyCancellable?
    await withCheckedContinuation { continuation in
        subscription = NotificationCenter.default.publisher(for: AppStateTracker.didBecomeActiveNotification)
            .first()
            .sink { _ in
                continuation.resume()
            }
    }

    subscription?.cancel()
}
#import <CoreLocation/CoreLocation.h>
#import <AirshipCore/AirshipCore.h>

@interface LocationPermissionDelegate : NSObject <UAPermissionDelegate>
@property (nonatomic, strong) CLLocationManager *locationManager;
@end

@implementation LocationPermissionDelegate

- (instancetype)init {
    self = [super init];
    if (self) {
        _locationManager = [[CLLocationManager alloc] init];
    }
    return self;
}

- (UAPermissionStatus)checkPermissionStatus {
    return [self status];
}

- (void)requestPermissionWithCompletionHandler:(void (^)(UAPermissionStatus))completionHandler {
    if ([self status] != UAPermissionStatusNotDetermined) {
        completionHandler([self status]);
        return;
    }
    
    if ([UAAppStateTracker shared].state != UAAppStateActive) {
        completionHandler(UAPermissionStatusNotDetermined);
        return;
    }
    
    [self.locationManager requestAlwaysAuthorization];
    
    // Wait for app to become active and check status
    dispatch_async(dispatch_get_main_queue(), ^{
        completionHandler([self status]);
    });
}

- (UAPermissionStatus)status {
    switch (self.locationManager.authorizationStatus) {
        case kCLAuthorizationStatusNotDetermined:
            return UAPermissionStatusNotDetermined;
        case kCLAuthorizationStatusRestricted:
        case kCLAuthorizationStatusDenied:
            return UAPermissionStatusDenied;
        case kCLAuthorizationStatusAuthorizedAlways:
        case kCLAuthorizationStatusAuthorizedWhenInUse:
            return UAPermissionStatusGranted;
        default:
            return UAPermissionStatusNotDetermined;
    }
}

@end

Register the Permission Delegate

After creating a location PermissionDelegate, register it with PermissionsManager after takeOff:

Registering the permission delegate

Airship.permissionsManager.setDelegate(
  LocationPermissionDelegate(),
  permission: .location
)
LocationPermissionDelegate *delegate = [[LocationPermissionDelegate alloc] init];
[[UAirship permissionsManager] setDelegate:delegate permission:UAPermissionLocation];