构建位置感知的企业应用
Build location-aware enterprise apps
2020年6月24日
一句话判断
iOS 14 新增的”大致位置”选项让用户可以选择只给 App 一个模糊的位置精度,如果你的企业 App 需要精确定位(比如室内导航、资产管理),你需要引导用户授予精确定位权限。
这场 Session 讲了什么
iOS 14 对定位权限做了重大调整。之前用户只有”允许”和”不允许”两个选项。iOS 14 新增了第三种选择:“大致位置”(Approximate Location)。用户选择大致位置后,App 收到的坐标会被模糊化到约几平方公里的范围。
对企业 App 来说,这带来了新的挑战。如果你的 App 依赖精确定位(比如工厂设备定位、仓库货物追踪、办公室签到),你需要处理用户只给大致位置的情况。Session 介绍了如何检测当前的定位精度、如何引导用户切换到精确定位、以及如何使用新的 CLLocationManager API 来请求一次性精确定位。
值得深挖的点
精确定位 vs 大致定位的差异
当用户选择大致位置时,CLLocationManager 返回的坐标会在真实位置附近随机偏移。偏移范围大约在 1-5 公里之间,并且会定期变化(即使设备没有移动)。这种设计是为了防止通过定位数据追踪用户。对于需要街区级别或室内级别精度的 App,大致位置是完全不够用的。
一次性精确定位请求
iOS 14 引入了一个新的 API:requestTemporaryFullAccuracyAuthorization(withPurposeKey:)。这个 API 允许你请求一次性的精确定位权限。用户会看到一个弹窗,说明你的 App 为什么需要精确定位。如果用户同意,你的 App 会在当前会话中获得精确定位能力。下次启动时,会恢复到大致位置。
代码片段
检查当前的定位精度
场景:在 App 启动时检查用户授予的定位精度。
import CoreLocation
class LocationManager: NSObject, CLLocationManagerDelegate {
private let manager = CLLocationManager()
var onLocationUpdate: ((CLLocation) -> Void)?
override init() {
super.init()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
}
func requestLocation() {
// 检查定位权限状态
let status = manager.authorizationStatus
switch status {
case .notDetermined:
// 首次请求定位权限
manager.requestWhenInUseAuthorization()
case .authorizedWhenInUse, .authorizedAlways:
// 检查精度级别(iOS 14 新增)
if #available(iOS 14.0, *) {
let accuracy = manager.accuracyAuthorization
switch accuracy {
case .fullAccuracy:
// 用户授予了精确定位
print("精确定位已授权")
manager.requestLocation()
case .reducedAccuracy:
// 用户只授予了大致定位
print("仅大致定位,需要引导用户开启精确定位")
requestFullAccuracy()
@unknown default:
break
}
}
case .denied, .restricted:
print("定位权限被拒绝")
default:
break
}
}
private func requestFullAccuracy() {
if #available(iOS 14.0, *) {
// 请求一次性精确定位权限
// purposeKey 对应 Info.plist 中 NSLocationTemporaryUsageDescriptionDictionary 里的 key
manager.requestTemporaryFullAccuracyAuthorization(
withPurposeKey: "PreciseAssetTracking"
) { error in
if let error = error {
print("精确定位请求失败: \(error)")
// 使用大致位置继续工作,或提示用户
} else {
print("获得精确定位权限")
self.manager.requestLocation()
}
}
}
}
// CLLocationManagerDelegate
func locationManager(_ manager: CLLocationManager,
didUpdateLocations locations: [CLLocation]) {
guard let location = locations.first else { return }
onLocationUpdate?(location)
}
func locationManager(_ manager: CLLocationManager,
didFailWithError error: Error) {
print("定位失败: \(error)")
}
}
配置 Info.plist 的定位权限说明
场景:为精确定位请求提供用户可见的说明文本。
<!-- Info.plist 配置 -->
<!-- 标准的定位权限说明 -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>需要访问您的位置来标记设备位置</string>
<!-- 一次性精确定位的说明字典(iOS 14 新增) -->
<key>NSLocationTemporaryUsageDescriptionDictionary</key>
<dict>
<!-- key 对应代码中的 purposeKey -->
<key>PreciseAssetTracking</key>
<string>需要精确位置来定位您附近的设备资产</string>
<key>IndoorNavigation</key>
<string>需要精确位置来提供室内导航服务</string>
<key>GeofenceCheckIn</key>
<string>需要精确位置来确认您已到达办公室</string>
</dict>
企业场景:设备资产管理
场景:在企业园区内追踪设备位置。
class AssetTrackingManager {
private let locationManager = CLLocationManager()
private var assets: [Asset] = []
struct Asset {
let id: String
let name: String
var lastKnownLocation: CLLocation?
}
func startTracking(assetId: String) {
// 先检查是否有精确定位权限
if #available(iOS 14.0, *) {
if locationManager.accuracyAuthorization == .reducedAccuracy {
// 没有精确定位,请求一次性权限
locationManager.requestTemporaryFullAccuracyAuthorization(
withPurposeKey: "PreciseAssetTracking"
) { [weak self] error in
if error == nil {
self?.beginTracking(assetId: assetId)
}
}
return
}
}
beginTracking(assetId: assetId)
}
private func beginTracking(assetId: String) {
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
locationManager.startUpdatingLocation()
// 持续更新设备位置
// 将位置数据上报到资产管理服务器
}
func checkProximity(targetLocation: CLLocation,
threshold: Double = 10.0) -> Bool {
guard let currentLocation = locationManager.location else { return false }
let distance = currentLocation.distance(from: targetLocation)
return distance <= threshold // 在 10 米范围内
}
}
处理精度变化的回调
场景:当用户在设置中切换定位精度时做出响应。
class LocationAwareViewController: UIViewController {
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 14.0, *) {
// 监听精度变化
// 当用户在设置中切换精确定位/大致定位时会触发
}
}
}
// CLLocationManagerDelegate
extension LocationAwareViewController {
@available(iOS 14.0, *)
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
let status = manager.authorizationStatus
let accuracy = manager.accuracyAuthorization
switch (status, accuracy) {
case (.authorizedWhenInUse, .fullAccuracy):
// 精确定位可用
showFullFeatureSet()
case (.authorizedWhenInUse, .reducedAccuracy):
// 只有大致定位
showAccuracyUpgradePrompt()
case (.denied, _):
// 定位被拒绝
showLocationDeniedMessage()
default:
break
}
}
private func showAccuracyUpgradePrompt() {
let alert = UIAlertController(
title: "需要精确定位",
message: "此功能需要精确位置才能正常工作。请在弹出的选项中选择「允许一次精确定位」。",
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "开启精确定位", style: .default) { [weak self] _ in
self?.locationManager.requestTemporaryFullAccuracyAuthorization(
withPurposeKey: "PreciseAssetTracking"
)
})
alert.addAction(UIAlertAction(title: "继续使用大致位置", style: .cancel))
present(alert, animated: true)
}
}
最佳实践
已有项目:如果你的 App 依赖精确定位,在 iOS 14 上必须处理 reducedAccuracy 的情况。添加 NSLocationTemporaryUsageDescriptionDictionary 到 Info.plist,在需要精确定位时通过 requestTemporaryFullAccuracyAuthorization 请求权限。
新项目:从产品设计阶段就考虑定位精度的影响。区分哪些功能需要精确定位,哪些功能大致位置就够了。对于只需要城市级别定位的功能(如天气),大致位置完全够用。只有室内导航、资产追踪等场景才需要精确定位。
还有什么值得关注
- 用户可以在「设置 > 隐私 > 定位服务」中随时切换精确定位/大致定位。
CLLocationManager新增了accuracyAuthorization属性来查询当前精度级别。- 大致位置的坐标会定期变化,不要用它来判断设备是否静止。