深入探索 MapKit
Go further with MapKit
2025年6月10日
一句话判断
如果你的 app 里有任何”把别人的地点 ID 塞进 MapKit”的胶水代码,这场 Session 直接帮你删掉它。
这场 Session 讲了什么
MapKit 一直有个尴尬的问题:你的地点数据来自高德、Google Places、自家后端,但 MapKit 只认自己的 Place ID。开发者被迫写一堆转换逻辑,把坐标、地址、第三方 ID 来回倒腾。这次 Apple 推出的 PlaceDescriptor 就是来干掉这层胶水的——它用一个 representations 数组把坐标、地址、服务标识符打包在一起,MapKit 自己挑最合适的去用。你不再需要先查一个 MapKit Place ID 才能操作地点。
另一个大动作是地理编码(Geocoding)从 CoreLocation 搬家到 MapKit。CLGeocoder 和 CLPlacemark 被废弃了,取而代之的是 MKReverseGeocodingRequest,返回的不再是那个臃肿的 CLPlacemark(里面有时区、海洋标识这些你永远用不到的字段),而是干净的 MKMapItem 加上全新的 MKAddress 类型。地址格式化终于不用自己拼字符串了。
骑行路线扩展到了 watchOS,Look Around 街景登陆 MapKit JS。骑行时看手表比掏手机自然得多,这个设备选择很合理。Look Around 在 Web 端的落地则直接抢了 Google Street View 的地盘,房产和旅游类网站终于有了 Apple 生态的街景方案。这几个变化单独看不大,但放在一起说明 Apple 在把 MapKit 从”iOS 地图控件”推向”全平台地点基础设施”——一个你可以在 iPhone、Apple Watch、Web 页面上统一调用的地点智能层。
值得深挖的点
PlaceDescriptor 的设计取舍
PlaceDescriptor 最有意思的地方是它的 representations 数组按优先级排列。最精确的信息排前面,MapKit 按顺序尝试匹配。这意味着你可以同时塞进坐标、模糊地址、高德的 POI ID,系统自己决定用哪个。这个设计的好处是容错性高——坐标偏了还有地址兜底,地址不精确还有服务标识符。但代价是你得把所有能拿到的信息都塞进去,数据量会膨胀。
serviceIdentifiers 字段用 Bundle identifier -> Place ID 的映射来实现跨服务引用。理论上你可以在 PlaceDescriptor 里声明”这个地点在高德是 xxx,在 Google 是 yyy”,MapKit 理解这个映射关系后就能在不同服务间打通地点引用。这比你手动维护一张”高德 ID -> Apple ID”的对照表优雅太多了。
但这里有个现实问题:Apple 并没有公开哪些第三方服务被支持,这个映射的覆盖范围是个黑盒。文档里给了 API,但没给你一张”支持的服务列表”。在中国市场,高德和百度的 POI 精度通常优于 Apple Maps,serviceIdentifiers 能不能真正打通这些数据源,得实测才知道。另一个容易忽略的问题是 Bundle identifier 的归属——serviceIdentifiers 的 key 应该是调用方应用自己的 bundle identifier,不是目标服务的标识符。这个语义反直觉,开发时很容易搞反。如果你的 app 重度依赖国内地图数据,别急着把现有的坐标转换逻辑删掉,先在真机上验证 PlaceDescriptor 在你的具体场景下能不能返回准确结果。
CLGeocoder 退役的影响
CLGeocoder 从 iOS 5 用到现在,废弃它是个大胆的决定。新的 MKReverseGeocodingRequest 返回 MKMapItem,信息确实更丰富——名称、电话、URL、分类一应俱全。新增的 MKAddress 类型提供 fullAddress、shortAddress 等预设格式,还有 MKAddressRepresentations 支持按语言环境自定义展示。这对做多语言 app 的团队是实打实的简化:以前你得自己判断 locale 然后拼地址字符串,现在框架帮你搞定。
但迁移有个坑:MKReverseGeocodingRequest 返回的是数组,不是单个结果。精度要求不高的场景取 first 没问题,但导航目的地确认这种场景你得遍历匹配。另外 address.representations 是异步属性,获取时需要 await——如果你的代码是同步写的,这不只是换 API,是调用链的架构调整。现有的同步地理编码逻辑需要拆成异步流程,涉及的上下游代码都得跟着改。这个工作量在大项目里可能比想象的大,建议先评估一下你有多少处调用了 CLGeocoder。
代码片段
用 PlaceDescriptor 桥接外部地点数据
场景:你的 app 从后端拿到一批高德坐标,要在 MapKit 里显示和搜索。
import MapKit
import GeoToolbox
let place = PlaceDescriptor(
coordinate: CLLocationCoordinate2D(latitude: 39.9042, longitude: 116.4074),
address: "北京市东城区天安门广场",
serviceIdentifiers: [Bundle.main.bundleIdentifier!: "gaode_poi_001"]
)
let request = MKMapItemRequest(placeDescriptor: place)
request.getMapItem { mapItem, error in
guard let item = mapItem else { return }
print(item.name ?? "未知")
}
坑:representations 为空时查询直接失败,务必确保至少提供一种表达。
新版反向地理编码
场景:外卖配送场景,把骑手 GPS 坐标转成用户可读地址。
import MapKit
func reverseGeocode(coordinate: CLLocationCoordinate2D) async throws -> String? {
let request = MKReverseGeocodingRequest(coordinate: coordinate)
let mapItems = try await request?.mapItems
guard let address = mapItems?.first?.address else { return nil }
return address.fullAddress
}
坑:MKReverseGeocodingRequest 在网络不可用时可能返回空结果,生产环境建议加离线缓存。
骑行路线规划
场景:共享单车 app 计算骑行路线和预计时间。
import MapKit
func planCyclingRoute(from: CLLocationCoordinate2D, to: CLLocationCoordinate2D) async throws -> MKRoute? {
let request = MKDirections.Request()
request.source = MKMapItem(placemark: MKPlacemark(coordinate: from))
request.destination = MKMapItem(placemark: MKPlacemark(coordinate: to))
request.transportType = .cycling
let response = try await MKDirections(request: request).calculate()
return response.routes.first
}
坑:.cycling 在部分国家和地区不可用,返回空 routes 不代表代码有问题,可能只是该地区不支持。
最佳实践
建议分三步走。
第一步,先别急着迁移。CLGeocoder 虽然被废弃了,但短期内不会被移除——Apple 废弃一个从 iOS 5 就存在的 API,通常会给至少两个大版本的缓冲期。花一两天时间在核心场景里跑一下 MKReverseGeocodingRequest,对比新旧 API 返回的地址数据质量,尤其是中文地址的格式化效果。MKAddress 的 fullAddress 对中文地址的处理逻辑跟 CLPlacemark 不一定一致,比如它可能会省略”中国”前缀或者调整省市区的顺序。如果目标市场主要在国内,这个细节值得认真对比。
第二步,PlaceDescriptor 先在新功能里用。如果正在做”收藏地点”或”分享位置”这类新功能,直接用 PlaceDescriptor 来建模地点数据——这是它最舒服的使用姿势。但对于已有的、跑得很稳的地点查询逻辑,没动力就别动。PlaceDescriptor 主要解决的是”没有 MapKit Place ID”的场景,不是让开发者把现有的 Place ID 逻辑重写一遍。如果现在的代码里有一张”第三方 ID -> MapKit Place ID”的映射表,保持现状就好,PlaceDescriptor 不会让这张表变得多余。
第三步,骑行路线可以马上用。这个改动风险最低,API 跟驾车路线几乎一致,只是 transportType 换一下。如果 app 有骑行相关场景,这是一个投入产出比很高的小改进。
还有什么值得关注
- GeoToolbox 是个新框架,目前公开的能力主要服务于 PlaceDescriptor 和地理编码,但它的定位是”跨平台地理工具箱”,后续大概率会扩展更多地理数据处理能力。
- Look Around 街景登陆 MapKit JS,意味着房产、旅游类 Web 应用终于能嵌入 Apple 街景了,之前这块是 Google 的地盘。
MKAddressRepresentations支持按语言环境自定义地址格式,做国际化的团队可以用它干掉一大段手动拼接地址的工具代码。