Skip to content

Commit fad9ad0

Browse files
authored
Merge pull request #14 from ra1028/refactor/example_map
refactor: Map app example
2 parents 669f438 + 608b825 commit fad9ad0

File tree

3 files changed

+88
-36
lines changed

3 files changed

+88
-36
lines changed
Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,28 @@
11
import Atoms
22
import CoreLocation
33

4-
struct LocationManagerAtom: ValueAtom, Hashable {
5-
func value(context: Context) -> LocationManagerProtocol {
6-
let manager = CLLocationManager()
7-
let delegate = LocationManagerDelegate()
8-
9-
manager.delegate = delegate
10-
manager.desiredAccuracy = kCLLocationAccuracyBest
11-
context.addTermination(manager.stopUpdatingLocation)
12-
context.keepUntilTermination(delegate)
13-
delegate.onChange = {
14-
context.reset(AuthorizationStatusAtom())
15-
}
4+
final class LocationObserver: NSObject, ObservableObject, CLLocationManagerDelegate {
5+
let manager: LocationManagerProtocol
166

17-
return manager
7+
deinit {
8+
manager.stopUpdatingLocation()
189
}
19-
}
2010

21-
struct CoordinateAtom: ValueAtom, Hashable {
22-
func value(context: Context) -> CLLocationCoordinate2D? {
23-
let manager = context.watch(LocationManagerAtom())
24-
return manager.location?.coordinate
11+
init(manager: LocationManagerProtocol) {
12+
self.manager = manager
13+
super.init()
14+
manager.delegate = self
2515
}
26-
}
2716

28-
struct AuthorizationStatusAtom: ValueAtom, Hashable {
29-
func value(context: Context) -> CLAuthorizationStatus {
30-
let manager = context.watch(LocationManagerAtom())
31-
return manager.authorizationStatus
32-
}
33-
}
17+
convenience override init() {
18+
let manager = CLLocationManager()
19+
manager.desiredAccuracy = kCLLocationAccuracyBest
3420

35-
private final class LocationManagerDelegate: NSObject, CLLocationManagerDelegate {
36-
var onChange: (() -> Void)?
21+
self.init(manager: manager)
22+
}
3723

3824
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
39-
onChange?()
25+
objectWillChange.send()
4026

4127
switch manager.authorizationStatus {
4228
case .authorizedAlways, .authorizedWhenInUse:
@@ -57,3 +43,22 @@ private final class LocationManagerDelegate: NSObject, CLLocationManagerDelegate
5743
print(error.localizedDescription)
5844
}
5945
}
46+
47+
struct LocationObserverAtom: ObservableObjectAtom, Hashable {
48+
func object(context: Context) -> LocationObserver {
49+
LocationObserver()
50+
}
51+
}
52+
53+
struct CoordinateAtom: ValueAtom, Hashable {
54+
func value(context: Context) -> CLLocationCoordinate2D? {
55+
let observer = context.watch(LocationObserverAtom())
56+
return observer.manager.location?.coordinate
57+
}
58+
}
59+
60+
struct AuthorizationStatusAtom: ValueAtom, Hashable {
61+
func value(context: Context) -> CLAuthorizationStatus {
62+
context.watch(LocationObserverAtom().select(\.manager.authorizationStatus))
63+
}
64+
}

Examples/Packages/iOS/Sources/ExampleMap/Dependency/LocationManager.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import CoreLocation
22

3-
protocol LocationManagerProtocol {
3+
protocol LocationManagerProtocol: AnyObject {
44
var delegate: CLLocationManagerDelegate? { get set }
55
var desiredAccuracy: CLLocationAccuracy { get set }
66
var location: CLLocation? { get }
77
var authorizationStatus: CLAuthorizationStatus { get }
8+
9+
func stopUpdatingLocation()
810
}
911

1012
extension CLLocationManager: LocationManagerProtocol {}
@@ -14,4 +16,9 @@ final class MockLocationManager: LocationManagerProtocol {
1416
var desiredAccuracy = kCLLocationAccuracyKilometer
1517
var location: CLLocation? = nil
1618
var authorizationStatus = CLAuthorizationStatus.notDetermined
19+
var isUpdatingLocation = true
20+
21+
func stopUpdatingLocation() {
22+
isUpdatingLocation = false
23+
}
1724
}

Examples/Packages/iOS/Tests/ExampleMapTests/ExampleMapTests.swift

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,68 @@ import XCTest
66

77
@MainActor
88
final class ExampleMapTests: XCTestCase {
9+
func testLocationObserverAtom() {
10+
let atom = LocationObserverAtom()
11+
let context = AtomTestContext()
12+
let manager = MockLocationManager()
13+
14+
context.override(atom) { _ in
15+
LocationObserver(manager: manager)
16+
}
17+
18+
context.watch(atom)
19+
20+
XCTAssertNotNil(manager.delegate)
21+
XCTAssertTrue(manager.isUpdatingLocation)
22+
23+
context.unwatch(atom)
24+
25+
XCTAssertFalse(manager.isUpdatingLocation)
26+
}
27+
928
func testCoordinateAtom() {
1029
let atom = CoordinateAtom()
1130
let context = AtomTestContext()
12-
let locationManager = MockLocationManager()
31+
let manager = MockLocationManager()
1332

14-
context.override(LocationManagerAtom()) { _ in locationManager }
33+
context.override(LocationObserverAtom()) { _ in
34+
LocationObserver(manager: manager)
35+
}
1536

16-
locationManager.location = CLLocation(latitude: 1, longitude: 2)
37+
manager.location = CLLocation(latitude: 1, longitude: 2)
1738

1839
XCTAssertEqual(context.watch(atom)?.latitude, 1)
1940
XCTAssertEqual(context.watch(atom)?.longitude, 2)
2041
}
2142

22-
func testAuthorizationStatusAtom() {
43+
func testAuthorizationStatusAtom() async {
2344
let atom = AuthorizationStatusAtom()
24-
let locationManager = MockLocationManager()
45+
let manager = MockLocationManager()
2546
let context = AtomTestContext()
47+
let observer = LocationObserver(manager: manager)
2648

27-
context.override(LocationManagerAtom()) { _ in locationManager }
49+
context.override(LocationObserverAtom()) { _ in
50+
observer
51+
}
2852

29-
locationManager.authorizationStatus = .authorizedWhenInUse
53+
manager.authorizationStatus = .authorizedWhenInUse
3054

3155
XCTAssertEqual(context.watch(atom), .authorizedWhenInUse)
56+
57+
manager.authorizationStatus = .authorizedAlways
58+
59+
Task {
60+
observer.objectWillChange.send()
61+
}
62+
63+
await context.waitUntilNextUpdate()
64+
65+
XCTAssertEqual(context.watch(atom), .authorizedAlways)
66+
67+
observer.objectWillChange.send()
68+
let didUpdate = await context.waitUntilNextUpdate(timeout: 1)
69+
70+
// Should not update if authorizationStatus is not changed.
71+
XCTAssertFalse(didUpdate)
3272
}
3373
}

0 commit comments

Comments
 (0)