WWDC Quick Look 💓 By SwiftGGTeam
Go further with MapKit

Go further with MapKit

观看原视频

Highlight

MapKit 在 2025 推出 PlaceDescriptor、把地理编码从 CoreLocation 搬到 MapKit、新增骑行路线,并把 Look Around 带到 MapKit JS 与 watchOS。


核心内容

MapKit 一直有个尴尬的问题。你的地点数据来自 CRM、自家后端、第三方服务,但 MapKit 只认自己去年推出的 Place ID。如果没有 Place ID,你就拿不到 Apple Maps 的富数据——名字、图标、营业时间、地址。开发者只剩两条路:要么先用 MKLocalSearch 做一遍搜索把第三方数据匹配回 MapKit Place ID(结果可能有多条,还得人工挑),要么干脆放弃富数据,自己在地图上画个光秃秃的 Marker。

WWDC25 这一场把这层胶水干掉了。Apple 推出 PlaceDescriptor,放在新框架 GeoToolbox 里。它允许你用坐标、地址或服务标识符(甚至同时提供多种)来描述一个地点,然后交给 MKMapItemRequest 去找到对应的 MKMapItem。同时,CLGeocoder 被废弃,地理编码改用 MKReverseGeocodingRequestMKGeocodingRequest,返回的 MKMapItem 上带着新增的 MKAddressMKAddressRepresentations,地址格式化不再需要自己拼字符串。骑行路线作为新的 transportType 加入,超过 20 个 MapKit API 第一次登陆 watchOS,Look Around 街景登陆 MapKit JS。一次会议覆盖了 Apple Maps 平台跨端化的所有关键拼图。


详细内容

PlaceDescriptor:用你已有的信息找到 Apple Maps 的富数据

最常见的场景:你只有一个坐标和名字,但想拿到 Apple Maps 的富数据。在 SwiftUI Map 里用坐标画 Marker 很简单,但那是个没有 Apple Maps 数据的空 Marker(04:49):

// Putting Marker on the Map with a coordinate

let annaLiviaCoordinates = CLLocationCoordinate2D(
    latitude: 53.347673,
    longitude: -6.290198
)
var body: some View {
    Map {
       Marker(
            "Anna Livia Fountain",
            coordinate: annaLiviaCoordinates
        )
    }
}

关键点:

  • CLLocationCoordinate2D 只是一个经纬度对,没有任何与 Apple Maps 数据关联的信息。
  • Marker 用这个坐标画出来的图钉,名字是你自己传的字符串,没有 Apple Maps 的图标、颜色和点击后的富数据。

PlaceDescriptor 把已有信息打包成结构化描述(05:07):

// Creating and resolving a PlaceDescriptor with coordinate PlaceRepresentation

import GeoToolbox
import MapKit

let annaLiviaCoordinates = CLLocationCoordinate2D(
    latitude: 53.347673,
    longitude: -6.290198
)
let annaLiviaDescriptor =  PlaceDescriptor(
    representations: [.coordinate(annaLiviaCoordinates)],
    commonName: "Anna Livia Fountain"
)

let request = MKMapItemRequest(placeDescriptor: annaLiviaDescriptor)
do {
    annaLiviaMapItem = try await request.mapItem
} catch {
    print("Error resolving placeDescriptor: \(error)")
}

关键点:

  • import GeoToolbox 引入新框架,PlaceDescriptor 类型在这里定义。
  • representations 数组按精确度从高到低排序,可以混合 .coordinate.address.deviceLocation 三种类型。
  • commonName 是地点的公开名称(如 Guggenheim Museum),不应该填用户隐私数据(如「妈妈家」),它本身不会帮 MapKit 找到地点,主要用于显示。
  • MKMapItemRequest(placeDescriptor:) 把 descriptor 转成请求,try await request.mapItem 异步返回单个 MKMapItem——与 MKLocalSearch 不同,这里只返回一个最佳匹配。

如果只有地址也行(05:56):

