1. 始める前に
この Codelab では、SwiftUI で Map SDK for iOS を利用する方法を解説します。
前提条件
- Swift の基本知識
- SwiftUI の基本的な扱いへの習熟
演習内容
- Maps SDK for iOS を有効化して使用し、SwiftUI を使って iOS アプリに Google マップを追加します。
- 地図にマーカーを追加します。
- SwiftUI のビューと
GMSMapView
オブジェクトの間で、相互に状態の受け渡しを行います。
必要なもの
- Xcode 11.0 以降
- 課金が有効になっている Google アカウント
- Maps SDK for iOS
- Carthage
2. 準備
以下の有効化の手順では、Maps SDK for iOS を有効化します。
Google Maps Platform をセットアップする
課金を有効にした Google Cloud Platform アカウントとプロジェクトをまだ作成していない場合は、Google Maps Platform スタートガイドに沿って請求先アカウントとプロジェクトを作成してください。
- Cloud Console で、プロジェクトのプルダウン メニューをクリックし、この Codelab に使用するプロジェクトを選択します。
3. スターター コードをダウンロードする
できるだけ早く演習を開始できるように、この Codelab で使用できるスターター コードが用意されています。すぐに次のステップに進んでも問題ありませんが、ご自身で構築するためのすべての手順を確認したい場合は、最後までお読みください。
git
がインストールされている場合は、リポジトリのクローンを作成します。
git clone https://github.com/googlecodelabs/maps-ios-swiftui.git
あるいは、以下のボタンをクリックしてソースコードをダウンロードすることもできます。
- コードを入手したら、
cd
でstarter/GoogleMapsSwiftUI
ディレクトリに移動します。 carthage update --platform iOS
を実行して、Maps SDK for iOS をダウンロードします。- 最後に、Xcode で
GoogleMapsSwiftUI.xcodeproj
ファイルを開きます。
4. コードの概要
ダウンロードしたスターター プロジェクトには次のクラスが含まれ、あらかじめ実装されています。
AppDelegate
- アプリケーションのUIApplicationDelegate
。ここで Maps SDK for iOS の初期化が行われます。City
- ひとつの都市を表す構造体(都市の名称と座標を含む)。MapViewController
- Google マップ(GMSMapView)を含むシンプルな UIKitUIViewController
。SceneDelegate
-ContentView
のインスタンス化が行われる、アプリケーションのUIWindowSceneDelegate
。
これに加えて以下のクラスが部分的に実装されており、この Codelab 内で実装を完成させることになります。
ContentView
- アプリを格納する最上位の SwiftUI ビュー。MapViewControllerBridge
- UIKit ビューを SwiftUI ビューにブリッジするクラス。具体的には、SwiftUI 内でMapViewController
へのアクセスを実現するクラスです。
5. SwiftUI と UIKit の違い
SwiftUI は、iOS アプリケーションの開発において UIKit を代替する UI フレームワークとして、iOS 13 で導入されました。先代の UIKit と比べると、SwiftUI にはさまざまな利点があります。たとえば次のようなものが挙げられます。
- 状態が変化するとビューが自動的に更新されます。State と呼ばれるオブジェクトにより、含まれる値が変化すると UI が自動的に更新される仕組みです。
- ライブ プレビューによる迅速な開発が可能です。ライブ プレビューにより、Swift UI ビューのプレビューを Xcode で手軽に参照できるため、視覚的な変化を確認するためにコードをビルドしてエミュレータにデプロイする作業は最小限で済むようになっています。
- Source of Truth(信頼できる情報源)は Swift 内にあります。SwiftUI のビューはすべて Swift 内で宣言されるため、従来のように Interface Builder を使う必要はありません。
- UIKit との相互運用性が確保されています。UIKit との相互運用性により、既存のアプリは既存のビューをそのまま活かしつつ、SwiftUI を追加で組み込んでいくことができます。また、Maps SDK for iOS のように SwiftUI に未対応のライブラリも、SwiftUI で使用することができます。
デメリットもいくつかあります。
- SwiftUI は iOS 13 以降でなければ利用できません。
- ビュー階層を Xcode のプレビューで検証することができません。
SwiftUI の状態とデータフロー
SwiftUI では、宣言型アプローチによる新しい形の UI 作成が可能です。ビューの外観とさまざまな状態を SwiftUI に伝えると、あとはシステムが自動的に処理してくれます。もとになっている状態がイベントやユーザー行動により変化すると、SwiftUI が自動的にビューを更新します。この設計は一般に「単方向データフロー」と呼ばれます。本 Codelab ではこの設計の詳細には踏み込みませんが、Apple のドキュメント State and Data Flow を読んで理解を深めることをおすすめします。
UIViewRepresentable または UIViewControllerRepresentable を使った UIKit と SwiftUI のブリッジ
Maps SDK for iOS は UIKit を基盤として設計されており、SwiftUI 対応のビューはまだ提供できません。このため、SwiftUI 内で使用する場合は、UIViewRepresentable
または UIViewControllerRepresentable
への準拠が必要となります。これらのプロトコルはそれぞれ、UIKit で構築された UIView
と UIViewController
を SwiftUI が組み込むことを可能にします。SwiftUI のビューに Google マップを追加すること自体はどちらのプロトコルでも可能ですが、次のステップでは、UIViewControllerRepresentable
を使って地図を含む UIViewController
を組み込む方法を解説します。
6 地図を追加する
このセクションでは、SwiftUI のビューに Google マップを追加します。
API キーを追加する
アプリに表示する地図をアカウントと関連付けるため、ここまでのステップで作成した API キーを、Maps SDK for iOS に提供する必要があります。
API キーを提供するには、AppDelegate.swift
ファイルを開いて application(_, didFinishLaunchingWithOptions)
メソッドを見つけます。現在 SDK は、GMSServices.provideAPIKey()
を介して、「YOUR_API_KEY」という文字列で初期化されています。この文字列を実際の API キーで置き換えましょう。この手順を完了すると、アプリケーション起動時に Maps SDK for iOS が初期化されます。
MapViewControllerBridge を使って Google マップを追加する
API キーを SDK に提供できたら、次はアプリに地図を表示しましょう。
スターター コードで提供されているビュー コントローラ MapViewController
は現在、ビューの中に GMSMapView
を含みます。ただ、これは UIKit で作成されたビュー コントローラなので、ContentView
内で使用できるよう、このクラスを SwiftUI にブリッジする必要があります。手順は次のとおりです。
- ファイル
MapViewControllerBridge
を Xcode で開きます。
このクラスは、UIKit の UIViewController
を SwiftUI のビューとして使用できるようラッピングするために必要なプロトコルである UIViewControllerRepresentable に準拠します。逆に言えば、このプロトコルに準拠すると、UIKit のビューを SwiftUI のビューにブリッジすることが可能です。プロトコルに準拠するためには、次の 2 つのメソッドを実装する必要があります。
makeUIViewController(context)
- もとになるUIViewController
を作成するために SwiftUI が呼び出すメソッドです。UIViewController
をインスタンス化して初期状態を渡す処理をこのメソッドで行います。updateUIViewController(_, context)
- 状態が変化するたびに SwiftUI が呼び出すメソッドです。もとになっているUIViewController
に状態の変化に応じて変更を加える処理を、このメソッドで行います。
MapViewController
を作成します。
関数 makeUIViewController(context)
内で、新しい MapViewController
をインスタンス化し、結果として返します。以上が済むと、MapViewControllerBridge
は次のようになります。
MapViewControllerBridge
import GoogleMaps
import SwiftUI
struct MapViewControllerBridge: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> MapViewController {
return MapViewController()
}
func updateUIViewController(_ uiViewController: MapViewController, context: Context) {
}
}
ContentView で MapViewControllerBridge を使用する
MapViewControllerBridge
が MapViewController
のインスタンスを作成するようになったので、次はこの構造体を ContentView
内で使うことにより地図を表示します。
- ファイル
ContentView
を Xcode で開きます。
SceneDelegate
でインスタンス化された ContentView
に、最上位のアプリケーション ビューが格納されています。地図の追加はこのファイル内から行います。
body
プロパティ内にMapViewControllerBridge
を作成します。
このファイルの body
プロパティ内には、あらかじめ ZStack
が用意され、実装されています。ZStack
には現在、操作可能かつドラッグ可能な都市のリストが含まれています。このリストは後の手順で使用します。ここでは ZStack
内で、ZStack
の最初の子ビューとして MapViewControllerBridge
を作成し、都市のリストのビューの後ろに地図が表示されるようにします。これにより、ContentView
内の body
プロパティの内容は次のようになります。
ContentView
var body: some View {
let scrollViewHeight: CGFloat = 80
GeometryReader { geometry in
ZStack(alignment: .top) {
// Map
MapViewControllerBridge()
// Cities List
CitiesList(markers: $markers) { (marker) in
guard self.selectedMarker != marker else { return }
self.selectedMarker = marker
self.zoomInCenter = false
self.expandList = false
} handleAction: {
self.expandList.toggle()
} // ...
}
}
}
- アプリを実行してみましょう。デバイスの画面に地図が読み込まれ、画面下方にはドラッグ可能な都市のリストが表示されているはずです。
7. 地図にマーカーを追加する
前のステップでは、操作可能な都市のリストとともに地図が表示されるようにしました。このセクションでは、リスト内の各都市に対応するマーカーを追加します。
マーカーを状態(State)として扱う
ContentView
は現在 markers
というプロパティを宣言しており、これは静的プロパティ cities
で宣言されている各都市に対応する GMSMarker
のリストです。このプロパティには SwiftUI プロパティ ラッパー State のアノテーションが付いており、SwiftUI の管理対象に指定されている点に注意しましょう。このため、このプロパティ内で変化(マーカーの追加や削除など)が検出されると、この状態を使用しているビューも更新されます。
ContentView
static let cities = [
City(name: "San Francisco", coordinate: CLLocationCoordinate2D(latitude: 37.7576, longitude: -122.4194)),
City(name: "Seattle", coordinate: CLLocationCoordinate2D(latitude: 47.6131742, longitude: -122.4824903)),
City(name: "Singapore", coordinate: CLLocationCoordinate2D(latitude: 1.3440852, longitude: 103.6836164)),
City(name: "Sydney", coordinate: CLLocationCoordinate2D(latitude: -33.8473552, longitude: 150.6511076)),
City(name: "Tokyo", coordinate: CLLocationCoordinate2D(latitude: 35.6684411, longitude: 139.6004407))
]
/// State for markers displayed on the map for each city in `cities`
@State var markers: [GMSMarker] = cities.map {
let marker = GMSMarker(position: $0.coordinate)
marker.title = $0.name
return marker
}
ContentView
が markers
プロパティを CitiesList
クラスに渡すことにより都市のリストを表示している点に注目しましょう。
CitiesList
struct CitiesList: View {
@Binding var markers: [GMSMarker]
var body: some View {
GeometryReader { geometry in
VStack(spacing: 0) {
// ...
// List of Cities
List {
ForEach(0..<self.markers.count) { id in
let marker = self.markers[id]
Button(action: {
buttonAction(marker)
}) {
Text(marker.title ?? "")
}
}
}.frame(maxWidth: .infinity)
}
}
}
}
Binding を介して State を MapViewControllerBridge に渡す
markers
プロパティのデータを表示している都市のリストに加えて、このプロパティを MapViewControllerBridge
構造体に渡して、地図上にマーカーが表示されるようにします。手順は次のとおりです。
MapViewControllerBridge
内で、@Binding
アノテーションの付いた新しいmarkers
プロパティを宣言する
MapViewControllerBridge
struct MapViewControllerBridge: : UIViewControllerRepresentable {
@Binding var markers: [GMSMarker]
// ...
}
MapViewControllerBridge
で、markers
プロパティを利用するようにupdateUIViewController(_, context)
メソッドを更新する
前のステップで触れたとおり、状態に変化があるたびに、SwiftUI によって updateUIViewController(_, context)
が呼び出されます。markers
内のマーカーが表示されるよう地図を更新する処理は、このメソッド内で行います。そのためには、各マーカーの map
プロパティを更新する必要があります。この手順を完了すると、MapViewControllerBridge
は次のようになります。
import GoogleMaps
import SwiftUI
struct MapViewControllerBridge: UIViewControllerRepresentable {
@Binding var markers: [GMSMarker]
func makeUIViewController(context: Context) -> MapViewController {
return MapViewController()
}
func updateUIViewController(_ uiViewController: MapViewController, context: Context) {
// Update the map for each marker
markers.forEach { $0.map = uiViewController.map }
}
}
markers
プロパティをContentView
からMapViewControllerBridge
に渡す
MapViewControllerBridge
に新しいプロパティを追加したため、このプロパティの値を MapViewControllerBridge
のイニシャライザで受け渡すことが必要になっています。このため、この時点でアプリをビルドしようとしてもコンパイルできません。これを修正するには、MapViewControllerBridge
が作成される ContentView
を更新して、markers
プロパティを渡すようにします。
struct ContentView: View {
// ...
var body: some View {
// ...
GeometryReader { geometry in
ZStack(alignment: .top) {
// Map
MapViewControllerBridge(markers: $markers)
// ...
}
}
}
}
MapViewControllerBridge
が受け取るのはバインド済みのプロパティなので、markers
を渡す際に接頭辞 $
が使用されていることに注目しましょう。$
は Swift プロパティ ラッパー用の予約済み接頭辞で、State に適用した場合、Binding が返されます。
- アプリを実行して、地図にマーカーが表示されることを確認しましょう。
8. 選択した都市への移動をアニメーション表示する
前のステップでは、SwiftUI のビュー間で State を受け渡すことにより、地図にマーカーを追加しました。このステップでは、操作可能なリストでタップされた都市 / マーカーへの移動を、アニメーション付きで表示します。アニメーション表示を行うには、State の変化に対応して地図のカメラ位置を変更します。地図のカメラの概念について詳しくは、Camera and View を参照してください。
選択した都市までアニメーション付きで地図を移動させる
選択した都市までアニメーション付きで地図を移動させる方法は次のとおりです。
MapViewControllerBridge
で新しい Binding を定義する
ContentView
には、selectedMarker
という State プロパティがあります。このプロパティは初期値が nil で、リストから都市が選択されるたびに更新されます。この処理は、ContentView
内の CitiesList
ビューの buttonAction
が担当しています。
ContentView
CitiesList(markers: $markers) { (marker) in
guard self.selectedMarker != marker else { return }
self.selectedMarker = marker
// ...
}
選択したマーカーまでアニメーション付きで地図を移動できるよう、MapViewControllerBridge
は selectedMarker
が変化するたびにそれを状態の変化として検出できる必要があります。よって、MapViewControllerBridge
内で GMSMarker
型の新しい Binding を定義し、プロパティを selectedMarker
と名付けます。
MapViewControllerBridge
struct MapViewControllerBridge: UIViewControllerRepresentable {
@Binding var selectedMarker: GMSMarker?
}
selectedMarker
が変化するたびに地図のアニメーション表示を行うようMapViewControllerBridge
を更新する
新しい Binding を宣言したら、選択したマーカーまでアニメーション付きで地図が移動するよう、MapViewControllerBridge
の updateUIViewController_, context)
関数を更新する必要があります。下のコードをコピーして使用しましょう。
struct MapViewControllerBridge: UIViewControllerRepresentable {
@Binding var selectedMarker: GMSMarker?
func updateUIViewController(_ uiViewController: MapViewController, context: Context) {
markers.forEach { $0.map = uiViewController.map }
selectedMarker?.map = uiViewController.map
animateToSelectedMarker(viewController: uiViewController)
}
private func animateToSelectedMarker(viewController: MapViewController) {
guard let selectedMarker = selectedMarker else {
return
}
let map = viewController.map
if map.selectedMarker != selectedMarker {
map.selectedMarker = selectedMarker
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
map.animate(toZoom: kGMSMinZoomLevel)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
map.animate(with: GMSCameraUpdate.setTarget(selectedMarker.position))
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
map.animate(toZoom: 12)
})
}
}
}
}
}
animateToSelectedMarker(viewController)
関数は、GMSMapView
の animate(with)
関数を使って、地図アニメーションのシーケンスを実行します。
ContentView
のselectedMarker
をMapViewControllerBridge
に渡す
MapViewControllerBridge
で新しい Binding の宣言が済んだら、ContentView
を更新して、MapViewControllerBridge
のインスタンス化の際に selectedMarker
を渡すようにします。
ContentView
struct ContentView: View {
// ...
var body: some View {
// ...
GeometryReader { geometry in
ZStack(alignment: .top) {
// Map
MapViewControllerBridge(markers: $markers, selectedMarker: $selectedMarker)
// ...
}
}
}
}
このステップが完了すると、リスト内で新たな都市を選択するたびに、地図がアニメーション表示されるようになります。
SwiftUI のビューをアニメーション表示して都市を強調する
State 遷移の際のアニメーション処理は SwiftUI が自動的に行うため、ビューをアニメーション表示させるのは簡単です。これを確認するため、地図移動のアニメーションが完了した後、選択した都市にビューをフォーカスさせるアニメーションをさらに追加してみましょう。手順は次のとおりです。
onAnimationEnded
クロージャをMapViewControllerBridge
に追加する
SwiftUI によるアニメーションは、前の手順で追加した地図のアニメーション表示の後で行われるため、MapViewControllerBridge
内で onAnimationEnded
という名の新しいクロージャを宣言し、animateToSelectedMarker(viewController)
メソッド内の最後の地図アニメーションから 0.5 秒の遅延後にクロージャを呼び出します。
MapViewControllerBridge
struct MapViewControllerBridge: UIViewControllerRepresentable {
var onAnimationEnded: () -> ()
private func animateToSelectedMarker(viewController: MapViewController) {
guard let selectedMarker = selectedMarker else {
return
}
let map = viewController.map
if map.selectedMarker != selectedMarker {
map.selectedMarker = selectedMarker
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
map.animate(toZoom: kGMSMinZoomLevel)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
map.animate(with: GMSCameraUpdate.setTarget(selectedMarker.position))
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
map.animate(toZoom: 12)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
// Invoke onAnimationEnded() once the animation sequence completes
onAnimationEnded()
})
})
}
}
}
}
}
MapViewControllerBridge
にonAnimationEnded
を実装する
ContentView
内の MapViewControllerBridge
がインスタンス化される箇所に、onAnimationEnded
クロージャを実装します。下のコードをコピー&ペーストしましょう。zoomInCenter
という名の新しい State を追加するとともに、clipShape
を使ってビューを変更し、zoomInCenter
の値に応じて切り取り図形の直径を変化させる内容になっています。
ContentView
struct ContentView: View {
@State var zoomInCenter: Bool = false
// ...
var body: some View {
// ...
GeometryReader { geometry in
ZStack(alignment: .top) {
// Map
let diameter = zoomInCenter ? geometry.size.width : (geometry.size.height * 2)
MapViewControllerBridge(markers: $markers, selectedMarker: $selectedMarker, onAnimationEnded: {
self.zoomInCenter = true
})
.clipShape(
Circle()
.size(
width: diameter,
height: diameter
)
.offset(
CGPoint(
x: (geometry.size.width - diameter) / 2,
y: (geometry.size.height - diameter) / 2
)
)
)
.animation(.easeIn)
.background(Color(red: 254.0/255.0, green: 1, blue: 220.0/255.0))
}
}
}
}
- アプリを実行して、アニメーションを確認してみましょう。
9. SwiftUI にイベントを送信する
このステップでは、GMSMapView
から発信されるイベントをリッスンし、そのイベントを SwiftUI に送ります。具体的には、地図ビューにデリゲートを設定し、カメラ移動イベントをリッスンすることによって、ある都市がフォーカスされ地図カメラがジェスチャーによって移動した後、地図を見渡しやすいように地図ビューのフォーカスを解除します。
SwiftUI の Coordinator を使用する
GMSMapView
は、カメラ位置の変更やマーカーのタップといったイベントを発信します。こういったイベントをリッスンする仕組みは、GMSMapViewDelegate プロトコルによるものです。SwiftUI では、UIKit ビュー コントローラのデリゲートとして使用する Coordinator という概念が新たに導入されています。よって SwiftUI の世界では、GMSMapViewDelegate
プロトコルへの準拠は Coordinator に担当させます。その方法は次のとおりです。
MapViewControllerBridge
内にMapViewCoordinator
という名の Coordinator を作成する
MapViewControllerBridge
クラス内に入れ子でクラスを作成し、MapViewCoordinator
と名付けます。このクラスは、GMSMapViewDelegate
に準拠するとともに、MapViewControllerBridge
をプロパティとして宣言する必要があります。
MapViewControllerBridge
struct MapViewControllerBridge: UIViewControllerRepresentable {
// ...
final class MapViewCoordinator: NSObject, GMSMapViewDelegate {
var mapViewControllerBridge: MapViewControllerBridge
init(_ mapViewControllerBridge: MapViewControllerBridge) {
self.mapViewControllerBridge = mapViewControllerBridge
}
}
}
MapViewControllerBridge
にmakeCoordinator()
を実装する
次に、MapViewControllerBridge
内に makeCoordinator()
メソッドを実装し、前のステップで作成した MapViewCoodinator
のインスタンスを返します。
MapViewControllerBridge
struct MapViewControllerBridge: UIViewControllerRepresentable {
// ...
func makeCoordinator() -> MapViewCoordinator {
return MapViewCoordinator(self)
}
}
MapViewCoordinator
を地図ビューのデリゲートに設定する
カスタム コーディネータが作成できたので、次はそのコーディネータを、ビュー コントローラの地図ビューのデリゲートに設定します。そのためには、makeUIViewController(context)
でビュー コントローラの初期化を更新します。前のステップで作成したコーディネータには、Context オブジェクトからアクセスできます。
MapViewControllerBridge
struct MapViewControllerBridge: UIViewControllerRepresentable {
// ...
func makeUIViewController(context: Context) -> MapViewController {
let uiViewController = MapViewController()
uiViewController.map.delegate = context.coordinator
return uiViewController
}
- カメラ移動イベントを上方プロパゲートできるよう、
MapViewControllerBridge
にクロージャを追加する
目的はビューを更新してカメラの移動を反映することなので、ブール値を受け付ける新しいクロージャ プロパティを MapViewControllerBridge
内に mapViewWillMove
という名前で宣言し、MapViewCoordinator
内のデリゲート メソッド mapView(_, willMove)
でこのクロージャを呼び出します。SwiftUI のビューがジェスチャー関連のカメラ移動イベントだけに反応できるよう、gesture
の値をクロージャに渡します。
MapViewControllerBridge
struct MapViewControllerBridge: UIViewControllerRepresentable {
var mapViewWillMove: (Bool) -> ()
//...
final class MapViewCoordinator: NSObject, GMSMapViewDelegate {
// ...
func mapView(_ mapView: GMSMapView, willMove gesture: Bool) {
self.mapViewControllerBridge.mapViewWillMove(gesture)
}
}
}
mapWillMove
の値を渡すよう ContentView を更新する
MapViewControllerBridge
で新しいクロージャを宣言したので、ContentView
を更新して新しいクロージャの値を渡します。クロージャ内で、移動イベントがジェスチャーに関係するものである場合は、State zoomInCenter
を false
に切り替えます。これにより、地図がジェスチャーによって移動した場合に、地図を全体表示に戻すことができます。
ContentView
struct ContentView: View {
@State var zoomInCenter: Bool = false
// ...
var body: some View {
// ...
GeometryReader { geometry in
ZStack(alignment: .top) {
// Map
let diameter = zoomInCenter ? geometry.size.width : (geometry.size.height * 2)
MapViewControllerBridge(markers: $markers, selectedMarker: $selectedMarker, onAnimationEnded: {
self.zoomInCenter = true
}, mapViewWillMove: { (isGesture) in
guard isGesture else { return }
self.zoomInCenter = false
})
// ...
}
}
}
}
- アプリを実行して、新しい変更点を確認してみましょう。
10. 完了
これで、この Codelab は完了です!今回の幅広い学習内容をもとに、ぜひご自身でも Maps SDK for iOS を使った SwiftUI アプリの開発に挑戦してみてください。
学習した内容
- SwiftUI と UIKit の違い
- UIViewControllerRepresentable を使って SwiftUI と UIKit をブリッジする方法
- State と Binding を使って地図ビューに変更を加える方法
- Coordinator を使って地図ビューから SwiftUI にイベントを送信する方法
次のステップ
- Maps SDK for iOS - Maps SDK for iOS の公式ドキュメント
- Places SDK for iOS - 現在地周辺のローカル ビジネスやスポットを発見
- maps-sdk-for-ios-samples - Maps SDK for iOS のあらゆる機能を実演したサンプルコード(GitHub 内)
- SwiftUI - SwiftUI に関する Apple の公式ドキュメント
- 皆様のお役に立つコンテンツをご提供できるよう、以下のアンケートにご協力ください。
他にどのような Codelab をご希望ですか。
ご希望の Codelab が上記にない場合、こちらからリクエストしてください。