Swift基于位置的持久化通知异常问题求助
问题描述
我为首个Swift应用编写了如下代码,目前仅当应用关闭且处于指定位置坐标时,通知事件触发后能正常推送通知,其余所有场景均失败。已在后台模式中开启Location Updates和Background Fetch权限,恳请各位提供解决思路,谢谢。
import SwiftUI import WebKit import CoreLocation import UserNotifications let locationManager = CLLocationManager() struct ContentView: View { @StateObject private var locationManager = LocationManager() var body: some View { WebView(url: URL(string: "https://www.website.uk")!) .edgesIgnoringSafeArea(.all) .onAppear { locationManager.requestPermissions() } } } struct WebView: UIViewRepresentable { var url: URL func makeUIView(context: Context) -> WKWebView { WKWebView() } func updateUIView(_ uiView: WKWebView, context: Context) { let request = URLRequest(url: url) uiView.load(request) } } @main struct NotificationTestApp: App { var body: some Scene { WindowGroup { ContentView() } } } class LocationManager: NSObject, CLLocationManagerDelegate, ObservableObject { private var locationManager = CLLocationManager() override init() { super.init() locationManager.delegate = self } func triggerNotification(_ password: String) { let center = UNUserNotificationCenter.current() let content = UNMutableNotificationContent() content.title = "My First Notification" content.body = password content.sound = UNNotificationSound.default content.subtitle = "Hello World" content.userInfo = ["value": "Data with local notification"] let fireDate = Calendar.current.dateComponents([.day, .month, .year, .hour, .minute, .second], from: Date().addingTimeInterval(10)) let trigger = UNCalendarNotificationTrigger(dateMatching: fireDate, repeats: true) let request = UNNotificationRequest(identifier: "reminder", content: content, trigger: trigger) center.add(request) { (error) in if error != nil { print("Error = \(error?.localizedDescription ?? "error in local notification") ") } } } func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) { if region.identifier == "LOC1" { triggerNotification("Boo: " + Date().formatted()) } } func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) { if region.identifier == "LOC1" { //reset the region state locationManager.stopMonitoring(for: region) locationManager.startMonitoring(for: region) } } func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) { print("Failed to monitor region: \(error.localizedDescription)") } func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { print("Location manager failed: \(error.localizedDescription)") } func requestPermissions() { locationManager.requestWhenInUseAuthorization() UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in if granted { print("Notification permission granted.") //self.triggerNotification("Test") } else if let error = error { print("Notification permission denied with error: \(error.localizedDescription)") } } //Setup Geofence let locationCoordinates = CLLocationCoordinate2D(latitude: 50.1234, longitude: -1.2345) let geofenceRegion = CLCircularRegion(center: locationCoordinates, radius: 500, identifier: "LOC1") geofenceRegion.notifyOnEntry = true geofenceRegion.notifyOnEntry = true locationManager.startMonitoring(for: geofenceRegion) } }
解决思路
1. 升级位置权限级别
当前使用的requestWhenInUseAuthorization()仅支持前台场景,后台/挂起状态下必须申请始终允许权限:
- 将
requestWhenInUseAuthorization()替换为requestAlwaysAuthorization() - 在
Info.plist中添加NSLocationAlwaysAndWhenInUseUsageDescription和NSLocationWhenInUseUsageDescription字段,补充权限申请说明文本
2. 修复地理围栏与通知触发逻辑
- 删除重复的
geofenceRegion.notifyOnEntry = true代码行 - 将重复触发的
UNCalendarNotificationTrigger改为即时触发(地理围栏只需要单次通知):// 生成唯一标识符避免覆盖请求,直接触发不需要延迟 let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil) - 退出区域时的重启监测逻辑改为先请求区域状态,避免无效操作:
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) { if region.identifier == "LOC1", let circularRegion = region as? CLCircularRegion { manager.requestState(for: circularRegion) } } // 通过状态回调处理后续逻辑 func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) { if region.identifier == "LOC1", state == .outside { manager.stopMonitoring(for: region) manager.startMonitoring(for: region) } }
3. 完善后台定位配置
在requestPermissions()中添加后台定位允许设置:
func requestPermissions() { locationManager.requestAlwaysAuthorization() locationManager.allowsBackgroundLocationUpdates = true // 新增该行 // 其余代码保持不变 }
4. 确保定位服务激活
在权限申请通过后,主动启动定位服务,保障地理围栏监测生效:
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in if granted { print("Notification permission granted.") DispatchQueue.main.async { self.locationManager.startUpdatingLocation() } } // 错误处理代码保持不变 }
5. 测试注意事项
- 必须用真机测试,模拟器的地理围栏触发逻辑与真机存在差异
- 测试时需实际移动到区域边界附近,设备静止时系统会延迟地理围栏触发
内容的提问来源于stack exchange,提问作者Dean Bayley
相关产品推荐
相关产品推荐