let address = "121-122 James's St, Dublin 8"
let descriptor =  PlaceDescriptor(
    representations: [.address(address)],
    commonName: "Obelisk Fountain"
)
let request = MKMapItemRequest(placeDescriptor: descriptor)
obeliskFountain = try await request.mapItem

关键点:

  • .address(_:) 用一个完整的邮政地址作为表示,地址越完整匹配越准。
  • 整个调用形态跟坐标版本一致,差别只在 representation 类型——表明 representations 数组是 PlaceDescriptor 的核心抽象。

跨服务场景下,supportingRepresentations 可以同时挂多家服务的标识符(06:45):

let identifiers = ["com.apple.MapKit" : "ICBB5FD7684CE949"]
let annaLiviaDescriptor =  PlaceDescriptor(
    representations: [.coordinate(annaLiviaCoordinates)],
    commonName: "Anna Livia Fountain",
    supportingRepresentations: [.serviceIdentifiers(identifiers)]
)

关键点:

  • .serviceIdentifiers(_:) 接受一个字典,key 是服务的 bundle identifier,value 是该服务给这个地点的 ID。
  • 如果字典里有 com.apple.MapKitMKMapItemRequest 会优先用它去取数据;失败时自动回退到 representations 里的坐标或地址。
  • 这套机制对 App Intents 特别重要:你给另一个 app 传 PlaceDescriptor 时,对方可能并不用 MapKit,但能从字典里找到自己认识的那个 ID。

Geocoding 搬家到 MapKit

CLGeocoder 在 iOS 26 被废弃,CLPlacemark 被软废弃(10:07)。反向地理编码改成(10:45):

import MapKit

let millCreekCoordinates = CLLocation(latitude: 39.042617, longitude: -94.587526)
if let request = MKReverseGeocodingRequest(location: millCreekCoordinates) {
    do {
        let mapItems = try await request.mapItems
        millCreekMapItem = mapItems.first
    } catch {
        print("Error reverse geocoding location: \(error)")
    }
}

关键点:

  • MKReverseGeocodingRequest(location:) 的初始化器返回可选值——如果传进无效坐标会直接返回 nil,省掉 runtime 抛错。
  • request.mapItems 返回一个数组,文档说绝大多数情况只有一项,取 .first 即可。
  • 这里返回的 MKMapItem 只包含地址点信息,不带 POI(兴趣点)富数据——不要指望它告诉你这是一家咖啡馆,它只告诉你这是哪条街多少号。

正向地理编码用 MKGeocodingRequest,地址变 MKMapItem 一步到位(13:50):

let request = MKGeocodingRequest(
    addressString: "1 Ferry Building, San Francisco"
)
mapItem = try await request?.mapItems.first

返回的 MKMapItem 上的 address: MKAddress?fullAddressshortAddress 两个字符串属性;addressRepresentations: MKAddressRepresentations? 提供更多展示策略,比如 fullAddress(includingRegion: false) 去掉国家、cityWithContext 自动决定补州还是补国家(Los Angeles, California vs. Paris, France)——具体用哪种,MapKit 会综合地址本身和设备 locale 做判断。

骑行路线 + Look Around 跨端化

骑行只需改 transportType16:06):

let request = MKDirections.Request()
request.source = MKMapItem.forCurrentLocation()
request.destination = selectedItem
request.transportType = .cycling
let directions = MKDirections(request: request)
let response = try await directions.calculate()
returnedRoutes = response.routes

关键点:

  • .cycling 是新的 transport type,跟 .walking.automobile 平级。
  • 骑行路线会利用驾车不可用的小路和绿道,并跳过不适合骑行的道路。
  • 同样的请求形态在 MapKit JS 用 mapkit.Directions.Transport.Cycling 也能拿到。
  • watchOS 26 SDK 新带了 20+ 个 MapKit API,骑行路径终于能在手表上直接算。

Look Around 在 MapKit JS 上的接入(18:10):

