Files
swift-composable-architectu…/Sources/ComposableCoreLocation/Live.swift
2020-05-20 18:13:55 +00:00

368 lines
13 KiB
Swift

import Combine
import ComposableArchitecture
import CoreLocation
extension LocationManager {
/// The live implementation of the `LocationManager` interface. This implementation is capable of
/// creating real `CLLocationManager` instances, listening to its delegate methods, and invoking
/// its methods. You will typically use this when building for the simulator or device:
///
/// let store = Store(
/// initialState: AppState(),
/// reducer: appReducer,
/// environment: AppEnvironment(
/// locationManager: LocationManager.live
/// )
/// )
///
public static let live: LocationManager = { () -> LocationManager in
var manager = LocationManager()
manager.authorizationStatus = CLLocationManager.authorizationStatus
manager.create = { id in
Effect.run { callback in
let manager = CLLocationManager()
var delegate = LocationManagerDelegate()
delegate.didChangeAuthorization = {
callback.send(.didChangeAuthorization($0))
}
#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst)
delegate.didDetermineStateForRegion = { state, region in
callback.send(.didDetermineState(state, region: Region(rawValue: region)))
}
#endif
#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst)
delegate.didEnterRegion = { region in
callback.send(.didEnterRegion(Region(rawValue: region)))
}
#endif
#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst)
delegate.didExitRegion = { region in
callback.send(.didExitRegion(Region(rawValue: region)))
}
#endif
#if os(iOS) || targetEnvironment(macCatalyst)
delegate.didFailRangingForConstraintWithError = { constraint, error in
callback.send(.didFailRanging(beaconConstraint: constraint, error: Error(error)))
}
#endif
delegate.didFailWithError = { error in
callback.send(.didFailWithError(Error(error)))
}
#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst)
delegate.didFinishDeferredUpdatesWithError = { error in
callback.send(.didFinishDeferredUpdatesWithError(error.map(Error.init)))
}
#endif
#if os(iOS) || targetEnvironment(macCatalyst)
delegate.didPauseLocationUpdates = {
callback.send(.didPauseLocationUpdates)
}
#endif
#if os(iOS) || targetEnvironment(macCatalyst)
delegate.didResumeLocationUpdates = {
callback.send(.didResumeLocationUpdates)
}
#endif
#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst)
delegate.didStartMonitoringForRegion = { region in
callback.send(.didStartMonitoring(region: Region(rawValue: region)))
}
#endif
#if os(iOS) || os(watchOS) || targetEnvironment(macCatalyst)
delegate.didUpdateHeading = { heading in
callback.send(.didUpdateHeading(newHeading: Heading(rawValue: heading)))
}
#endif
#if os(macOS)
delegate.didUpdateToLocationFromLocation = { newLocation, oldLocation in
callback.send(
.didUpdateTo(
newLocation: Location(rawValue: newLocation),
oldLocation: Location(rawValue: oldLocation)
)
)
}
#endif
delegate.didUpdateLocations = {
callback.send(.didUpdateLocations($0.map(Location.init(rawValue:))))
}
#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst)
delegate.monitoringDidFailForRegionWithError = { region, error in
callback.send(
.monitoringDidFail(region: region.map(Region.init(rawValue:)), error: Error(error)))
}
#endif
#if os(iOS) || targetEnvironment(macCatalyst)
delegate.didVisit = { visit in
callback.send(.didVisit(Visit(visit: visit)))
}
#endif
manager.delegate = delegate
dependencies[id] = Dependencies(
locationManager: manager,
locationManagerDelegate: delegate
)
return AnyCancellable {
dependencies[id] = nil
}
}
}
manager.destroy = { id in
.fireAndForget {
dependencies[id] = nil
}
}
manager.locationServicesEnabled = CLLocationManager.locationServicesEnabled
manager.requestLocation = { id in
.fireAndForget { dependencies[id]?.locationManager.requestLocation() }
}
#if os(iOS) || os(macOS) || os(watchOS) || targetEnvironment(macCatalyst)
manager.requestAlwaysAuthorization = { id in
.fireAndForget { dependencies[id]?.locationManager.requestAlwaysAuthorization() }
}
#endif
#if os(iOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst)
manager.requestWhenInUseAuthorization = { id in
.fireAndForget { dependencies[id]?.locationManager.requestWhenInUseAuthorization() }
}
#endif
manager.set = { id, properties in
.fireAndForget {
guard let manager = dependencies[id]?.locationManager else { return }
#if os(iOS) || os(watchOS) || targetEnvironment(macCatalyst)
if let activityType = properties.activityType {
manager.activityType = activityType
}
if let allowsBackgroundLocationUpdates = properties.allowsBackgroundLocationUpdates {
manager.allowsBackgroundLocationUpdates = allowsBackgroundLocationUpdates
}
#endif
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) || targetEnvironment(macCatalyst)
if let desiredAccuracy = properties.desiredAccuracy {
manager.desiredAccuracy = desiredAccuracy
}
if let distanceFilter = properties.distanceFilter {
manager.distanceFilter = distanceFilter
}
#endif
#if os(iOS) || os(watchOS) || targetEnvironment(macCatalyst)
if let headingFilter = properties.headingFilter {
manager.headingFilter = headingFilter
}
if let headingOrientation = properties.headingOrientation {
manager.headingOrientation = headingOrientation
}
#endif
#if os(iOS) || targetEnvironment(macCatalyst)
if let pausesLocationUpdatesAutomatically = properties.pausesLocationUpdatesAutomatically
{
manager.pausesLocationUpdatesAutomatically = pausesLocationUpdatesAutomatically
}
if let showsBackgroundLocationIndicator = properties.showsBackgroundLocationIndicator {
manager.showsBackgroundLocationIndicator = showsBackgroundLocationIndicator
}
#endif
}
}
#if os(iOS) || targetEnvironment(macCatalyst)
manager.startMonitoringVisits = { id in
.fireAndForget { dependencies[id]?.locationManager.startMonitoringVisits() }
}
#endif
#if os(iOS) || os(macOS) || os(watchOS) || targetEnvironment(macCatalyst)
manager.startUpdatingLocation = { id in
.fireAndForget { dependencies[id]?.locationManager.startUpdatingLocation() }
}
#endif
#if os(iOS) || targetEnvironment(macCatalyst)
manager.stopMonitoringVisits = { id in
.fireAndForget { dependencies[id]?.locationManager.stopMonitoringVisits() }
}
#endif
manager.stopUpdatingLocation = { id in
.fireAndForget { dependencies[id]?.locationManager.stopUpdatingLocation() }
}
return manager
}()
}
private struct Dependencies {
let locationManager: CLLocationManager
let locationManagerDelegate: LocationManagerDelegate
}
private var dependencies: [AnyHashable: Dependencies] = [:]
private class LocationManagerDelegate: NSObject, CLLocationManagerDelegate {
var didChangeAuthorization: (CLAuthorizationStatus) -> Void = { _ in fatalError() }
#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst)
var didDetermineStateForRegion: (CLRegionState, CLRegion) -> Void = { _, _ in fatalError() }
#endif
#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst)
var didEnterRegion: (CLRegion) -> Void = { _ in fatalError() }
#endif
#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst)
var didExitRegion: (CLRegion) -> Void = { _ in fatalError() }
#endif
#if os(iOS) || targetEnvironment(macCatalyst)
var didFailRangingForConstraintWithError: (CLBeaconIdentityConstraint, Error) -> Void = {
_, _ in fatalError()
}
#endif
var didFailWithError: (Error) -> Void = { _ in fatalError() }
#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst)
var didFinishDeferredUpdatesWithError: (Error?) -> Void = { _ in fatalError() }
#endif
#if os(iOS) || targetEnvironment(macCatalyst)
var didPauseLocationUpdates: () -> Void = { fatalError() }
#endif
#if os(iOS) || targetEnvironment(macCatalyst)
var didRangeBeaconsSatisfyingConstraint: ([CLBeacon], CLBeaconIdentityConstraint) -> Void = {
_, _ in fatalError()
}
#endif
#if os(iOS) || targetEnvironment(macCatalyst)
var didResumeLocationUpdates: () -> Void = { fatalError() }
#endif
#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst)
var didStartMonitoringForRegion: (CLRegion) -> Void = { _ in fatalError() }
#endif
#if os(iOS) || os(watchOS) || targetEnvironment(macCatalyst)
var didUpdateHeading: (CLHeading) -> Void = { _ in fatalError() }
#endif
var didUpdateLocations: ([CLLocation]) -> Void = { _ in fatalError() }
#if os(macOS)
var didUpdateToLocationFromLocation: (CLLocation, CLLocation) -> Void = { _, _ in fatalError() }
#endif
#if os(iOS) || targetEnvironment(macCatalyst)
var didVisit: (CLVisit) -> Void = { _ in fatalError() }
#endif
#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst)
var monitoringDidFailForRegionWithError: (CLRegion?, Error) -> Void = { _, _ in fatalError() }
#endif
func locationManager(
_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus
) {
self.didChangeAuthorization(status)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
self.didFailWithError(error)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
self.didUpdateLocations(locations)
}
#if os(macOS)
func locationManager(
_ manager: CLLocationManager, didUpdateTo newLocation: CLLocation,
from oldLocation: CLLocation
) {
self.didUpdateToLocationFromLocation(newLocation, oldLocation)
}
#endif
#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst)
func locationManager(
_ manager: CLLocationManager, didFinishDeferredUpdatesWithError error: Error?
) {
self.didFinishDeferredUpdatesWithError(error)
}
#endif
#if os(iOS) || targetEnvironment(macCatalyst)
func locationManagerDidPauseLocationUpdates(_ manager: CLLocationManager) {
self.didPauseLocationUpdates()
}
#endif
#if os(iOS) || targetEnvironment(macCatalyst)
func locationManagerDidResumeLocationUpdates(_ manager: CLLocationManager) {
self.didResumeLocationUpdates()
}
#endif
#if os(iOS) || os(watchOS) || targetEnvironment(macCatalyst)
func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) {
self.didUpdateHeading(newHeading)
}
#endif
#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst)
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
self.didEnterRegion(region)
}
#endif
#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst)
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
self.didExitRegion(region)
}
#endif
#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst)
func locationManager(
_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion
) {
self.didDetermineStateForRegion(state, region)
}
#endif
#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst)
func locationManager(
_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error
) {
self.monitoringDidFailForRegionWithError(region, error)
}
#endif
#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst)
func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion) {
self.didStartMonitoringForRegion(region)
}
#endif
#if os(iOS) || targetEnvironment(macCatalyst)
func locationManager(
_ manager: CLLocationManager, didRange beacons: [CLBeacon],
satisfying beaconConstraint: CLBeaconIdentityConstraint
) {
self.didRangeBeaconsSatisfyingConstraint(beacons, beaconConstraint)
}
#endif
#if os(iOS) || targetEnvironment(macCatalyst)
func locationManager(
_ manager: CLLocationManager, didFailRangingFor beaconConstraint: CLBeaconIdentityConstraint,
error: Error
) {
self.didFailRangingForConstraintWithError(beaconConstraint, error)
}
#endif
#if os(iOS) || targetEnvironment(macCatalyst)
func locationManager(_ manager: CLLocationManager, didVisit visit: CLVisit) {
self.didVisit(visit)
}
#endif
}