diff --git a/Package.swift b/Package.swift index 48b0461..5e0634b 100644 --- a/Package.swift +++ b/Package.swift @@ -15,6 +15,7 @@ import PackageDescription /// - `CompressionDependency`: `\.compress` and `\.decompress` /// - `DataDependency`: `\.dataReader` and `\.dataWriter` /// - `DeviceDependency`: `\.device` and `\.deviceCheckDevice` +/// - `LocationManagerDependency`: `\.locationManager` /// - `LoggerDependency`: `\.logger` /// - `NotificationCenterDependency`: `\.notificationCenter` /// - `PathDependency`: `\.path` @@ -182,6 +183,7 @@ let package = Package( "DataDependency", "DependenciesAdditionsBasics", "DeviceDependency", + "LocationManagerDependency", "LoggerDependency", "NotificationCenterDependency", "PathDependency", @@ -240,6 +242,15 @@ let package = Package( ] ), + .target( + name: "LocationManagerDependency", + dependencies: [ + .product(name: "Dependencies", package: "swift-dependencies"), + .product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"), + "DependenciesAdditionsBasics", + ] + ), + .target( name: "LoggerDependency", dependencies: [ @@ -427,6 +438,7 @@ func addIndividualProducts() { .library(name: "CompressionDependency", targets: ["CompressionDependency"]), .library(name: "DataDependency", targets: ["DataDependency"]), .library(name: "DeviceDependency", targets: ["DeviceDependency"]), + .library(name: "LocationManagerDependency", targets: ["LocationManagerDependency"]), .library(name: "LoggerDependency", targets: ["LoggerDependency"]), .library(name: "NotificationCenterDependency", targets: ["NotificationCenterDependency"]), .library(name: "PathDependency", targets: ["PathDependency"]), diff --git a/Sources/LocationManagerDependency/LocationManagerDependency.swift b/Sources/LocationManagerDependency/LocationManagerDependency.swift new file mode 100644 index 0000000..fa7d470 --- /dev/null +++ b/Sources/LocationManagerDependency/LocationManagerDependency.swift @@ -0,0 +1,592 @@ +#if canImport(CoreLocation) + @preconcurrency import CoreLocation + import Dependencies + @_spi(Internals) import DependenciesAdditionsBasics + + extension DependencyValues { + /// An abstraction of `CLLocationManager`, the central object for managing + /// notification-related activities for your app or app extension. + public var locationManager: LocationManager { + get { self[LocationManager.self] } + set { self[LocationManager.self] = newValue } + } + } + + extension LocationManager: DependencyKey { + public static var liveValue: LocationManager { .system } + public static var testValue: LocationManager { .unimplemented } + public static var previewValue: LocationManager { .system } + } + + public struct LocationManager: Sendable, ConfigurableProxy { + public struct Implementation: Sendable { + // MARK: Determining the availability of services + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) + @ReadOnlyProxy public var significantLocationChangeMonitoringAvailable: Bool + #endif + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) || os(watchOS) + @ReadOnlyProxy public var headingAvailable: Bool + #endif + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) + @ReadOnlyProxy public var isAuthorizedForWidgetUpdates: Bool + #endif + @ReadOnlyProxy public var accuracyAuthorization: CLAccuracyAuthorization + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) + @FunctionProxy public var isMonitoringAvailable: @Sendable (AnyClass) -> Bool + @ReadOnlyProxy public var isRangingAvailable: Bool + #endif + @ReadOnlyProxy public var locationServicesEnabled: Bool + + // MARK: Receiving data from location services + @ReadWriteProxy public var delegate: (CLLocationManagerDelegate & Sendable)? + + // MARK: Requesting authorization for location services + @FunctionProxy public var requestWhenInUseAuthorization: @Sendable () -> Void + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) || os(watchOS) + @FunctionProxy public var requestAlwaysAuthorization: @Sendable () -> Void + #endif + @FunctionProxy public var requestTemporaryFullAccuracyAuthorization: + @Sendable (_ purposeKey: String, _ completion: ((Error?) -> Void)?) -> Void + @ReadOnlyProxy public var authorizationStatus: CLAuthorizationStatus + + // MARK: Specifying distance and accuracy + @ReadWriteProxy public var distanceFilter: CLLocationDistance + @ReadWriteProxy public var desiredAccuracy: CLLocationAccuracy + + // MARK: Running the standard location service + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) || os(watchOS) + @FunctionProxy public var startUpdatingLocation: @Sendable () -> Void + #endif + @FunctionProxy public var stopUpdatingLocation: @Sendable () -> Void + @FunctionProxy public var requestLocation: @Sendable () -> Void + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) + @ReadWriteProxy public var pausesLocationUpdatesAutomatically: Bool + #endif + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) || os(watchOS) + @ReadWriteProxy public var allowsBackgroundLocationUpdates: Bool + #endif + #if os(iOS) || targetEnvironment(macCatalyst) + @ReadWriteProxy public var showsBackgroundLocationIndicator: Bool + #endif + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) || os(watchOS) + @ReadWriteProxy public var activityType: CLActivityType + #endif + + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) + // MARK: Running the significant change location service + @FunctionProxy public var startMonitoringSignificantLocationChanges: @Sendable () -> Void + @FunctionProxy public var stopMonitoringSignificantLocationChanges: @Sendable () -> Void + + // MARK: Running the visits location service + @FunctionProxy public var startMonitoringVisits: @Sendable () -> Void + @FunctionProxy public var stopMonitoringVisits: @Sendable () -> Void + #endif + + // MARK: Running the heading service + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) || os(watchOS) + @FunctionProxy public var startUpdatingHeading: @Sendable () -> Void + #endif + #if os(iOS) || targetEnvironment(macCatalyst) || os(watchOS) + @FunctionProxy public var stopUpdatingHeading: @Sendable () -> Void + #endif + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) || os(watchOS) + @FunctionProxy public var dismissHeadingCalibrationDisplay: @Sendable () -> Void + @ReadWriteProxy public var headingFilter: CLLocationDegrees + @ReadWriteProxy public var headingOrientation: CLDeviceOrientation + #endif + + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) + // MARK: Running the region-monitoring service + @FunctionProxy public var startMonitoring: @Sendable (CLRegion) -> Void + @FunctionProxy public var stopMonitoring: @Sendable (CLRegion) -> Void + @ReadOnlyProxy public var monitoredRegions: Set + @ReadOnlyProxy public var maximumRegionMonitoringDistance: CLLocationDistance + + // MARK: Performing beacon ranging + @FunctionProxy public var requestState: @Sendable (CLRegion) -> Void + @FunctionProxy public var startRangingBeacons: + @Sendable (CLBeaconIdentityConstraint) -> Void + @FunctionProxy public var stopRangingBeacons: @Sendable (CLBeaconIdentityConstraint) -> Void + @ReadOnlyProxy public var rangedBeaconConstraints: Set + #endif + + #if os(iOS) + // MARK: Monitoring location push notifications + @FunctionProxy public var startMonitoringLocationPushes: + @Sendable (_ completion: ((Data?, Error?) -> Void)?) -> Void + @FunctionProxy public var stopMonitoringLocationPushes: @Sendable () -> Void + #endif + + // MARK: Getting recent location and heading data + @ReadOnlyProxy public var location: CLLocation? + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) || os(watchOS) + @ReadOnlyProxy public var heading: CLHeading? + #endif + + #if os(watchOS) + // MARK: watchOS-specific methods + @FunctionProxy public var requestHistoricalLocations: + @Sendable ( + _ purposeKey: String, + _ sampleCount: Int, + _ completionHandler: @escaping ([CLLocation], Error?) -> Void + ) -> Void + #endif + } + + @_spi(Internals) public var _implementation: Implementation + + // MARK: Determining the availability of services + + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) + + /// Returns a Boolean value indicating whether the significant-change location service is + /// available on the device. + @available(iOS 4, macOS 10.7, macCatalyst 13.1, *) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + public func significantLocationChangeMonitoringAvailable() -> Bool { + self._implementation.significantLocationChangeMonitoringAvailable + } + + #endif + + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) || os(watchOS) + + /// Returns a Boolean value indicating whether the location manager is able to generate + /// heading-related events. + @available(iOS 4, macOS 10.7, macCatalyst 13.1, watchOS 2, *) + @available(tvOS, unavailable) + public func headingAvailable() -> Bool { + self._implementation.headingAvailable + } + + #endif + + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) + + /// A Boolean value that indicates whether a widget is eligible to receive location updates. + @available(iOS 14, macOS 11, macCatalyst 14, *) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + public var isAuthorizedForWidgetUpdates: Bool { + self._implementation.isAuthorizedForWidgetUpdates + } + + #endif + + /// A value that indicates the level of location accuracy the app has permission to use. + @available(iOS 14, macOS 11, macCatalyst 14, tvOS 14, watchOS 7, *) + public var accuracyAuthorization: CLAccuracyAuthorization { + self._implementation.accuracyAuthorization + } + + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) + + /// Returns a Boolean value indicating whether the device supports region monitoring using the + /// specified class. + @available(iOS 7, macOS 10.10, macCatalyst 13.1, *) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + public func isMonitoringAvailable(for class: AnyClass) -> Bool { + self._implementation.isMonitoringAvailable(`class`) + } + + /// Returns a Boolean value indicating whether the device supports ranging of beacons that use + /// the iBeacon protocol. + @available(iOS 7, macOS 10.15, macCatalyst 13.1, *) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + public func isRangingAvailable() -> Bool { + self._implementation.isRangingAvailable + } + + #endif + + /// Returns a Boolean value indicating whether location services are enabled on the device. + @available(iOS 4, macOS 10.7, macCatalyst 13.1, tvOS 9, watchOS 2, *) + public func locationServicesEnabled() -> Bool { + self._implementation.locationServicesEnabled + } + + // MARK: Receiving data from location services + + /// The delegate object to receive update events. + @available(iOS 2, macOS 10.6, macCatalyst 13.1, tvOS 9, watchOS 2, *) + public var delegate: (CLLocationManagerDelegate & Sendable)? { + get { self._implementation.delegate } + nonmutating set { self._implementation.delegate = newValue } + } + + // MARK: Requesting authorization for location services + + /// Requests the user’s permission to use location services while the app is in use. + @available(iOS 8, macOS 10.15, macCatalyst 13.1, tvOS 9, watchOS 2, *) + public func requestWhenInUseAuthorization() { + self._implementation.requestWhenInUseAuthorization() + } + + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) || os(watchOS) + + /// Requests the user’s permission to use location services regardless of whether the app is + /// in use. + @available(iOS 8, macOS 10.15, macCatalyst 13.1, watchOS 2, *) + @available(tvOS, unavailable) + public func requestAlwaysAuthorization() { + self._implementation.requestAlwaysAuthorization() + } + + #endif + + /// Requests permission to temporarily use location services with full accuracy and reports the + /// results to the provided completion handler. + @available(iOS 14, macOS 11, macCatalyst 14, tvOS 14, watchOS 7, *) + public func requestTemporaryFullAccuracyAuthorization( + withPurposeKey purposeKey: String, + completion: ((Error?) -> Void)? + ) { + self._implementation.requestTemporaryFullAccuracyAuthorization(purposeKey, completion) + } + + /// Requests permission to temporarily use location services with full accuracy. + @available(iOS 14, macOS 11, macCatalyst 14, tvOS 14, watchOS 7, *) + public func requestTemporaryFullAccuracyAuthorization(withPurposeKey purposeKey: String) { + self._implementation.requestTemporaryFullAccuracyAuthorization(purposeKey, nil) + } + + /// The current authorization status for the app. + /// - Remark: In iOS 4.2–14.0, iPadOS 4.2–14.0, macOS 10.7–11.0, + /// Mac Catalyst 13.1–14.0, tvOS 9.0–14.0 and watchOS 2.0–7.0, + /// this property calls the deprecated + /// `class func authorizationStatus() -> CLAuthorizationStatus`. + @available(iOS 4.2, macOS 10.7, macCatalyst 13.1, tvOS 9, watchOS 2, *) + public var authorizationStatus: CLAuthorizationStatus { + self._implementation.authorizationStatus + } + + // MARK: Specifying distance and accuracy + + /// The minimum distance in meters the device must move horizontally before an update event + /// is generated. + @available(iOS 2, macOS 10.6, macCatalyst 13.1, tvOS 9, watchOS 2, *) + public var distanceFilter: CLLocationDistance { + get { self._implementation.distanceFilter } + nonmutating set { self._implementation.distanceFilter = newValue } + } + + /// The accuracy of the location data that your app wants to receive. + @available(iOS 2, macOS 10.6, macCatalyst 13.1, tvOS 9, watchOS 2, *) + public var desiredAccuracy: CLLocationAccuracy { + get { self._implementation.desiredAccuracy } + nonmutating set { self._implementation.desiredAccuracy = newValue } + } + + // MARK: Running the standard location service + + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) || os(watchOS) + + /// Starts the generation of updates that report the user’s current location. + @available(iOS 2, macOS 10.6, macCatalyst 13, watchOS 3, *) + @available(tvOS, unavailable) + public func startUpdatingLocation() { + self._implementation.startUpdatingLocation() + } + + #endif + + /// Stops the generation of location updates. + @available(iOS 2, macOS 10.6, macCatalyst 13.1, tvOS 9, watchOS 2, *) + public func stopUpdatingLocation() { + self._implementation.stopUpdatingLocation() + } + + /// Requests the one-time delivery of the user’s current location. + @available(iOS 9, macOS 10.14, macCatalyst 13.1, tvOS 9, watchOS 2, *) + public func requestLocation() { + self._implementation.requestLocation() + } + + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) + + /// A Boolean value that indicates whether the location-manager object may pause location updates. + @available(iOS 6, macOS 10.15, macCatalyst 13.1, *) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + public var pausesLocationUpdatesAutomatically: Bool { + get { self._implementation.pausesLocationUpdatesAutomatically } + nonmutating set { self._implementation.pausesLocationUpdatesAutomatically = newValue } + } + + #endif + + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) || os(watchOS) + + /// A Boolean value that indicates whether the app receives location updates when running + /// in the background. + @available(iOS 6, macOS 10.15, macCatalyst 13.1, watchOS 4, *) + @available(tvOS, unavailable) + public var allowsBackgroundLocationUpdates: Bool { + get { self._implementation.allowsBackgroundLocationUpdates } + nonmutating set { self._implementation.allowsBackgroundLocationUpdates = newValue } + } + + #endif + + #if os(iOS) || targetEnvironment(macCatalyst) + + /// A Boolean value that indicates whether the status bar changes its appearance when an app uses + /// location services in the background. + @available(iOS 11, macCatalyst 13.1, *) + @available(macOS, unavailable) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + public var showsBackgroundLocationIndicator: Bool { + get { self._implementation.showsBackgroundLocationIndicator } + nonmutating set { self._implementation.showsBackgroundLocationIndicator = newValue } + } + + #endif + + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) || os(watchOS) + + /// The type of activity the app expects the user to typically perform while in the app’s + /// location session. + @available(iOS 6, macOS 10.15, macCatalyst 13.1, watchOS 4, *) + @available(tvOS, unavailable) + public var activityType: CLActivityType { + get { self._implementation.activityType } + nonmutating set { self._implementation.activityType = newValue } + } + + #endif + + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) + + // MARK: Running the significant change location service + + /// Starts the generation of updates based on significant location changes. + @available(iOS 4, macOS 10.7, macCatalyst 13.1, *) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + public func startMonitoringSignificantLocationChanges() { + self._implementation.startMonitoringSignificantLocationChanges() + } + + /// Stops the delivery of location events based on significant location changes. + @available(iOS 4, macOS 10.7, macCatalyst 13.1, *) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + public func stopMonitoringSignificantLocationChanges() { + self._implementation.stopMonitoringSignificantLocationChanges() + } + + // MARK: Running the visits location service + + /// Starts the delivery of visit-related events. + @available(iOS 8, macOS 10.15, macCatalyst 13.1, *) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + public func startMonitoringVisits() { + self._implementation.startMonitoringVisits() + } + + /// Stops the delivery of visit-related events. + @available(iOS 8, macOS 10.15, macCatalyst 13.1, *) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + public func stopMonitoringVisits() { + self._implementation.stopMonitoringVisits() + } + + #endif + + // MARK: Running the heading service + + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) || os(watchOS) + + /// Starts the generation of updates that report the user’s current heading. + @available(iOS 3, macOS 10.15, macCatalyst 13.1, watchOS 2, *) + @available(tvOS, unavailable) + public func startUpdatingHeading() { + self._implementation.startUpdatingHeading() + } + + #endif + + #if os(iOS) || targetEnvironment(macCatalyst) || os(watchOS) + + /// Stops the generation of heading updates. + @available(iOS 3, macCatalyst 13.1, watchOS 2, *) + @available(macOS, unavailable) + @available(tvOS, unavailable) + public func stopUpdatingHeading() { + self._implementation.stopUpdatingHeading() + } + + #endif + + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) || os(watchOS) + + /// Dismisses the heading calibration view from the screen immediately. + @available(iOS 3, macOS 10.15, macCatalyst 13.1, watchOS 2, *) + @available(tvOS, unavailable) + public func dismissHeadingCalibrationDisplay() { + self._implementation.dismissHeadingCalibrationDisplay() + } + + /// The minimum angular change in degrees required to generate new heading events. + @available(iOS 3, macOS 10.15, macCatalyst 13.1, watchOS 2, *) + @available(tvOS, unavailable) + public var headingFilter: CLLocationDegrees { + get { self._implementation.headingFilter } + nonmutating set { self._implementation.headingFilter = newValue } + } + + /// The device orientation to use when computing heading values. + @available(iOS 4, macOS 10.15, macCatalyst 13.1, watchOS 2, *) + @available(tvOS, unavailable) + public var headingOrientation: CLDeviceOrientation { + get { self._implementation.headingOrientation } + nonmutating set { self._implementation.headingOrientation = newValue } + } + + #endif + + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) + + // MARK: Running the region-monitoring service + + /// Starts monitoring the specified region. + @available(iOS 5, macOS 10.8, macCatalyst 13.1, *) + @available(watchOS, unavailable) + @available(tvOS, unavailable) + public func startMonitoring(for region: CLRegion) { + self._implementation.startMonitoring(region) + } + + /// Stops monitoring the specified region. + @available(iOS 4, macOS 10.8, macCatalyst 13.1, *) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + public func stopMonitoring(for region: CLRegion) { + self._implementation.stopMonitoring(region) + } + + /// The set of shared regions monitored by all location-manager objects. + @available(iOS 4, macOS 10.8, macCatalyst 13.1, *) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + public var monitoredRegions: Set { + self._implementation.monitoredRegions + } + + /// The largest boundary distance that can be assigned to a region. + @available(iOS 4, macOS 10.8, macCatalyst 13.1, *) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + public var maximumRegionMonitoringDistance: CLLocationDistance { + self._implementation.maximumRegionMonitoringDistance + } + + // MARK: Performing beacon ranging + + /// Retrieves the state of a region asynchronously. + @available(iOS 7, macOS 10.10, macCatalyst 13.1, *) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + public func requestState(for region: CLRegion) { + self._implementation.requestState(region) + } + + /// Starts the delivery of notifications for the specified beacon constraints. + @available(iOS 13, macOS 10.15, macCatalyst 13.1, *) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + public func startRangingBeacons(satisfying constraint: CLBeaconIdentityConstraint) { + self._implementation.startRangingBeacons(constraint) + } + + /// Stops the delivery of notifications for the specified beacon constraints. + @available(iOS 13, macOS 10.15, macCatalyst 13.1, *) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + public func stopRangingBeacons(satisfying constraint: CLBeaconIdentityConstraint) { + self._implementation.stopRangingBeacons(constraint) + } + + /// The set of beacon constraints currently being tracked using ranging. + @available(iOS 13, macOS 10.15, macCatalyst 13.1, *) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + public var rangedBeaconConstraints: Set { + self._implementation.rangedBeaconConstraints + } + + #endif + + #if os(iOS) && !targetEnvironment(macCatalyst) + + // MARK: Monitoring location push notifications + + /// Starts monitoring for the delivery of Apple Push Notification service (APNs) location pushes, + /// and provides a device-specific token for sending pushes. + @available(iOS 15, *) + @available(macOS, unavailable) + @available(macCatalyst, unavailable) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + public func startMonitoringLocationPushes(completion: ((Data?, Error?) -> Void)?) { + self._implementation.startMonitoringLocationPushes(completion) + } + + /// Stops monitoring for Apple Push Notification service (APNs) location pushes. + @available(iOS 15, *) + @available(macOS, unavailable) + @available(macCatalyst, unavailable) + @available(tvOS, unavailable) + @available(watchOS, unavailable) + public func stopMonitoringLocationPushes() { + self._implementation.stopMonitoringLocationPushes() + } + + #endif + + // MARK: Getting recent location and heading data + + /// The most recently retrieved user location. + @available(iOS 2, macOS 10.6, macCatalyst 13.1, tvOS 9, watchOS 2, *) + public var location: CLLocation? { + self._implementation.location + } + + #if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) || os(watchOS) + + /// The most recently reported heading. + @available(iOS 4, macOS 10.15, macCatalyst 13.1, watchOS 2, *) + @available(tvOS, unavailable) + public var heading: CLHeading? { + self._implementation.heading + } + + #endif + + #if os(watchOS) + + // MARK: watchOS-specific methods + + @available(watchOS 9, *) + @available(iOS, unavailable) + @available(macOS, unavailable) + @available(macCatalyst, unavailable) + @available(tvOS, unavailable) + public func requestHistoricalLocations( + purposeKey: String, + sampleCount: Int, + completionHandler: @escaping ([CLLocation], Error?) -> Void + ) { + self._implementation.requestHistoricalLocations(purposeKey, sampleCount, completionHandler) + } + + #endif + } +#endif diff --git a/Sources/LocationManagerDependency/LocationManagerDependency_iOS.swift b/Sources/LocationManagerDependency/LocationManagerDependency_iOS.swift new file mode 100644 index 0000000..44686f5 --- /dev/null +++ b/Sources/LocationManagerDependency/LocationManagerDependency_iOS.swift @@ -0,0 +1,247 @@ +#if canImport(CoreLocation) && os(iOS) + @preconcurrency import CoreLocation + import Dependencies + @_spi(Internals) import DependenciesAdditionsBasics + import XCTestDynamicOverlay + + extension LocationManager { + static var system: Self { + let manager = CLLocationManager() + let _implementation = Implementation( + significantLocationChangeMonitoringAvailable: .init { + CLLocationManager.significantLocationChangeMonitoringAvailable() + }, + headingAvailable: .init { CLLocationManager.headingAvailable() }, + isAuthorizedForWidgetUpdates: .init { + if #available(iOS 14, *) { + return manager.isAuthorizedForWidgetUpdates + } else { + return false + } + }, + accuracyAuthorization: .init { + if #available(iOS 14, *) { + return manager.accuracyAuthorization + } else { + // Before introducing reduced accuracy, systems only authorized precise accuracy + return CLAccuracyAuthorization.fullAccuracy + } + }, + isMonitoringAvailable: .init { CLLocationManager.isMonitoringAvailable(for: $0) }, + isRangingAvailable: .init { CLLocationManager.isRangingAvailable() }, + locationServicesEnabled: .init { CLLocationManager.locationServicesEnabled() }, + delegate: .init( + .init( + get: { manager.delegate }, + set: { manager.delegate = $0 } + )), + requestWhenInUseAuthorization: .init { manager.requestWhenInUseAuthorization() }, + requestAlwaysAuthorization: .init { manager.requestAlwaysAuthorization() }, + requestTemporaryFullAccuracyAuthorization: .init { + if #available(iOS 14, *) { + return manager.requestTemporaryFullAccuracyAuthorization( + withPurposeKey: $0, completion: $1) + } else { + fatalError( + "'requestTemporaryFullAccuracyAuthorization(withPurposeKey:completion:)' is unavailable" + ) + } + }, + authorizationStatus: .init { + if #available(iOS 14, *) { + return manager.authorizationStatus + } else { + return CLLocationManager.authorizationStatus() + } + }, + distanceFilter: .init( + .init( + get: { manager.distanceFilter }, + set: { manager.distanceFilter = $0 } + )), + desiredAccuracy: .init( + .init( + get: { manager.desiredAccuracy }, + set: { manager.desiredAccuracy = $0 } + )), + startUpdatingLocation: .init { manager.startUpdatingLocation() }, + stopUpdatingLocation: .init { manager.stopUpdatingLocation() }, + requestLocation: .init { manager.requestLocation() }, + pausesLocationUpdatesAutomatically: .init( + .init( + get: { manager.pausesLocationUpdatesAutomatically }, + set: { manager.pausesLocationUpdatesAutomatically = $0 } + )), + allowsBackgroundLocationUpdates: .init( + .init( + get: { manager.allowsBackgroundLocationUpdates }, + set: { manager.allowsBackgroundLocationUpdates = $0 } + )), + showsBackgroundLocationIndicator: .init( + .init( + get: { manager.showsBackgroundLocationIndicator }, + set: { manager.showsBackgroundLocationIndicator = $0 } + )), + activityType: .init( + .init( + get: { manager.activityType }, + set: { manager.activityType = $0 } + )), + startMonitoringSignificantLocationChanges: .init { + manager.startMonitoringSignificantLocationChanges() + }, + stopMonitoringSignificantLocationChanges: .init { + manager.stopMonitoringSignificantLocationChanges() + }, + startMonitoringVisits: .init { manager.startMonitoringVisits() }, + stopMonitoringVisits: .init { manager.stopMonitoringVisits() }, + startUpdatingHeading: .init { manager.startUpdatingHeading() }, + stopUpdatingHeading: .init { manager.stopUpdatingHeading() }, + dismissHeadingCalibrationDisplay: .init { manager.dismissHeadingCalibrationDisplay() }, + headingFilter: .init( + .init( + get: { manager.headingFilter }, + set: { manager.headingFilter = $0 } + )), + headingOrientation: .init( + .init( + get: { manager.headingOrientation }, + set: { manager.headingOrientation = $0 } + )), + startMonitoring: .init { manager.startMonitoring(for: $0) }, + stopMonitoring: .init { manager.stopMonitoring(for: $0) }, + monitoredRegions: .init { manager.monitoredRegions }, + maximumRegionMonitoringDistance: .init { manager.maximumRegionMonitoringDistance }, + requestState: .init { manager.requestState(for: $0) }, + startRangingBeacons: .init { manager.startRangingBeacons(satisfying: $0) }, + stopRangingBeacons: .init { manager.stopRangingBeacons(satisfying: $0) }, + rangedBeaconConstraints: .init { manager.rangedBeaconConstraints }, + startMonitoringLocationPushes: .init { + #if !targetEnvironment(macCatalyst) + if #available(iOS 15.0, *) { + return manager.startMonitoringLocationPushes(completion: $0) + } else { + fatalError("'startMonitoringLocationPushes(completion:)' is unavailable") + } + #else + fatalError("'startMonitoringLocationPushes(completion:)' is unavailable") + #endif + }, + stopMonitoringLocationPushes: .init { () -> Void in + #if !targetEnvironment(macCatalyst) + if #available(iOS 15.0, *) { + return manager.stopMonitoringLocationPushes() + } else { + fatalError("'stopMonitoringLocationPushes()' is unavailable") + } + #else + fatalError("'stopMonitoringLocationPushes()' is unavailable") + #endif + }, + location: .init { manager.location }, + heading: .init { manager.heading } + ) + return LocationManager(_implementation: _implementation) + } + } + + extension LocationManager { + static var unimplemented: LocationManager { + let _implementation = Implementation( + significantLocationChangeMonitoringAvailable: .unimplemented( + #"@Dependency(\.locationManager.significantLocationChangeMonitoringAvailable)"#, + placeholder: false), + headingAvailable: .unimplemented( + #"@Dependency(\.locationManager.headingAvailable)"#, + placeholder: false), + isAuthorizedForWidgetUpdates: .unimplemented( + #"@Dependency(\.locationManager.isAuthorizedForWidgetUpdates)"#, + placeholder: false), + accuracyAuthorization: .unimplemented( + #"@Dependency(\.locationManager.accuracyAuthorization)"#, + placeholder: .reducedAccuracy), + isMonitoringAvailable: .unimplemented( + #"@Dependency(\.locationManager.isMonitoringAvailable)"#), + isRangingAvailable: .unimplemented( + #"@Dependency(\.locationManager.isRangingAvailable)"#, + placeholder: false), + locationServicesEnabled: .unimplemented( + #"@Dependency(\.locationManager.locationServicesEnabled)"#, + placeholder: false), + delegate: .unimplemented( + #"@Dependency(\.locationManager.delegate)"#), + requestWhenInUseAuthorization: .unimplemented( + #"@Dependency(\.locationManager.requestWhenInUseAuthorization)"#), + requestAlwaysAuthorization: .unimplemented( + #"@Dependency(\.locationManager.requestAlwaysAuthorization)"#), + requestTemporaryFullAccuracyAuthorization: .unimplemented( + #"@Dependency(\.locationManager.requestTemporaryFullAccuracyAuthorization)"#), + authorizationStatus: .unimplemented( + #"@Dependency(\.locationManager.authorizationStatus)"#, + placeholder: .denied), + distanceFilter: .unimplemented( + #"@Dependency(\.locationManager.distanceFilter)"#), + desiredAccuracy: .unimplemented( + #"@Dependency(\.locationManager.desiredAccuracy)"#), + startUpdatingLocation: .unimplemented( + #"@Dependency(\.locationManager.startUpdatingLocation)"#), + stopUpdatingLocation: .unimplemented( + #"@Dependency(\.locationManager.stopUpdatingLocation)"#), + requestLocation: .unimplemented( + #"@Dependency(\.locationManager.requestLocation)"#), + pausesLocationUpdatesAutomatically: .unimplemented( + #"@Dependency(\.locationManager.pausesLocationUpdatesAutomatically)"#), + allowsBackgroundLocationUpdates: .unimplemented( + #"@Dependency(\.locationManager.allowsBackgroundLocationUpdates)"#, + placeholder: false), + showsBackgroundLocationIndicator: .unimplemented( + #"@Dependency(\.locationManager.showsBackgroundLocationIndicator)"#), + activityType: .unimplemented( + #"@Dependency(\.locationManager.activityType)"#), + startMonitoringSignificantLocationChanges: .unimplemented( + #"@Dependency(\.locationManager.startMonitoringSignificantLocationChanges)"#), + stopMonitoringSignificantLocationChanges: .unimplemented( + #"@Dependency(\.locationManager.stopMonitoringSignificantLocationChanges)"#), + startMonitoringVisits: .unimplemented( + #"@Dependency(\.locationManager.startMonitoringVisits)"#), + stopMonitoringVisits: .unimplemented( + #"@Dependency(\.locationManager.stopMonitoringVisits)"#), + startUpdatingHeading: .unimplemented( + #"@Dependency(\.locationManager.startUpdatingHeading)"#), + stopUpdatingHeading: .unimplemented( + #"@Dependency(\.locationManager.stopUpdatingHeading)"#), + dismissHeadingCalibrationDisplay: .unimplemented( + #"@Dependency(\.locationManager.dismissHeadingCalibrationDisplay)"#), + headingFilter: .unimplemented( + #"@Dependency(\.locationManager.headingFilter)"#), + headingOrientation: .unimplemented( + #"@Dependency(\.locationManager.headingOrientation)"#), + startMonitoring: .unimplemented( + #"@Dependency(\.locationManager.startMonitoring)"#), + stopMonitoring: .unimplemented( + #"@Dependency(\.locationManager.stopMonitoring)"#), + monitoredRegions: .unimplemented( + #"@Dependency(\.locationManager.monitoredRegions)"#), + maximumRegionMonitoringDistance: .unimplemented( + #"@Dependency(\.locationManager.maximumRegionMonitoringDistance)"#), + requestState: .unimplemented( + #"@Dependency(\.locationManager.requestState)"#), + startRangingBeacons: .unimplemented( + #"@Dependency(\.locationManager.startRangingBeacons)"#), + stopRangingBeacons: .unimplemented( + #"@Dependency(\.locationManager.stopRangingBeacons)"#), + rangedBeaconConstraints: .unimplemented( + #"@Dependency(\.locationManager.rangedBeaconConstraints)"#), + startMonitoringLocationPushes: .unimplemented( + #"@Dependency(\.locationManager.startMonitoringLocationPushes)"#), + stopMonitoringLocationPushes: .unimplemented( + #"@Dependency(\.locationManager.stopMonitoringLocationPushes)"#), + location: .unimplemented( + #"@Dependency(\.locationManager.location)"#), + heading: .unimplemented( + #"@Dependency(\.locationManager.heading)"#) + ) + return LocationManager(_implementation: _implementation) + } + } +#endif diff --git a/Sources/LocationManagerDependency/LocationManagerDependency_macOS.swift b/Sources/LocationManagerDependency/LocationManagerDependency_macOS.swift new file mode 100644 index 0000000..2983edd --- /dev/null +++ b/Sources/LocationManagerDependency/LocationManagerDependency_macOS.swift @@ -0,0 +1,211 @@ +#if canImport(CoreLocation) && os(macOS) + @preconcurrency import CoreLocation + import Dependencies + @_spi(Internals) import DependenciesAdditionsBasics + import XCTestDynamicOverlay + + extension LocationManager { + static var system: Self { + let manager = CLLocationManager() + let _implementation = Implementation( + significantLocationChangeMonitoringAvailable: .init { + CLLocationManager.significantLocationChangeMonitoringAvailable() + }, + headingAvailable: .init { CLLocationManager.headingAvailable() }, + isAuthorizedForWidgetUpdates: .init { + if #available(macOS 11, *) { + return manager.isAuthorizedForWidgetUpdates + } else { + return false + } + }, + accuracyAuthorization: .init { + if #available(macOS 11, *) { + return manager.accuracyAuthorization + } else { + // Before introducing reduced accuracy, systems only authorized precise accuracy + return CLAccuracyAuthorization.fullAccuracy + } + }, + isMonitoringAvailable: .init { CLLocationManager.isMonitoringAvailable(for: $0) }, + isRangingAvailable: .init { CLLocationManager.isRangingAvailable() }, + locationServicesEnabled: .init { CLLocationManager.locationServicesEnabled() }, + delegate: .init( + .init( + get: { manager.delegate }, + set: { manager.delegate = $0 } + )), + requestWhenInUseAuthorization: .init { manager.requestWhenInUseAuthorization() }, + requestAlwaysAuthorization: .init { manager.requestAlwaysAuthorization() }, + requestTemporaryFullAccuracyAuthorization: .init { + if #available(macOS 11, *) { + return manager.requestTemporaryFullAccuracyAuthorization( + withPurposeKey: $0, completion: $1) + } else { + fatalError( + "'requestTemporaryFullAccuracyAuthorization(withPurposeKey:completion:)' is unavailable" + ) + } + }, + authorizationStatus: .init { + if #available(macOS 11, *) { + return manager.authorizationStatus + } else { + return CLLocationManager.authorizationStatus() + } + }, + distanceFilter: .init( + .init( + get: { manager.distanceFilter }, + set: { manager.distanceFilter = $0 } + )), + desiredAccuracy: .init( + .init( + get: { manager.desiredAccuracy }, + set: { manager.desiredAccuracy = $0 } + )), + startUpdatingLocation: .init { manager.startUpdatingLocation() }, + stopUpdatingLocation: .init { manager.stopUpdatingLocation() }, + requestLocation: .init { manager.requestLocation() }, + pausesLocationUpdatesAutomatically: .init( + .init( + get: { manager.pausesLocationUpdatesAutomatically }, + set: { manager.pausesLocationUpdatesAutomatically = $0 } + )), + allowsBackgroundLocationUpdates: .init( + .init( + get: { manager.allowsBackgroundLocationUpdates }, + set: { manager.allowsBackgroundLocationUpdates = $0 } + )), + activityType: .init( + .init( + get: { manager.activityType }, + set: { manager.activityType = $0 } + )), + startMonitoringSignificantLocationChanges: .init { + manager.startMonitoringSignificantLocationChanges() + }, + stopMonitoringSignificantLocationChanges: .init { + manager.stopMonitoringSignificantLocationChanges() + }, + startMonitoringVisits: .init { manager.startMonitoringVisits() }, + stopMonitoringVisits: .init { manager.stopMonitoringVisits() }, + startUpdatingHeading: .init { manager.startUpdatingHeading() }, + dismissHeadingCalibrationDisplay: .init { manager.dismissHeadingCalibrationDisplay() }, + headingFilter: .init( + .init( + get: { manager.headingFilter }, + set: { manager.headingFilter = $0 } + )), + headingOrientation: .init( + .init( + get: { manager.headingOrientation }, + set: { manager.headingOrientation = $0 } + )), + startMonitoring: .init { manager.startMonitoring(for: $0) }, + stopMonitoring: .init { manager.stopMonitoring(for: $0) }, + monitoredRegions: .init { manager.monitoredRegions }, + maximumRegionMonitoringDistance: .init { manager.maximumRegionMonitoringDistance }, + requestState: .init { manager.requestState(for: $0) }, + startRangingBeacons: .init { manager.startRangingBeacons(satisfying: $0) }, + stopRangingBeacons: .init { manager.stopRangingBeacons(satisfying: $0) }, + rangedBeaconConstraints: .init { manager.rangedBeaconConstraints }, + location: .init { manager.location }, + heading: .init { manager.heading } + ) + return LocationManager(_implementation: _implementation) + } + } + + extension LocationManager { + static var unimplemented: LocationManager { + let _implementation = Implementation( + significantLocationChangeMonitoringAvailable: .unimplemented( + #"@Dependency(\.locationManager.significantLocationChangeMonitoringAvailable)"#, + placeholder: false), + headingAvailable: .unimplemented( + #"@Dependency(\.locationManager.headingAvailable)"#, + placeholder: false), + isAuthorizedForWidgetUpdates: .unimplemented( + #"@Dependency(\.locationManager.isAuthorizedForWidgetUpdates)"#, + placeholder: false), + accuracyAuthorization: .unimplemented( + #"@Dependency(\.locationManager.accuracyAuthorization)"#, + placeholder: .reducedAccuracy), + isMonitoringAvailable: .unimplemented( + #"@Dependency(\.locationManager.isMonitoringAvailable)"#), + isRangingAvailable: .unimplemented( + #"@Dependency(\.locationManager.isRangingAvailable)"#, + placeholder: false), + locationServicesEnabled: .unimplemented( + #"@Dependency(\.locationManager.locationServicesEnabled)"#, + placeholder: false), + delegate: .unimplemented( + #"@Dependency(\.locationManager.delegate)"#), + requestWhenInUseAuthorization: .unimplemented( + #"@Dependency(\.locationManager.requestWhenInUseAuthorization)"#), + requestAlwaysAuthorization: .unimplemented( + #"@Dependency(\.locationManager.requestAlwaysAuthorization)"#), + requestTemporaryFullAccuracyAuthorization: .unimplemented( + #"@Dependency(\.locationManager.requestTemporaryFullAccuracyAuthorization)"#), + authorizationStatus: .unimplemented( + #"@Dependency(\.locationManager.authorizationStatus)"#, + placeholder: .denied), + distanceFilter: .unimplemented( + #"@Dependency(\.locationManager.distanceFilter)"#), + desiredAccuracy: .unimplemented( + #"@Dependency(\.locationManager.desiredAccuracy)"#), + startUpdatingLocation: .unimplemented( + #"@Dependency(\.locationManager.startUpdatingLocation)"#), + stopUpdatingLocation: .unimplemented( + #"@Dependency(\.locationManager.stopUpdatingLocation)"#), + requestLocation: .unimplemented( + #"@Dependency(\.locationManager.requestLocation)"#), + pausesLocationUpdatesAutomatically: .unimplemented( + #"@Dependency(\.locationManager.pausesLocationUpdatesAutomatically)"#), + allowsBackgroundLocationUpdates: .unimplemented( + #"@Dependency(\.locationManager.allowsBackgroundLocationUpdates)"#, + placeholder: false), + activityType: .unimplemented( + #"@Dependency(\.locationManager.activityType)"#), + startMonitoringSignificantLocationChanges: .unimplemented( + #"@Dependency(\.locationManager.startMonitoringSignificantLocationChanges)"#), + stopMonitoringSignificantLocationChanges: .unimplemented( + #"@Dependency(\.locationManager.stopMonitoringSignificantLocationChanges)"#), + startMonitoringVisits: .unimplemented( + #"@Dependency(\.locationManager.startMonitoringVisits)"#), + stopMonitoringVisits: .unimplemented( + #"@Dependency(\.locationManager.stopMonitoringVisits)"#), + startUpdatingHeading: .unimplemented( + #"@Dependency(\.locationManager.startUpdatingHeading)"#), + dismissHeadingCalibrationDisplay: .unimplemented( + #"@Dependency(\.locationManager.dismissHeadingCalibrationDisplay)"#), + headingFilter: .unimplemented( + #"@Dependency(\.locationManager.headingFilter)"#), + headingOrientation: .unimplemented( + #"@Dependency(\.locationManager.headingOrientation)"#), + startMonitoring: .unimplemented( + #"@Dependency(\.locationManager.startMonitoring)"#), + stopMonitoring: .unimplemented( + #"@Dependency(\.locationManager.stopMonitoring)"#), + monitoredRegions: .unimplemented( + #"@Dependency(\.locationManager.monitoredRegions)"#), + maximumRegionMonitoringDistance: .unimplemented( + #"@Dependency(\.locationManager.maximumRegionMonitoringDistance)"#), + requestState: .unimplemented( + #"@Dependency(\.locationManager.requestState)"#), + startRangingBeacons: .unimplemented( + #"@Dependency(\.locationManager.startRangingBeacons)"#), + stopRangingBeacons: .unimplemented( + #"@Dependency(\.locationManager.stopRangingBeacons)"#), + rangedBeaconConstraints: .unimplemented( + #"@Dependency(\.locationManager.rangedBeaconConstraints)"#), + location: .unimplemented( + #"@Dependency(\.locationManager.location)"#), + heading: .unimplemented( + #"@Dependency(\.locationManager.heading)"#) + ) + return LocationManager(_implementation: _implementation) + } + } +#endif diff --git a/Sources/LocationManagerDependency/LocationManagerDependency_tvOS.swift b/Sources/LocationManagerDependency/LocationManagerDependency_tvOS.swift new file mode 100644 index 0000000..eb38e4b --- /dev/null +++ b/Sources/LocationManagerDependency/LocationManagerDependency_tvOS.swift @@ -0,0 +1,93 @@ +#if canImport(CoreLocation) && os(tvOS) + @preconcurrency import CoreLocation + import Dependencies + @_spi(Internals) import DependenciesAdditionsBasics + import XCTestDynamicOverlay + + extension LocationManager { + static var system: Self { + let manager = CLLocationManager() + let _implementation = Implementation( + accuracyAuthorization: .init { + if #available(tvOS 14, *) { + return manager.accuracyAuthorization + } else { + // Before introducing reduced accuracy, systems only authorized precise accuracy + return CLAccuracyAuthorization.fullAccuracy + } + }, + locationServicesEnabled: .init { CLLocationManager.locationServicesEnabled() }, + delegate: .init( + .init( + get: { manager.delegate }, + set: { manager.delegate = $0 } + )), + requestWhenInUseAuthorization: .init { manager.requestWhenInUseAuthorization() }, + requestTemporaryFullAccuracyAuthorization: .init { + if #available(tvOS 14, *) { + return manager.requestTemporaryFullAccuracyAuthorization( + withPurposeKey: $0, completion: $1) + } else { + fatalError( + "'requestTemporaryFullAccuracyAuthorization(withPurposeKey:completion:)' is unavailable" + ) + } + }, + authorizationStatus: .init { + if #available(tvOS 14, *) { + return manager.authorizationStatus + } else { + return CLLocationManager.authorizationStatus() + } + }, + distanceFilter: .init( + .init( + get: { manager.distanceFilter }, + set: { manager.distanceFilter = $0 } + )), + desiredAccuracy: .init( + .init( + get: { manager.desiredAccuracy }, + set: { manager.desiredAccuracy = $0 } + )), + stopUpdatingLocation: .init { manager.stopUpdatingLocation() }, + requestLocation: .init { manager.requestLocation() }, + location: .init { manager.location } + ) + return LocationManager(_implementation: _implementation) + } + } + + extension LocationManager { + static var unimplemented: LocationManager { + let _implementation = Implementation( + accuracyAuthorization: .unimplemented( + #"@Dependency(\.locationManager.accuracyAuthorization)"#, + placeholder: .reducedAccuracy), + locationServicesEnabled: .unimplemented( + #"@Dependency(\.locationManager.locationServicesEnabled)"#, + placeholder: false), + delegate: .unimplemented( + #"@Dependency(\.locationManager.delegate)"#), + requestWhenInUseAuthorization: .unimplemented( + #"@Dependency(\.locationManager.requestWhenInUseAuthorization)"#), + requestTemporaryFullAccuracyAuthorization: .unimplemented( + #"@Dependency(\.locationManager.requestTemporaryFullAccuracyAuthorization)"#), + authorizationStatus: .unimplemented( + #"@Dependency(\.locationManager.authorizationStatus)"#, + placeholder: .denied), + distanceFilter: .unimplemented( + #"@Dependency(\.locationManager.distanceFilter)"#), + desiredAccuracy: .unimplemented( + #"@Dependency(\.locationManager.desiredAccuracy)"#), + stopUpdatingLocation: .unimplemented( + #"@Dependency(\.locationManager.stopUpdatingLocation)"#), + requestLocation: .unimplemented( + #"@Dependency(\.locationManager.requestLocation)"#), + location: .unimplemented( + #"@Dependency(\.locationManager.location)"#) + ) + return LocationManager(_implementation: _implementation) + } + } +#endif diff --git a/Sources/LocationManagerDependency/LocationManagerDependency_watchOS.swift b/Sources/LocationManagerDependency/LocationManagerDependency_watchOS.swift new file mode 100644 index 0000000..9ff367e --- /dev/null +++ b/Sources/LocationManagerDependency/LocationManagerDependency_watchOS.swift @@ -0,0 +1,155 @@ +#if canImport(CoreLocation) && os(watchOS) + @preconcurrency import CoreLocation + import Dependencies + @_spi(Internals) import DependenciesAdditionsBasics + import XCTestDynamicOverlay + + extension LocationManager { + static var system: Self { + let manager = CLLocationManager() + let _implementation = Implementation( + headingAvailable: .init { CLLocationManager.headingAvailable() }, + accuracyAuthorization: .init { + if #available(watchOS 7, *) { + return manager.accuracyAuthorization + } else { + // Before introducing reduced accuracy, systems only authorized precise accuracy + return CLAccuracyAuthorization.fullAccuracy + } + }, + locationServicesEnabled: .init { CLLocationManager.locationServicesEnabled() }, + delegate: .init( + .init( + get: { manager.delegate }, + set: { manager.delegate = $0 } + )), + requestWhenInUseAuthorization: .init { manager.requestWhenInUseAuthorization() }, + requestAlwaysAuthorization: .init { manager.requestAlwaysAuthorization() }, + requestTemporaryFullAccuracyAuthorization: .init { + if #available(watchOS 7, *) { + manager.requestTemporaryFullAccuracyAuthorization(withPurposeKey: $0, completion: $1) + } else { + fatalError( + "'requestTemporaryFullAccuracyAuthorization(withPurposeKey:completion:)' is unavailable" + ) + } + }, + authorizationStatus: .init { + if #available(watchOS 7, *) { + return manager.authorizationStatus + } else { + return CLLocationManager.authorizationStatus() + } + }, + distanceFilter: .init( + .init( + get: { manager.distanceFilter }, + set: { manager.distanceFilter = $0 } + )), + desiredAccuracy: .init( + .init( + get: { manager.desiredAccuracy }, + set: { manager.desiredAccuracy = $0 } + )), + startUpdatingLocation: .init { manager.startUpdatingLocation() }, + stopUpdatingLocation: .init { manager.stopUpdatingLocation() }, + requestLocation: .init { manager.requestLocation() }, + allowsBackgroundLocationUpdates: .init( + .init( + get: { manager.allowsBackgroundLocationUpdates }, + set: { manager.allowsBackgroundLocationUpdates = $0 } + )), + activityType: .init( + .init( + get: { manager.activityType }, + set: { manager.activityType = $0 } + )), + startUpdatingHeading: .init { manager.startUpdatingHeading() }, + stopUpdatingHeading: .init { manager.stopUpdatingHeading() }, + dismissHeadingCalibrationDisplay: .init { manager.dismissHeadingCalibrationDisplay() }, + headingFilter: .init( + .init( + get: { manager.headingFilter }, + set: { manager.headingFilter = $0 } + )), + headingOrientation: .init( + .init( + get: { manager.headingOrientation }, + set: { manager.headingOrientation = $0 } + )), + location: .init { manager.location }, + heading: .init { manager.heading }, + requestHistoricalLocations: .init { + if #available(watchOS 9.0, *) { + manager.requestHistoricalLocations( + purposeKey: $0, sampleCount: $1, completionHandler: $2) + } else { + fatalError( + "'requestHistoricalLocations(purposeKey:sampleCount:completionHandler:)' is unavailable" + ) + } + } + ) + return LocationManager(_implementation: _implementation) + } + } + + extension LocationManager { + static var unimplemented: LocationManager { + let _implementation = Implementation( + headingAvailable: .unimplemented( + #"@Dependency(\.locationManager.headingAvailable)"#, + placeholder: false), + accuracyAuthorization: .unimplemented( + #"@Dependency(\.locationManager.accuracyAuthorization)"#, + placeholder: .reducedAccuracy), + locationServicesEnabled: .unimplemented( + #"@Dependency(\.locationManager.locationServicesEnabled)"#, + placeholder: false), + delegate: .unimplemented( + #"@Dependency(\.locationManager.delegate)"#), + requestWhenInUseAuthorization: .unimplemented( + #"@Dependency(\.locationManager.requestWhenInUseAuthorization)"#), + requestAlwaysAuthorization: .unimplemented( + #"@Dependency(\.locationManager.requestAlwaysAuthorization)"#), + requestTemporaryFullAccuracyAuthorization: .unimplemented( + #"@Dependency(\.locationManager.requestTemporaryFullAccuracyAuthorization)"#), + authorizationStatus: .unimplemented( + #"@Dependency(\.locationManager.authorizationStatus)"#, + placeholder: .denied), + distanceFilter: .unimplemented( + #"@Dependency(\.locationManager.distanceFilter)"#), + desiredAccuracy: .unimplemented( + #"@Dependency(\.locationManager.desiredAccuracy)"#), + startUpdatingLocation: .unimplemented( + #"@Dependency(\.locationManager.startUpdatingLocation)"#), + stopUpdatingLocation: .unimplemented( + #"@Dependency(\.locationManager.stopUpdatingLocation)"#), + requestLocation: .unimplemented( + #"@Dependency(\.locationManager.requestLocation)"#), + allowsBackgroundLocationUpdates: .unimplemented( + #"@Dependency(\.locationManager.allowsBackgroundLocationUpdates)"#, + placeholder: false), + activityType: .unimplemented( + #"@Dependency(\.locationManager.activityType)"#), + startUpdatingHeading: .unimplemented( + #"@Dependency(\.locationManager.startUpdatingHeading)"#), + stopUpdatingHeading: .unimplemented( + #"@Dependency(\.locationManager.stopUpdatingHeading)"#), + dismissHeadingCalibrationDisplay: .unimplemented( + #"@Dependency(\.locationManager.dismissHeadingCalibrationDisplay)"#), + headingFilter: .unimplemented( + #"@Dependency(\.locationManager.headingFilter)"#), + headingOrientation: .unimplemented( + #"@Dependency(\.locationManager.headingOrientation)"#), + location: .unimplemented( + #"@Dependency(\.locationManager.location)"#), + heading: .unimplemented( + #"@Dependency(\.locationManager.heading)"#), + requestHistoricalLocations: .unimplemented( + #"@Dependency(\.locationManager.requestHistoricalLocations)"#) + ) + return LocationManager(_implementation: _implementation) + } + } +#endif