const placeLookup = new mapkit.PlaceLookup();
const place = await new Promise(
    resolve => placeLookup.getPlace(
        "IBE1F65094A7A13B1",
        (error, result) => resolve(result)
    )
);

const lookAround = new mapkit.LookAround(
    document.getElementById("container"),
    place,
    options
);

关键点:

  • mapkit.PlaceLookup().getPlace(id, callback) 用 Place ID 取 place 对象,回调风格。
  • new mapkit.LookAround(container, place, options) 把交互式街景注入指定 DOM 元素。
  • options 支持 openDialog(加载即全屏)、showsDialogControl(提供进出全屏的按钮)、showsCloseControl(提供关闭按钮)。
  • 静态预览用 mapkit.LookAroundPreview,不可拖动,点击后弹出全屏交互式视图。

核心启发

  • 做什么:把 App 里所有「自己拼坐标 / 地址 + 自己画 Marker」的页面切到 PlaceDescriptor。

    • 为什么值得做:你现在的 Marker 不带 Apple Maps 的图标、营业时间、电话、官网链接;用户点开看到的是个空气。换成 MKMapItemRequest(placeDescriptor:) 拿到的 MKMapItem 后再画 Marker,所有这些数据自动出现,外加 .mapItemDetailSelectionAccessory(.callout) 就能弹出官方 Place Card。
    • 怎么开始:找出 app 里所有用 Marker(_:, coordinate:)Marker(_:, location:) 的位置,在数据层加一个把 (coordinate, name)PlaceDescriptor 再 resolve 成 MKMapItem 的方法。先在一个低风险页面试,对比前后用户停留时长。
  • 做什么:评估 CLGeocoder -> MKReverseGeocodingRequest 的迁移,特别是中文地址。

    • 为什么值得做:CLGeocoder 已废弃,未来某个 iOS 版本会被移除;现在迁移成本可控。但更现实的动机是 MKAddressRepresentationsCLPlacemark 自己拼字符串清晰得多——比如「列表里同省地址省略省份」这种场景以前要写一堆 if-else,现在 .fullAddress(includingRegion: false) 一行解决。
    • 怎么开始:找一个用户能直接看到地址的页面(订单收货地址、打卡记录),并行跑新旧 API,对比中文地址里省/市/区的顺序与简繁差异。如果差异可接受就推全量。
  • 做什么:骑行路线作为短平快收益加进去。

    • 为什么值得做:API 形态跟驾车几乎一样,只改一行 transportType = .cycling,但对骑行用户体验提升明显——骑行路径会走小路、避开不安全路段,估算时间也更准。Apple Watch 上的 MapKit 同样新增了这一能力,骑行场景下看手表比掏手机自然。
    • 怎么开始:在现有路线规划界面加一个 Cycling tab;watchOS 26 上做一个独立的小 widget 显示「下一段骑行的距离 + ETA」。
  • 做什么:MapKit JS Look Around 替换 iframe 嵌入的 Google Street View。

    • 为什么值得做:房产、旅游、餐饮 web 项目长期被 Street View 锁定,UI 风格和品牌不匹配。Look Around 提供 360 度交互视图,支持 openDialog 等可定制 options,事件回调允许接入自家加载动画和错误提示。
    • 怎么开始:先用 mapkit.LookAroundPreview 在房源卡片上做静态预览;点击后再切到全屏 LookAround。监听 error 事件准备好回退 UI(很多地区还没有 Look Around 覆盖)。

关联 Session

  • What’s new in MapKit — MapKit 在 iOS 26 的全部更新一览,包括 PlaceDescriptor、Geocoding、骑行路线的完整列表。
  • What’s new in watchOS — watchOS 26 新增 20+ 个 MapKit API,骑行路线和地图导航第一次在手表上原生可用。
  • What’s new in watchOS 26 — 围绕 watchOS 26 的开发者视角更新,包含地图、健身与运动场景。
  • What’s new in Wallet — Wallet 与位置/场所交互的更新,跟 MapKit Place Card 的统一 URL 方案配合使用。

评论

GitHub Issues · utterances