WWDC Quick Look 💓 By SwiftGGTeam
Go further with MapKit

Go further with MapKit

元の動画を見る

ハイライト

MapKit は 2025 年に PlaceDescriptor を導入し、Geocoding を CoreLocation から MapKit へ移行、サイクリングルートを追加し、Look Around を MapKit JS と watchOS にも展開します。


主要内容

MapKit には長年、ある種の気まずい問題がありました。地点データは CRM や自社バックエンド、サードパーティのサービスから来るのに、MapKit が認識するのは去年導入された Place ID だけ。Place ID がなければ Apple Maps のリッチデータ(名称、アイコン、営業時間、住所)は手に入りません。開発者に残された選択肢は二つだけでした。MKLocalSearch で一度検索をかけてサードパーティのデータを MapKit Place ID にマッチングさせる(結果が複数返ってくる場合は人手で選ぶ必要あり)か、あるいはリッチデータを諦めて地図上に素っ気ない Marker を自分で描くか。

WWDC25 のこのセッションでは、その糊付け作業を取り除く話が中心になります。Apple は新フレームワーク GeoToolbox の中に PlaceDescriptor を導入しました。座標、住所、サービス識別子(あるいはそれらの組み合わせ)で地点を記述し、MKMapItemRequest に渡して対応する MKMapItem を取得できます。同時に CLGeocoder は非推奨となり、Geocoding は 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 の 3 種類を混在させることもできます。
  • commonName は地点の公開名称(例: Guggenheim Museum)を入れます。ユーザーのプライバシーに関わる情報(例: 「お母さんの家」)を入れるべきではありません。これ自体が地点を見つける手がかりにはならず、主に表示用です。
  • MKMapItemRequest(placeDescriptor:) で descriptor をリクエストに変換し、try await request.mapItem で 1 件の MKMapItem を非同期で受け取ります。MKLocalSearch と異なり、ここでは最も合致する 1 件のみが返ります。

