You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

iOS Swift 后台定位问题:锁屏后每30秒获取位置中断解决方案

iOS Swift锁屏后持续每30秒获取位置的解决方案

嘿,这个问题我之前帮好几个开发者踩过坑——iOS对后台权限管控特别严,尤其是锁屏后App进入后台状态,普通的定时器或者定位设置很容易被系统掐断。咱们一步步来搞定它:

1. 先搞定基础配置(缺一不可)

iOS后台定位的前提是正确配置权限和后台模式,不然代码写得再好也白搭:

  • 配置Info.plist权限描述
    必须添加这两个键(iOS 11+ 需优先配置NSLocationAlwaysAndWhenInUseUsageDescription),内容要清晰告诉用户你为什么需要这个权限,比如“需要后台获取位置来提供实时轨迹服务”:

    • NSLocationAlwaysAndWhenInUseUsageDescription
    • NSLocationWhenInUseUsageDescription
  • 开启后台模式
    在Xcode里打开项目,进入Signing & Capabilities标签,点击+ Capability,搜索Background Modes并添加,然后勾选Location updates选项。

2. 正确设置CLLocationManager

核心是让定位管理器明确可以在后台工作,同时避免系统自动暂停更新:

import CoreLocation
import UIKit

class LocationManager: NSObject, CLLocationManagerDelegate {
    static let shared = LocationManager()
    private let manager = CLLocationManager()
    private var lastFetchTime: Date? // 记录上次获取位置的时间
    private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid
    
    private override init() {
        super.init()
        manager.delegate = self
        // 按需设置精度,太高会耗电,iOS也可能限制频率
        manager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
        manager.distanceFilter = kCLDistanceFilterNone // 不限制距离变化,有更新就回调
        manager.allowsBackgroundLocationUpdates = true // 允许后台更新
        manager.pausesLocationUpdatesAutomatically = false // 禁止系统自动暂停定位
    }
    
    // 启动定位流程
    func startFetchingLocation() {
        // iOS 13+ 权限请求有顺序要求:先请求WhenInUse,用户同意后再请求Always
        if #available(iOS 13.0, *) {
            manager.requestWhenInUseAuthorization()
        } else {
            manager.requestAlwaysAuthorization()
        }
    }
    
    // 权限状态变化回调
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        switch manager.authorizationStatus {
        case .authorizedAlways, .authorizedWhenInUse:
            // 第一次启动定位
            manager.startUpdatingLocation()
            // 调度下一次30秒后的定位请求
            scheduleNextFetch()
        case .denied, .restricted:
            print("定位权限被拒绝,无法后台获取位置")
        default:
            break
        }
    }
    
    // 获取到位置的回调
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let latestLocation = locations.last else { return }
        
        // 记录本次获取时间
        lastFetchTime = Date()
        print("后台获取到位置:\(latestLocation.coordinate.latitude), \(latestLocation.coordinate.longitude)")
        
        // 这里可以处理你的业务逻辑,比如上传到服务器
        handleLocationBusiness(latestLocation)
        
        // 暂时停止定位,节省电量,等下一次调度再启动
        manager.stopUpdatingLocation()
    }
    
    // 调度下一次30秒后的定位
    private func scheduleNextFetch() {
        DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 30) { [weak self] in
            guard let self = self else { return }
            
            // 保险起见,检查距离上次获取是否超过25秒(防止调度误差)
            let needFetch = self.lastFetchTime == nil || Date().timeIntervalSince(self.lastFetchTime!) > 25
            if needFetch {
                self.manager.startUpdatingLocation()
            }
            
            // 循环调度,保证每30秒一次
            self.scheduleNextFetch()
        }
    }
    
    // 处理业务逻辑(比如上传),需要申请后台任务防止被系统挂起
    private func handleLocationBusiness(_ location: CLLocation) {
        // 申请后台任务标识符
        backgroundTaskID = UIApplication.shared.beginBackgroundTask(withName: "LocationBusiness") {
            // 任务超时,结束后台任务
            UIApplication.shared.endBackgroundTask(self.backgroundTaskID)
            self.backgroundTaskID = .invalid
        }
        
        // 模拟业务操作(比如上传服务器)
        DispatchQueue.global().async {
            // 这里替换成你的实际业务代码
            Thread.sleep(forTimeInterval: 3)
            print("位置业务处理完成")
            
            // 结束后台任务
            UIApplication.shared.endBackgroundTask(self.backgroundTaskID)
            self.backgroundTaskID = .invalid
        }
    }
}

3. 关键注意事项

  • 权限请求顺序:iOS 13及以上,必须先请求WhenInUse权限,用户同意后再请求Always权限(可以在用户同意后弹出二次请求),直接请求Always会被系统拒绝。
  • 耗电优化:持续后台定位非常耗电,建议在不需要的时候及时停止定位;如果不需要高精度,把desiredAccuracy设为更低的值,比如kCLLocationAccuracyHundredMeters
  • 审核注意:App Store审核时,必须在权限描述和App功能里明确说明后台定位的必要性,如果理由不充分,会被拒绝。
  • 系统限制:如果设备静止不动,iOS可能会降低定位更新频率来省电,这时候你可以通过定期启动/停止定位的方式(就像上面代码里的那样)来强制触发更新,但也不要太频繁,避免被系统判定为滥用权限。

最后,在App启动的时候调用LocationManager.shared.startFetchingLocation()就可以开始后台定位了。

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

火山引擎 最新活动