You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS13 SwiftUI背景位置更新处理器实现技术问询

解决SwiftUI后台持续更新位置的问题(无需「始终允许」权限)

我很理解你的困扰——不少开发者都碰到过这个问题:用户通常不愿意把位置权限从「使用应用期间」切换到「始终允许」,但我们又需要在APP后台时持续获取位置更新。结合你给出的配置和代码,我来帮你调整出正确的实现方式:

一、先完善Info.plist的配置

你的plist已经加了UIBackgroundModes的location项,这很好,但还有两个细节要调整:

  • 替换旧的NSLocationUsageDescription为NSLocationWhenInUseUsageDescription(前者在iOS 11+已经被废弃,requestWhenInUseAuthorization需要这个描述)
  • 确保NSLocationAlwaysAndWhenInUseUsageDescription保留,后续请求「始终允许」权限时会用到

调整后的相关配置片段:

<key>NSLocationWhenInUseUsageDescription</key>
<string>Delivering location based Fuel Price Updates, Weather, News & Sports. As well as local offers.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Delivering location based Fuel Price Updates, Weather, News & Sports. As well as local offers.</string>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
<!-- 如果不需要audio后台模式可以移除 -->
<string>audio</string>
</array>

二、修改LocationManager的核心实现

你的现有代码缺少后台位置更新的关键配置,我来帮你调整:

修改后的完整LocationManager代码

import Foundation
import CoreLocation
import Combine
import UIKit
import SwiftUI

class LocationManager: NSObject, CLLocationManagerDelegate, ObservableObject {
    private let manager: CLLocationManager
    static var LMlat = 0.0
    static var LMlong = 0.0
    @Published var lastKnownLocation: CLLocation?
    
    override init() {
        self.manager = CLLocationManager()
        super.init()
        // 提前设置delegate,避免延迟
        self.manager.delegate = self
        // 开启后台位置更新权限
        self.manager.allowsBackgroundLocationUpdates = true
        // 禁止系统自动暂停位置更新
        self.manager.pausesLocationUpdatesAutomatically = false
        // 如果是导航类应用,可以设置activityType优化后台更新频率
        self.manager.activityType = .automotiveNavigation
    }
    
    func startUpdating() {
        // 先请求「使用应用期间」权限
        if manager.authorizationStatus == .notDetermined {
            manager.requestWhenInUseAuthorization()
        }
        manager.startUpdatingLocation()
    }
    
    // 权限状态变化处理
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        switch status {
        case .denied, .restricted:
            print("位置权限被拒绝/受限")
            // 这里可以引导用户去设置开启权限
        case .authorizedWhenInUse, .authorizedAlways:
            print("位置权限已授权")
            manager.startUpdatingLocation()
        default:
            break
        }
    }
    
    // 位置更新回调(后台也会触发)
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let newLocation = locations.last else { return }
        
        // 过滤掉精度低的位置(可选,根据需求调整)
        guard newLocation.horizontalAccuracy < 100 else { return }
        
        lastKnownLocation = newLocation
        
        // 仅当位置变化超过阈值时才更新服务器(避免频繁请求)
        let locationChanged = abs(newLocation.coordinate.latitude - LocationManager.LMlat) > 0.0001 ||
                              abs(newLocation.coordinate.longitude - LocationManager.LMlong) > 0.0001
        
        if locationChanged {
            LocationManager.LMlat = newLocation.coordinate.latitude
            LocationManager.LMlong = newLocation.coordinate.longitude
            updateServerLocation(latitude: LocationManager.LMlat, longitude: LocationManager.LMlong)
            print("位置已更新,同步到服务器")
        }
    }
    
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print("位置更新失败:\(error.localizedDescription)")
    }
    
    // 同步位置到服务器
    private func updateServerLocation(latitude:Double,longitude:Double) {
        let locationurl = URL(string: "https://EXAMPLE.com/lat=\(latitude)&long=\(longitude)")!
        URLSession.shared.dataTask(with: locationurl) { _, _, err in
            if let err = err {
                print("同步服务器失败:\(err.localizedDescription)")
                return
            }
            DispatchQueue.main.async {
                print("位置已成功同步到服务器")
            }
        }.resume()
    }
}

关键修改点说明

  1. 初始化时配置后台更新:在init里就设置allowsBackgroundLocationUpdates = true和pausesLocationUpdatesAutomatically = false,这是后台持续更新的核心开关
  2. 优化权限请求逻辑:只在权限未确定时请求「使用应用期间」权限,避免重复请求;权限状态变化后自动启动更新
  3. 过滤无效位置:添加了精度过滤,避免使用误差过大的位置数据
  4. 优化位置变化判断:用阈值判断位置是否真的变化,减少不必要的服务器请求
  5. 设置activityType:如果你的应用是导航类,设置.automotiveNavigation可以让iOS更积极地在后台提供位置更新

三、ContentView中的正确调用

确保你用@StateObject持有LocationManager(SwiftUI 2.0+),避免对象被意外释放:

struct ContentView: View {
    @StateObject private var locationManager = LocationManager()
    
    var lat: String{
        return "\(locationManager.lastKnownLocation?.coordinate.latitude ?? 0.0)"
    }
    var lon: String{
        return "\(locationManager.lastKnownLocation?.coordinate.longitude ?? 0.0)"
    }
    
    var body: some View {
        Text("当前位置:\(lat), \(lon)")
            .onAppear {
                locationManager.startUpdating()
            }
    }
}

重要注意事项

  • 即使只有「使用应用期间」权限,开启allowsBackgroundLocationUpdates后,APP在后台仍然可以获取位置更新,但iOS会根据应用类型限制更新频率(导航类应用会获得更频繁的更新)
  • 不要滥用后台位置更新,否则可能被App Store拒绝,确保你的使用场景符合Apple的后台模式规范
  • 如果后续需要请求「始终允许」权限,建议在用户主动触发某个需要后台持续定位的功能时再发起请求,而不是盲目弹出权限弹窗

内容的提问来源于stack exchange,提问作者RussellHarrower

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 00:32:45