fix: require a background keep alive

This commit is contained in:
CelloSerenity 2026-09-27 13:54:35 -06:00
parent 2d09a405de
commit 6a078be17e
3 changed files with 86 additions and 30 deletions

View file

@ -29,10 +29,15 @@ enum AppBootstrapper {
}
private static func startConfiguredKeepAliveServices() {
guard UserDefaults.standard.bool(forKey: "keepAliveAudio") else {
return
let defaults = UserDefaults.standard
if defaults.bool(forKey: "keepAliveAudio") {
BackgroundAudioManager.shared.start()
} else if defaults.bool(forKey: "keepAliveLocation") {
BackgroundLocationManager.shared.start()
} else {
defaults.set(true, forKey: "keepAliveAudio")
BackgroundAudioManager.shared.start()
}
BackgroundAudioManager.shared.start()
}
private static func applyDocumentPickerCopyWorkaround() {

View file

@ -10,6 +10,7 @@ final class BackgroundLocationManager: NSObject, CLLocationManagerDelegate {
private let locationManager = CLLocationManager()
private var isRunning = false
private var persistentEnabled = false
private var activityCount = 0
private override init() {
@ -22,7 +23,35 @@ final class BackgroundLocationManager: NSObject, CLLocationManagerDelegate {
}
func start() {
isRunning = true
persistentEnabled = true
refreshRunningState()
}
func stop() {
persistentEnabled = false
refreshRunningState()
}
func requestStart() {
activityCount += 1
refreshRunningState()
}
func requestStop() {
activityCount = max(activityCount - 1, 0)
refreshRunningState()
}
private func refreshRunningState() {
let shouldRun = persistentEnabled || (activityCount > 0 && UserDefaults.standard.bool(forKey: "keepAliveLocation"))
guard shouldRun != isRunning else { return }
isRunning = shouldRun
guard shouldRun else {
locationManager.stopUpdatingLocation()
return
}
switch locationManager.authorizationStatus {
case .authorizedAlways:
locationManager.startUpdatingLocation()
@ -30,27 +59,10 @@ final class BackgroundLocationManager: NSObject, CLLocationManagerDelegate {
locationManager.requestAlwaysAuthorization()
case .notDetermined:
locationManager.requestAlwaysAuthorization()
default:
break
}
}
func stop() {
isRunning = false
locationManager.stopUpdatingLocation()
}
func requestStart() {
activityCount += 1
if activityCount == 1, UserDefaults.standard.bool(forKey: "keepAliveLocation") {
start()
}
}
func requestStop() {
activityCount = max(activityCount - 1, 0)
if activityCount == 0 {
stop()
case .denied, .restricted:
restoreAudioFallback()
@unknown default:
restoreAudioFallback()
}
}
@ -59,11 +71,22 @@ final class BackgroundLocationManager: NSObject, CLLocationManagerDelegate {
switch manager.authorizationStatus {
case .authorizedAlways, .authorizedWhenInUse:
manager.startUpdatingLocation()
default:
break
case .denied, .restricted:
restoreAudioFallback()
case .notDetermined:
return
@unknown default:
restoreAudioFallback()
}
}
private func restoreAudioFallback() {
persistentEnabled = false
isRunning = activityCount > 0 && UserDefaults.standard.bool(forKey: "keepAliveLocation")
UserDefaults.standard.set(true, forKey: "keepAliveAudio")
BackgroundAudioManager.shared.start()
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
// Location fixes may fail (e.g. no GPS indoors) — that's fine.
// The manager just needs to be running, not actually fix a location.

View file

@ -95,8 +95,7 @@ struct SettingsView: View {
}
}
.onChange(of: keepAliveAudio) { _, enabled in
if enabled { BackgroundAudioManager.shared.start() }
else { BackgroundAudioManager.shared.stop() }
handleAudioKeepAliveChange(enabled)
}
Toggle(isOn: $keepAliveLocation) {
@ -107,7 +106,7 @@ struct SettingsView: View {
}
}
.onChange(of: keepAliveLocation) { _, enabled in
if !enabled { BackgroundLocationManager.shared.stop() }
handleLocationKeepAliveChange(enabled)
}
} header: {
@ -242,6 +241,35 @@ struct SettingsView: View {
return "Version \(appVersion) • iOS \(UIDevice.current.systemVersion) • \(txmLabel)"
}
private func handleAudioKeepAliveChange(_ enabled: Bool) {
if enabled {
BackgroundAudioManager.shared.start()
BackgroundLocationManager.shared.stop()
return
}
if !keepAliveLocation {
keepAliveLocation = true
}
BackgroundAudioManager.shared.stop()
BackgroundLocationManager.shared.start()
}
private func handleLocationKeepAliveChange(_ enabled: Bool) {
if enabled {
if !keepAliveAudio {
BackgroundLocationManager.shared.start()
}
return
}
BackgroundLocationManager.shared.stop()
if !keepAliveAudio {
keepAliveAudio = true
BackgroundAudioManager.shared.start()
}
}
// MARK: - Business Logic
private func openAppFolder() {