住所しか持っていない場合も同じ要領です(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.MapKit が含まれていれば MKMapItemRequest はそれを優先的に使い、失敗した場合は representations の座標や住所にフォールバックします。
  • この仕組みは App Intents で特に重要です。別の app に PlaceDescriptor を渡したとき、相手は MapKit を使っていないかもしれませんが、辞書から自分の知っている ID を見つけられる、というわけです。

Geocoding は MapKit へ引っ越し

CLGeocoder は iOS 26 で非推奨になり、CLPlacemark もソフト非推奨になりました(10:07)。逆 Geocoding は次のように変わります(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 が返るので、ランタイムでの throw を避けられます。
  • request.mapItems は配列を返しますが、ドキュメント上ほとんどのケースで 1 件しか返らないため、.first を取れば十分です。
  • ここで返る MKMapItem は住所地点の情報のみで、POI(興味地点)のリッチデータは含まれません。「カフェですよ」とは教えてくれず、「○○通りの○番地です」までしか分からない、と割り切ってください。

正方向の Geocoding は MKGeocodingRequest を使い、住所を一発で MKMapItem に変換できます(13:50)。

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

返ってくる MKMapItemaddress: MKAddress? には fullAddressshortAddress の 2 つの文字列プロパティがあります。addressRepresentations: MKAddressRepresentations? はさらに表示戦略を提供します。たとえば fullAddress(includingRegion: false) で国名を除いたり、cityWithContext が「州を補うか国を補うか」を自動で決定したり(Los Angeles, California に対して Paris, France のように)。どちらを使うかは MapKit が住所そのものとデバイスの locale を組み合わせて判断します。

サイクリングルート + Look Around のクロスエンド化

サイクリングは transportType を変えるだけです(16: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 が追加され、サイクリング経路も Apple Watch 上で直接計算できるようになりました。

MapKit JS での Look Around 統合はこちら(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 を使います。ドラッグはできず、クリックするとフルスクリーンのインタラクティブビューが開きます。

重要ポイント

  • やること: アプリ内で「自前で座標 / 住所をこねて Marker を描いている」画面を、すべて PlaceDescriptor に切り替える。

    • なぜやる価値があるか: 現状の Marker には Apple Maps のアイコン、営業時間、電話番号、公式サイトのリンクが付きません。ユーザーがタップしても空気のような情報しか出てきません。MKMapItemRequest(placeDescriptor:) で取得した MKMapItem から Marker を描けば、これらのデータが自動的に表示され、.mapItemDetailSelectionAccessory(.callout) を組み合わせれば公式の Place Card もポップアップ表示できます。
    • 始め方: app 内で Marker(_:, coordinate:)Marker(_:, location:) を使っている箇所をすべて洗い出し、データ層に「(coordinate, name)PlaceDescriptor に変換し、MKMapItem に resolve する」メソッドを 1 つ追加します。リスクの低い画面で先行投入し、導入前後でユーザー滞在時間を比較してみましょう。
  • やること: CLGeocoder から MKReverseGeocodingRequest への移行を評価する。日本語住所では特に。

    • なぜやる価値があるか: CLGeocoder は非推奨となり、将来のいずれかの iOS バージョンで削除されます。今のうちに移行すればコストをコントロールできます。より現実的な動機としては、MKAddressRepresentations のほうが CLPlacemark を使った文字列の手組みに比べて格段にすっきりしている点です。「同じ都道府県の住所一覧では都道府県を省略する」といった処理も、これまで if-else を山ほど書いていたものが .fullAddress(includingRegion: false) 1 行で済みます。
    • 始め方: ユーザーが住所を直接目にする画面(注文の配送先、チェックイン履歴など)を 1 つ選び、新旧 API を並走させて、日本語住所での都道府県・市区町村の並び順や表記の違いを比較してください。差分が許容範囲なら全面展開へ進みます。
  • やること: 短期で確実なリターンとしてサイクリングルートを追加する。

    • なぜやる価値があるか: API の形は自動車とほぼ同じで、transportType = .cycling を 1 行入れるだけです。それでもサイクリングユーザー側の体験は大きく向上します。経路は小道を通ったり危険な区間を避けたりするように引かれ、所要時間の見積もりも正確になります。Apple Watch の MapKit にも同じ機能が追加されており、サイクリング中はスマホを取り出すより手元の時計を見るほうが自然です。
    • 始め方: 既存の経路案内画面に Cycling タブを追加し、watchOS 26 上では「次の区間の距離 + ETA」を表示する独立した小さな widget を用意します。
  • やること: MapKit JS の Look Around で iframe 埋め込みの Google Street View を置き換える。

    • なぜやる価値があるか: 不動産、観光、グルメ系の web プロジェクトは長らく Street View にロックインされていて、UI のトーンやブランドと馴染まないケースが多くありました。Look Around は 360 度のインタラクティブビューを提供し、openDialog などのカスタマイズ可能な options に対応、イベントコールバックで自前のローディングアニメーションやエラー表示を差し込めます。
    • 始め方: まず物件カード上で mapkit.LookAroundPreview による静的プレビューを使い、クリック時にフルスクリーンの LookAround へ切り替えます。error イベントを購読してフォールバック UI を準備しましょう(Look Around が未対応の地域もまだ多いです)。

関連セッション

  • What’s new in MapKit — iOS 26 における MapKit 全体のアップデート総覧。PlaceDescriptor、Geocoding、サイクリングルートの完全な一覧を含みます。
  • What’s new in watchOS — watchOS 26 で追加された 20 以上の MapKit API。サイクリングルートと地図ナビゲーションが Apple Watch 上で初めてネイティブに使えます。
  • What’s new in watchOS 26 — watchOS 26 を開発者視点で見たアップデート。地図、フィットネス、運動シーンを含みます。
  • What’s new in Wallet — Wallet と位置 / 場所連携のアップデート。MapKit Place Card の統一 URL スキームと組み合わせて使えます。

コメント

GitHub Issues · utterances