apple_kit

Apple-native services for Radroots iOS and macOS apps
git clone https://radroots.dev/git/apple_kit.git
Log | Files | Refs | README | LICENSE

RadrootsApplePermissionStatus.swift (6838B)


      1 import Foundation
      2 
      3 #if canImport(AVFoundation)
      4     import AVFoundation
      5 #endif
      6 
      7 #if canImport(CoreLocation)
      8     import CoreLocation
      9 #endif
     10 
     11 #if canImport(Photos)
     12     import Photos
     13 #endif
     14 
     15 #if canImport(UserNotifications)
     16     import UserNotifications
     17 #endif
     18 
     19 public struct RadrootsApplePermissionStatusAdapters: Sendable {
     20     public let now: @Sendable () -> Date
     21     public let notificationStatus: @Sendable () async -> RadrootsPermissionStatus
     22     public let cameraStatus: @Sendable () -> RadrootsPermissionStatus
     23     public let photosStatus: @Sendable () -> RadrootsPermissionStatus
     24     public let microphoneStatus: @Sendable () -> RadrootsPermissionStatus
     25     public let locationStatus: @Sendable () -> RadrootsPermissionStatus
     26 
     27     public init(
     28         now: @escaping @Sendable () -> Date = Date.init,
     29         notificationStatus: @escaping @Sendable () async -> RadrootsPermissionStatus,
     30         cameraStatus: @escaping @Sendable () -> RadrootsPermissionStatus,
     31         photosStatus: @escaping @Sendable () -> RadrootsPermissionStatus,
     32         microphoneStatus: @escaping @Sendable () -> RadrootsPermissionStatus,
     33         locationStatus: @escaping @Sendable () -> RadrootsPermissionStatus
     34     ) {
     35         self.now = now
     36         self.notificationStatus = notificationStatus
     37         self.cameraStatus = cameraStatus
     38         self.photosStatus = photosStatus
     39         self.microphoneStatus = microphoneStatus
     40         self.locationStatus = locationStatus
     41     }
     42 
     43     public static var live: Self {
     44         Self(
     45             notificationStatus: {
     46                 await Self.currentNotificationStatus()
     47             },
     48             cameraStatus: {
     49                 Self.currentCameraStatus()
     50             },
     51             photosStatus: {
     52                 Self.currentPhotosStatus()
     53             },
     54             microphoneStatus: {
     55                 Self.currentMicrophoneStatus()
     56             },
     57             locationStatus: {
     58                 Self.currentLocationStatus()
     59             }
     60         )
     61     }
     62 
     63     public static func currentNotificationStatus() async -> RadrootsPermissionStatus {
     64         #if canImport(UserNotifications)
     65             return await withCheckedContinuation { continuation in
     66                 UNUserNotificationCenter.current().getNotificationSettings { settings in
     67                     continuation.resume(returning: Self.permissionStatus(for: settings.authorizationStatus))
     68                 }
     69             }
     70         #else
     71             return .unsupported
     72         #endif
     73     }
     74 
     75     public static func currentCameraStatus() -> RadrootsPermissionStatus {
     76         #if canImport(AVFoundation)
     77             return permissionStatus(for: AVCaptureDevice.authorizationStatus(for: .video))
     78         #else
     79             return .unsupported
     80         #endif
     81     }
     82 
     83     public static func currentPhotosStatus() -> RadrootsPermissionStatus {
     84         #if canImport(Photos)
     85             return permissionStatus(for: PHPhotoLibrary.authorizationStatus(for: .readWrite))
     86         #else
     87             return .unsupported
     88         #endif
     89     }
     90 
     91     public static func currentMicrophoneStatus() -> RadrootsPermissionStatus {
     92         #if canImport(AVFoundation)
     93             return permissionStatus(for: AVCaptureDevice.authorizationStatus(for: .audio))
     94         #else
     95             return .unsupported
     96         #endif
     97     }
     98 
     99     public static func currentLocationStatus() -> RadrootsPermissionStatus {
    100         #if canImport(CoreLocation)
    101             guard CLLocationManager.locationServicesEnabled() else {
    102                 return .unavailable
    103             }
    104             return permissionStatus(for: CLLocationManager().authorizationStatus)
    105         #else
    106             return .unsupported
    107         #endif
    108     }
    109 
    110     #if canImport(UserNotifications)
    111         public static func permissionStatus(for authorizationStatus: UNAuthorizationStatus) -> RadrootsPermissionStatus {
    112             switch authorizationStatus {
    113             case .notDetermined:
    114                 .notDetermined
    115             case .denied:
    116                 .denied
    117             case .authorized:
    118                 .authorized
    119             case .provisional:
    120                 .limited
    121             case .ephemeral:
    122                 .limited
    123             @unknown default:
    124                 .unavailable
    125             }
    126         }
    127     #endif
    128 
    129     #if canImport(AVFoundation)
    130         public static func permissionStatus(for authorizationStatus: AVAuthorizationStatus) -> RadrootsPermissionStatus {
    131             switch authorizationStatus {
    132             case .notDetermined:
    133                 .notDetermined
    134             case .restricted:
    135                 .restricted
    136             case .denied:
    137                 .denied
    138             case .authorized:
    139                 .authorized
    140             @unknown default:
    141                 .unavailable
    142             }
    143         }
    144     #endif
    145 
    146     #if canImport(Photos)
    147         public static func permissionStatus(for authorizationStatus: PHAuthorizationStatus) -> RadrootsPermissionStatus {
    148             switch authorizationStatus {
    149             case .notDetermined:
    150                 .notDetermined
    151             case .restricted:
    152                 .restricted
    153             case .denied:
    154                 .denied
    155             case .authorized:
    156                 .authorized
    157             case .limited:
    158                 .limited
    159             @unknown default:
    160                 .unavailable
    161             }
    162         }
    163     #endif
    164 
    165     #if canImport(CoreLocation)
    166         public static func permissionStatus(for authorizationStatus: CLAuthorizationStatus) -> RadrootsPermissionStatus {
    167             switch authorizationStatus {
    168             case .notDetermined:
    169                 .notDetermined
    170             case .restricted:
    171                 .restricted
    172             case .denied:
    173                 .denied
    174             case .authorizedAlways:
    175                 .authorized
    176             #if os(iOS)
    177                 case .authorizedWhenInUse:
    178                     .authorized
    179             #endif
    180             @unknown default:
    181                 .unavailable
    182             }
    183         }
    184     #endif
    185 }
    186 
    187 public final class RadrootsApplePermissionStatusProvider: RadrootsPermissionStatusProvider, Sendable {
    188     private let adapters: RadrootsApplePermissionStatusAdapters
    189 
    190     public init(adapters: RadrootsApplePermissionStatusAdapters = .live) {
    191         self.adapters = adapters
    192     }
    193 
    194     public func snapshot(for kind: RadrootsPermissionKind) async throws -> RadrootsPermissionSnapshot {
    195         let status = switch kind {
    196         case .notifications:
    197             await adapters.notificationStatus()
    198         case .camera:
    199             adapters.cameraStatus()
    200         case .photos:
    201             adapters.photosStatus()
    202         case .microphone:
    203             adapters.microphoneStatus()
    204         case .location:
    205             adapters.locationStatus()
    206         }
    207         return RadrootsPermissionSnapshot(kind: kind, status: status, observedAt: adapters.now())
    208     }
    209 }