-
Notifications
You must be signed in to change notification settings - Fork 222
Add Static Location and Live Location Support #3531
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
340f10c
92d15ac
962ffb4
cb365fc
8d3b662
b35f54e
afc3f68
736c08e
154e394
3b4d322
b9e1f2c
ec2f59a
0a1e120
61a6a70
64881a1
6f57eb9
caf76ab
5d09df8
667739f
713c5e8
54d623f
495b358
b09bf38
6468d0d
effff35
1e684db
81ad0f4
9ded30e
7df6c3e
b91fa09
e50a63b
1e9ef44
98afdba
7a0bd66
ecbb7e2
ec15a92
5e3881d
64bcf7e
f3460be
0af646e
544b506
0777d9f
b2dc128
45ad2f1
a3a252e
6cf17a3
26a5e72
e9be9de
37713b8
ef4a214
722cbfc
a347d00
c58a3c3
776a991
d3dfd2e
047f962
ea5802e
4712649
0a46f19
6be1283
5044362
2957975
a73406b
e203dfa
5c30dbe
1d7ab64
251fa84
a211f5d
5b64e9a
95d359f
a8bf8de
9c351a8
b035ed6
2ff83a0
3c0cf34
601fded
e8b6587
61882ef
f7dbf60
7b60602
cb135d0
f3d7025
5952dd6
cd18446
77baae2
0765272
d06eda0
37bd1d1
81cc2ee
2c5b331
d7728de
70fda95
6f277d5
9d2b557
6748eed
1048c22
25d3705
46c5fc6
d4de8f8
58653d8
5775d32
ce36472
7ab0dd8
356cb17
001e2af
9311f09
9a24297
6c1f1b0
9a4d314
334c6e4
47cc6c2
62fe047
875edd8
4b8181f
c07f7ad
97c1e79
b4033af
96e5fc6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,99 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Copyright © 2025 Stream.io Inc. All rights reserved. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import CoreLocation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import Foundation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
enum LocationPermissionError: Error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
case permissionDenied | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
case permissionRestricted | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
class LocationProvider: NSObject { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private let locationManager: CLLocationManager | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private var onCurrentLocationFetch: ((Result<CLLocation, Error>) -> Void)? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var didUpdateLocation: ((CLLocation) -> Void)? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var lastLocation: CLLocation? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var onError: ((Error) -> Void)? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private init(locationManager: CLLocationManager = CLLocationManager()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.locationManager = locationManager | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
super.init() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
static let shared = LocationProvider() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
var isMonitoringLocation: Bool { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
locationManager.delegate != nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func startMonitoringLocation() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
locationManager.allowsBackgroundLocationUpdates = true | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
locationManager.delegate = self | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
requestPermission { [weak self] error in | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
guard let error else { return } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self?.onError?(error) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func stopMonitoringLocation() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
locationManager.allowsBackgroundLocationUpdates = false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
locationManager.stopUpdatingLocation() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
locationManager.delegate = nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func getCurrentLocation(completion: @escaping (Result<CLLocation, Error>) -> Void) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onCurrentLocationFetch = completion | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if let lastLocation = lastLocation { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onCurrentLocationFetch?(.success(lastLocation)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onCurrentLocationFetch = nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
requestPermission { [weak self] error in | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
guard let error else { return } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self?.onCurrentLocationFetch?(.failure(error)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self?.onCurrentLocationFetch = nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func requestPermission(completion: @escaping (Error?) -> Void) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
locationManager.delegate = self | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
switch locationManager.authorizationStatus { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
case .notDetermined: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
locationManager.requestWhenInUseAuthorization() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
completion(nil) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
case .authorizedWhenInUse, .authorizedAlways: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
locationManager.startUpdatingLocation() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
completion(nil) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
case .denied: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
completion(LocationPermissionError.permissionDenied) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
case .restricted: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
completion(LocationPermissionError.permissionRestricted) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@unknown default: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
break | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+61
to
+77
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Premature success callback can mis-signal permission status Inside Consider: case .notDetermined:
- locationManager.requestWhenInUseAuthorization()
- completion(nil)
+ locationManager.requestWhenInUseAuthorization()
+ // Defer completion until we know the final status in
+ // `locationManagerDidChangeAuthorization`.
+ return and move the 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
extension LocationProvider: CLLocationManagerDelegate { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
let status = manager.authorizationStatus | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if status == .authorizedWhenInUse || status == .authorizedAlways { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
manager.startUpdatingLocation() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
guard let location = locations.first else { return } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
didUpdateLocation?(location) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
lastLocation = location | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onCurrentLocationFetch?(.success(location)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onCurrentLocationFetch = nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func locationManager(_ manager: CLLocationManager, didFailWithError error: any Error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onError?(error) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+88
to
+98
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Dispatch location callbacks on the main queue
- didUpdateLocation?(location)
+ DispatchQueue.main.async { [weak self] in
+ self?.didUpdateLocation?(location)
+ }
...
- onError?(error)
+ DispatchQueue.main.async { [weak self] in
+ self?.onError?(error)
+ } Making the dispatch explicit keeps UI handling code safe without forcing every caller to hop to the main thread themselves. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Remove or set proper value for LSApplicationCategoryType.
The
LSApplicationCategoryType
key is set to an empty string, which is not a valid App Store category. Either remove this key entirely or set it to a proper category like"public.app-category.social-networking"
.📝 Committable suggestion
🤖 Prompt for AI Agents