Meet Apple Maps Server APIs
Maps & Location 进阶 20m

Apple Maps Server APIs 初探:服务端地图能力补全

Meet Apple Maps Server APIs

2022年6月6日

在 Apple 官方观看视频

一句话判断

Apple 终于把 Maps 的服务端能力补齐了——Geocoding、Search、ETA 这些 Google Maps 早就有的 API,现在 Apple Maps 也有了,但每天 25000 次的调用上限说明 Apple 对这个服务的定位还是「补充」而非「替代」。

这场 Session 讲了什么

Apple Maps 一直以来都是客户端为中心的生态——MapKit 负责 iOS 端,MapKit JS 负责 Web 端,但服务端一直是空白。如果你想在后端做地理编码或者搜索周边商家,要么自己调 Google Maps API,要么在客户端做这些操作然后忍受大量的网络请求。

今年 Apple 推出了四个 Server API:Geocoding(地址转坐标)、Reverse Geocoding(坐标转地址)、Search(搜索地点)、ETA(预估到达时间)。这四个 API 的定位很明确——把原来必须在客户端做的地图操作下沉到服务端,减少客户端和 Apple Maps 之间的网络往返。

Session 用了一个很实际的例子来说明:假设你在做一个店铺定位 App,需要显示三家店铺的地址和距离。没有 Server API 时,客户端要先从你的服务器拿地址列表,然后逐个调 MapKit 做地理编码和距离计算,来回好几次网络请求。有了 Server API 后,你的服务器一次性把这些都处理好,客户端只需一次请求就能拿到完整数据。架构变简洁了,带宽和功耗也省了。

值得深挖的点

认证机制复用 MapKit JS 的 JWT 方案。 Server API 的认证方式和 MapKit JS 完全一致:用开发者账号下载的私钥生成 JWT 格式的 Maps auth token,再通过 Token API 换取有效期 30 分钟的 Maps access token。这个设计的好处是如果你已经在用 MapKit JS,迁移成本几乎为零。但我觉得这个 30 分钟刷新机制是个需要注意的点——你需要做好 token 的缓存和自动刷新,别让用户等着你换 token。

25000 次/天的配额是个信号。 这个数字听起来不少,但对一个稍微有点规模的应用来说其实很容易用完,尤其是 Geocoding + ETA 组合使用的场景。更关键的是,这个配额和 MapKit JS 的服务调用是共享的。我的判断是,Apple 现阶段并不是想和 Google Maps 在服务端 API 市场正面竞争,而是给已经在使用 MapKit 生态的开发者提供一个闭环方案。如果你的用量真的很大,Session 里也说了可以联系他们谈额外配额。

代码片段

用 Geocode API 获取地址的经纬度:

// 将地址 URL 编码后调用 Geocode API
let address = "1 Apple Park Way, Cupertino, CA"
let encodedAddress = address.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!

// 请求 Geocode API
let url = URL(string: "https://maps-api.apple.com/v1/geocode?q=\(encodedAddress)")!
var request = URLRequest(url: url)
request.setValue("Bearer \(mapsAccessToken)", forHTTPHeaderField: "Authorization")

// 响应中包含 latitude 和 longitude
// 注意:Maps access token 需要每 30 分钟刷新一次

用 ETA API 计算到店铺的距离:

// 用 Geocode 拿到的经纬度调 ETA API,最多支持 10 个目的地
let origin = "37.7749,-122.4194"  // 用户位置
let destination = "37.3230,-122.0322"  // 店铺位置
let url = URL(string: "https://maps-api.apple.com/v1/eta?origin=\(origin)&destination=\(destination)")!

// 响应中的 distanceMeters 就是距离
// 注意:ETA API 返回的是实际路径距离,不是直线距离

Token API 换取 access token:

// 用 Maps auth token(JWT)换取 Maps access token
let url = URL(string: "https://maps-api.apple.com/v1/token")!
var request = URLRequest(url: url)
request.setValue("Bearer \(mapsAuthToken)", forHTTPHeaderField: "Authorization")

// 响应中的 expiresInSeconds 表示 token 有效期(1800 秒 = 30 分钟)
// 收到 429 状态码时使用指数退避策略重试,不要立即重试

最佳实践

  • 把地理编码和距离计算这些重复性操作放到服务端做,你的服务器作为网关统一调用 Apple Maps Server API,客户端只需要和你自己的服务器通信。
  • 配好 token 自动刷新机制,在 token 到期前 1-2 分钟就提前刷新,避免请求时 token 过期。
  • 在客户端做好配额耗尽时的降级处理——当服务端返回 429 时,考虑使用 MapKit 在客户端做兜底。
  • 遇到 429 错误时用指数退避策略重试,不要循环重试。
  • 在 Maps developer dashboard 监控你的 API 使用量,别等到被限流了才发现。

还有什么值得关注

  • Server API 的配额和 MapKit JS 共享,如果你两个都在用,要特别注意配额分配。
  • Search API 可以发现商家、兴趣点等,但文档在 Session 之外,需要看 Apple 的详细文档。
  • 整个 Apple Maps 栈(MapKit + MapKit JS + Server API)现在形成了一个完整闭环,对不想混用多套地图服务的团队来说是个不错的选择。
WWDC 2022