apple_kit

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

RadrootsApplePermissionStatus.swift (6490B)


      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 }