This is my setup:
Granted always allow permission.
I have location added in UIBackgroundModes, but I did NOT set allowsBackgroundLocationUpdates to true
Note: I have this allowsBackgroundLocationUpdates = true set in my earlier version of app, which worked but we noticed it drained battery much faster, hence we removed all the settings that could affect battery.
The location model is setup with 20 regions, when boundary crossing happen, app sends a local notification. This works fine when app is in foreground/background. If app is killed, the app receives notification for boundary crossing only once.
Failed case for region monitoring:
Setup region monitoring
Kill the app
cross the boundary, app sends a local notification.
wait for 1 hour
leave the device in same state (notification is not opened, app is still killed state)
cross the boundary again
expect a notification, but app did not register any event related to region monitoring.
The console logs did not print anything in this second case.
public class LocationViewModel: NSObject, ObservableObject {
private let maxMonitoredRegions = 20
private var anyCancellable: AnyCancellable?
private let locationManager: CLLocationManager
@Published public var authorizationStatus: CLAuthorizationStatus
@Published public var isMonitoringAvailable: Bool
@Published public var monitoredRegions: [Region]
@Published public var recentLocation: CLLocation?
public var newlyEnteredRegionSignal = PassthroughSubject<CLRegion, Never>()
public var recentLocationSignal = PassthroughSubject<CLLocation, Never>()
public var authorizationStatusPublisher: Published<CLAuthorizationStatus>.Publisher { $authorizationStatus }
public var isLocationEnabled: Bool {
locationManager.authorizationStatus == .authorizedWhenInUse ||
locationManager.authorizationStatus == .authorizedAlways
}
public override init() {
locationManager = CLLocationManager()
authorizationStatus = locationManager.authorizationStatus
isMonitoringAvailable = CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self)
monitoredRegions = []
super.init()
locationManager.delegate = self
monitoredRegions.append(contentsOf: getMonitoredRegions())
requestLocation()
}
public func requestLocation() {
locationManager.requestLocation()
}
public func startRegionMonitoring(regions: [CLRegion]) {
guard isMonitoringAvailable else {
return
}
stopRegionMonitoring()
if regions.isEmpty {
return
}
if regions.count <= 20 {
for region in regions {
locationManager.startMonitoring(for: region)
}
} else {
for region in regions[0...maxMonitoredRegions-1] {
locationManager.startMonitoring(for: region)
}
}
}
public func stopRegionMonitoring() {
guard isMonitoringAvailable else {
return
}
if monitoredRegions.isEmpty {
return
}
for region in monitoredRegions {
let monitoredRegion = LocationUtils.convertRegionToCLRegion(region)
locationManager.stopMonitoring(for: monitoredRegion)
}
monitoredRegions.removeAll()
}
private func getMonitoredRegions() -> [Region] {
let monitoredRegions = locationManager.monitoredRegions
var regions = [Region]()
for monitoredRegion in monitoredRegions {
if let region = LocationUtils.convertCLRegionToRegion(monitoredRegion) {
regions.append(region)
}
}
return regions
}
public func stopMonitoring() {
recentLocation = nil
stopRegionMonitoring()
}
}
extension LocationViewModel: CLLocationManagerDelegate {
public func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
authorizationStatus = manager.authorizationStatus
switch authorizationStatus {
case .notDetermined:
stopMonitoring()
case .denied:
stopMonitoring()
case .authorizedAlways:
break
case .authorizedWhenInUse:
// If user has requested whenInUse, request for always allow.
locationManager.requestAlwaysAuthorization()
@unknown default:
break
}
if let location = manager.location {
recentLocationSignal.send(location)
recentLocation = location
}
}
public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let recentLocation = locations.last {
self.recentLocation = recentLocation
recentLocationSignal.send(recentLocation)
}
}
public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
}
public func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion) {
if let monitoredRegion = LocationUtils.convertCLRegionToRegion(region) {
let oldRegion = monitoredRegions.first {
$0.identifier == monitoredRegion.identifier
}
if oldRegion == nil {
monitoredRegions.append(monitoredRegion)
}
}
}
public func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) {
}
public func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
newlyEnteredRegionSignal.send(region)
}
public func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
newlyEnteredRegionSignal.send(region)
}
}
When app is awaken due to location event on app delegate, we initialize this location model, and location manager, and remove old monitored regions, and call startMonitoringRegions again, to keep the regions updated.
Please let me know if I'm missing any crucial information.
Post
Replies
Boosts
Views
Activity
I have created a swift package CommonPackage with files that are common to two targets.
In my project I have two targets.
MainApp
Safari Extension target for MainApp
Now, I added this package in the Frameworks, Libraries section for both targets. I am able to use this package in the main app target, but when I import this package in my Safari Extension target in SFSafariExtensionHandler class , it is giving me the
No such module CommonPackage error.
The package file is pretty generic:
let package = Package(
name: "CommonPackage",
platforms: [
.iOS(.v14)
],
products: [
.library(
name: "CommonPackage",
targets: ["CommonPackage"]),
],
dependencies: [
// Depends on another swift package, stored in separate repo.
],
targets: [
.target(
name: "CommonPackage",
dependencies: []),
.testTarget(
name: "CommonPackage Tests",
dependencies: ["CommonPackage"]),
]
)
Not sure why I am not able to use this package in the safari extension target. I spent many hours trying different solutions to resolve No such module error, but none worked. In one of WWDC video they show, this kind of modularization is supported, but in my case it is not. I started to think, is it because of the way my project is supported?
Note: The safari extension target is a child target to main app target, which means, the safari extension is added as a dependency in the main app target settings.
Really appreciate if someone can point me in right direction. I can provide more details in the question, but not sure what relevant details I need to include.