diff --git a/package/ios/CameraViewManager.swift b/package/ios/CameraViewManager.swift index e63b6d9fbb..ecfcf3d2ad 100644 --- a/package/ios/CameraViewManager.swift +++ b/package/ios/CameraViewManager.swift @@ -131,11 +131,10 @@ final class CameraViewManager: RCTViewManager { } @objc - final func requestLocationPermission(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { - let manager = CLLocationManager() - let promise = Promise(resolver: resolve, rejecter: reject) - let delegate = LocationManagerDelegate(promise: promise) - manager.requestWhenInUseAuthorization() + final func requestLocationPermission(_ resolve: @escaping RCTPromiseResolveBlock, reject _: @escaping RCTPromiseRejectBlock) { + CLLocationManager.requestAccess(for: .whenInUse) { status in + resolve(status.descriptor) + } } // MARK: Private diff --git a/package/ios/Core/CameraSession+Location.swift b/package/ios/Core/CameraSession+Location.swift index 69b3db78e7..876d3de1c9 100644 --- a/package/ios/Core/CameraSession+Location.swift +++ b/package/ios/Core/CameraSession+Location.swift @@ -10,7 +10,8 @@ import Foundation extension CameraSession { func configureLocationOutput(configuration: CameraConfiguration) throws { - if configuration.enableLocation { + let locationServicesEnabled = CLLocationManager.locationServicesEnabled() + if configuration.enableLocation && locationServicesEnabled { let locationProvider = LocationProvider() guard locationProvider.hasPermission else { // Location permission has been denied diff --git a/package/ios/Core/PhotoCaptureDelegate.swift b/package/ios/Core/PhotoCaptureDelegate.swift index 87afc5009b..205cc34361 100644 --- a/package/ios/Core/PhotoCaptureDelegate.swift +++ b/package/ios/Core/PhotoCaptureDelegate.swift @@ -8,12 +8,9 @@ import AVFoundation -// Keeps a strong reference on delegates, as the AVCapturePhotoOutput only holds a weak reference. -private var delegatesReferences: [NSObject] = [] - // MARK: - PhotoCaptureDelegate -class PhotoCaptureDelegate: NSObject, AVCapturePhotoCaptureDelegate { +class PhotoCaptureDelegate: GlobalReferenceHolder, AVCapturePhotoCaptureDelegate { private let promise: Promise private let enableShutterSound: Bool private let cameraSessionDelegate: CameraSessionDelegate? @@ -28,7 +25,7 @@ class PhotoCaptureDelegate: NSObject, AVCapturePhotoCaptureDelegate { self.metadataProvider = metadataProvider self.cameraSessionDelegate = cameraSessionDelegate super.init() - delegatesReferences.append(self) + makeGlobal() } func photoOutput(_: AVCapturePhotoOutput, willCapturePhotoFor _: AVCaptureResolvedPhotoSettings) { @@ -43,7 +40,7 @@ class PhotoCaptureDelegate: NSObject, AVCapturePhotoCaptureDelegate { func photoOutput(_: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { defer { - delegatesReferences.removeAll(where: { $0 == self }) + removeGlobal() } if let error = error as NSError? { promise.reject(error: .capture(.unknown(message: error.description)), cause: error) @@ -80,7 +77,7 @@ class PhotoCaptureDelegate: NSObject, AVCapturePhotoCaptureDelegate { func photoOutput(_: AVCapturePhotoOutput, didFinishCaptureFor _: AVCaptureResolvedPhotoSettings, error: Error?) { defer { - delegatesReferences.removeAll(where: { $0 == self }) + removeGlobal() } if let error = error as NSError? { if error.code == -11807 { diff --git a/package/ios/Extensions/CLLocationManager+requestAccess.swift b/package/ios/Extensions/CLLocationManager+requestAccess.swift new file mode 100644 index 0000000000..b678fe801a --- /dev/null +++ b/package/ios/Extensions/CLLocationManager+requestAccess.swift @@ -0,0 +1,58 @@ +// +// CLLocationManager+requestAccess.swift +// VisionCamera +// +// Created by Marc Rousavy on 21.03.24. +// + +import CoreLocation +import Foundation + +extension CLLocationManager { + enum AccessType { + case whenInUse + case always + } + + static func requestAccess(for accessType: AccessType, _ callback: @escaping (_ status: CLAuthorizationStatus) -> Void) { + let manager = CLLocationManager() + let delegate = CLLocationManagerCallbackDelegate(locationManager: manager, callback: callback) + manager.delegate = delegate + switch accessType { + case .whenInUse: + manager.requestWhenInUseAuthorization() + case .always: + manager.requestAlwaysAuthorization() + } + } + + private class CLLocationManagerCallbackDelegate: GlobalReferenceHolder, CLLocationManagerDelegate { + private let locationManager: CLLocationManager + private let callback: (_ status: CLAuthorizationStatus) -> Void + + init(locationManager: CLLocationManager, callback: @escaping (_ status: CLAuthorizationStatus) -> Void) { + self.locationManager = locationManager + self.callback = callback + super.init() + makeGlobal() + } + + private var authorizationStatus: CLAuthorizationStatus { + if #available(iOS 14.0, *) { + return locationManager.authorizationStatus + } else { + return CLLocationManager.authorizationStatus() + } + } + + func locationManagerDidChangeAuthorization(_: CLLocationManager) { + if authorizationStatus == .notDetermined { + // This method is called once on init with status "notDetermined", ignore the first call. + return + } + + removeGlobal() + callback(authorizationStatus) + } + } +} diff --git a/package/ios/LocationManagerDelegate.swift b/package/ios/LocationManagerDelegate.swift deleted file mode 100644 index 92c62bd10b..0000000000 --- a/package/ios/LocationManagerDelegate.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// LocationManagerDelegate.swift -// VisionCamera -// -// Created by Marc Rousavy on 19.03.24. -// - -import CoreLocation -import Foundation - -// Keeps a strong reference on delegates, as the CLLocationManager only holds a weak reference. -private var delegatesReferences: [NSObject] = [] - -// MARK: - LocationManagerDelegate - -class LocationManagerDelegate: NSObject, CLLocationManagerDelegate { - private let promise: Promise - - init(promise: Promise) { - self.promise = promise - super.init() - delegatesReferences.append(self) - } - - func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { - defer { - delegatesReferences.removeAll(where: { $0 == self }) - } - - if #available(iOS 14.0, *) { - let status = manager.authorizationStatus.descriptor - promise.resolve(status) - } else { - let status = CLLocationManager.authorizationStatus().descriptor - promise.resolve(status) - } - } -} diff --git a/package/ios/React Utils/GlobalReferenceHolder.swift b/package/ios/React Utils/GlobalReferenceHolder.swift new file mode 100644 index 0000000000..5de42cc738 --- /dev/null +++ b/package/ios/React Utils/GlobalReferenceHolder.swift @@ -0,0 +1,20 @@ +// +// GlobalReferenceHolder.swift +// VisionCamera +// +// Created by Marc Rousavy on 21.03.24. +// + +import Foundation + +class GlobalReferenceHolder: NSObject { + private static var references: [GlobalReferenceHolder] = [] + + func makeGlobal() { + GlobalReferenceHolder.references.append(self) + } + + func removeGlobal() { + GlobalReferenceHolder.references.removeAll(where: { $0 == self }) + } +}