ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
CalendarとEventKitの説明
ユーザーが時間をよりうまく管理できるようにカレンダーを取り入れる方法を学びましょう。アプリから新しいイベントを作成し、イベントを取得し、バーチャル会議の機能拡張を実装する方法を説明します。また、ユーザーのカレンダーデータのプライバシーを侵害せずにアプリが継続して接続できるようにカレンダーのアクセスレベルを変更する方法もご説明します。
関連する章
- 0:41 - Integrate with Calendar
- 1:52 - Frameworks overview
- 4:36 - Adding events
- 4:56 - Adding: EventKitUI
- 8:07 - Adding: Siri Event Suggestions
- 10:59 - Adding: Write-only
- 12:16 - Adding: EventKit
- 14:23 - Full Access
- 18:01 - Virtual conferences
リソース
- Accessing Calendar using EventKit and EventKitUI
- EventKit
- Explore the Human Interface Guidelines for privacy
- Universal Links for Developers
関連ビデオ
WWDC23
WWDC20
-
ダウンロード
♪♪
こんにちは Adamです この動画では カレンダーと EventKitを使用することで 皆さんのアプリがユーザーの時間管理に 役立つようにする方法を説明します まず アプリとカレンダーを連携させる いくつかの方法と 関連するフレームワークの 概要について説明します 次にこれらのフレームワークを使用して イベントの追加や 完全アクセスによるイベントの取得 バーチャル会議拡張の実装など 一般的な機能を実装する方法についての 具体的な例を紹介します
ユーザーはカレンダーに依存して 時間を追跡したり 将来の予定を立てますが カレンダーにはさまざまな用途があります カレンダーと連携することで アプリは異なる役割を果たすことができます これらの役割がすべて1つにまとまると より豊富なカレンダー体験を 提供することができます 一部のアプリは予約 チケットの購入 会合の手配をサポートします イベントを追加することで それらに関与します 一部のアプリはカスタムカレンダー ウィジェットやプランナーなどの イベントを表示するのに役立ちます
両方のアクションを行うアプリもあります ユーザーがイベントを確認し編集して 予定を管理するのをサポートします
音声通話やビデオ通話をサポートする アプリもカレンダーと連携できます バーチャル会議拡張は カレンダーアプリの体験を 向上するだけでなく 皆さんのアプリにショートカットを 提供します
これらすべてがまとまることで 一貫した時間管理体験を提供します これらの各分野については 後で具体的な例を紹介します
カレンダーの連携に使用できる フレームワークは2つあります EventKitフレームワークは カレンダーデータと直接動作します EventKitUIはiOS Mac Catalystフレームワークで アプリでカレンダーUIを表示するための ビューコントローラーを提供します それぞれを詳しく見てみましょう
EventKitの基本的な タイプから説明します
EKEventStoreは カレンダーデータの主要な接点となります EventStoreはアクセスの リクエストや取得・保存に使用します アプリケーションでは1つのみ使用します EKEventクラスは 特定のイベントをあらわし タイトル 開始日 終了日 場所などの プロパティがあります 各イベントはカレンダーに属しており EKCalendarクラスで表されます カレンダーにはタイトルと色があり イベントを色付けするのに便利です 最後に各カレンダーアカウントは EKSourceによって表されます これはカレンダーのコレクションです ソースはUIでカレンダーを グループ化するのに便利です
EventKitはカレンダーデータを 操作するための基礎的なフレームワークです EventKitUIはEventKitの上に構築され 便利な組み込みのビューを提供します EventKitUIが提供する ビューコントローラーは3つあります EKEventEditViewControllerは イベントエディターを表示します これを使用して新規イベントを追加するか 既存のイベントに変更を加えます EKEventViewControllerは イベントの詳細を表示します これを使用して 既存のイベントについての情報を アプリで表示します EKCalendarChooserは カレンダーリストを表示し 単一または複数の選択をサポートします これを使用すると ユーザーはカレンダーを選択して イベントを追加したり アプリで表示するカレンダーを 選択したりできます
カレンダーは非公開なので システムは許可なしに アプリがカレンダーイベントを書き込んだり 読み込んだりすることを防止します アプリによるカレンダーへの アクセスレベルは3つあります アクセスなし 書き込み専用アクセス または完全アクセスです カレンダーアクセスのないアプリは EventKitUIまたは Siri Event Suggestionsを 使用してイベントを追加できます 書き込み専用アクセスを持つアプリは EventKitで直接イベントを追加できます 完全アクセスのあるアプリは 既存のイベントの取得や変更 既存のカレンダーへのアクセスと 新規カレンダーの作成が行えます
カレンダーを操作するための 最も一般的な方法の1つは 新規イベントを追加することです イベントは異なる方法で カレンダーに追加できます EventKitUIかSiri Event Suggestionsで 一度に1つのイベントを追加できます イベントを直接保存する場合は EventKitを使用します イベントをカレンダーに 追加するための一番簡単な方法は EventKitUIにほとんどの作業を 任せる方法です EKEventEditViewControllerを提示し イベントの詳細が入力されている エディターを表示します これによりユーザーは カレンダーを選択したり イベントを保存する前に その他の変更を行うことができます iOS 17では このUIは 別の処理で実行されます つまりカレンダーへのアクセスを 要求する必要はありません
EventKitUIにイベントを追加するには 4つの手順を踏みます まずイベントストアを作成します 次にイベントを作成し 詳細を入力します 次にビューコントローラーを作成し イベントの編集に構成します 最後にビューコントローラーを提示します コードを使用して 詳細に見てみましょう まずeventStoreを作成します 次にイベントを作成し 詳細を入力します ここで設定した詳細は エディターUIで使用されます エディターが提示されると ユーザーは変更を行うことができます しかし ボタンを押すだけで 確認できるのが理想的です ですので適切な詳細を入力することで ユーザーの時間を節約できます すべてのイベントにはタイトルが必要です タイトルはウィジェットや通知など 多くの場所で使用されるので シンプルなものにしましょう
最も重要なプロパティは 開始日と終了日です 開始日の作成には DateComponentを使用します
開始日を作成したら 期間を追加して終了日を計算します 基盤のカレンダーとDateComponents タイプで日付を計算してください そうしないと夏時間が始まる頃に 予期せぬエラーが発生します このサンプルでは開始日に2時間足します
イベントが特定の時間帯で起こる場合は それも設定することを忘れないでください デフォルトの時間帯は 現在のシステムの時間帯となります
場所を設定し イベントが起こる場所を ユーザーに伝えます 完全な住所を含めるか MapKitハンドルを使用すると マップの提案や出発時間の通知などの 機能を有効にできます 最後にメモを入力して 詳細情報を追加します
イベントプロパティを設定したら 次のステップは EKEvent EditViewControllerの作成です イベントとイベントストアプロパティを 割当てます ユーザーはエディターを使用して イベントの追加やキャンセルを行えます イベントが追加されたかどうかを 確認するには 委譲プロパティを使用して EKEventEditViewDelegate プロトコルを実装します
最後にエディターを提示します この時点では イベントはまだ カレンダーに追加されていません ボタンをタップすることで イベントが保存されます をタップすると 何も 保存されずにエディターが終了します EventKitUIでイベントを追加する 完全な例については 「Accessing Calendar using EventKit and EventKitUI」サンプルコードの 「DropInLessons」ターゲットを ご覧ください アプリで行った予約のイベントを Siri Event Suggestionsで 追加することもできます Siri Event Suggestions APIは Intents Frameworkの一部です Calendarアクセスのプロンプトを 必要とせず UIはアプリに表示されません 代わりにイベントは招待状のように Calendar Inboxに表示されます 表示後にカレンダーに追加するか 無視することができます
Siri Event Suggestionsは レストランやホテルの予約 フライトやレンタルカーの予約 コンサートやスポーツイベントなどの チケット予約をサポートします 予約がキャンセルされたり 変更された場合は イベントを更新することができます
Siri Event Suggestions APIを 使用するには4つの手順を踏みます まずINReservationを作成します 次に 予約をIntentsと 応答にラップします 次にINInteractionを作成します 最後に操作をシステムにドネートします ではいくつかのサンプルコードを 見てみましょう システムが予約を特定するための 一意の参照が必要になります INSpeakableStringインスタンスを 作成し 一意の言語 identifierと 話されたフレーズでこの参照を作成します この表現はSiriとの会話で この予約を参照する際に使用できます
INDateComponentsRangeを使用して この予約の開始時間と終了時間を設定します
CLPlacemarkタイプを使用して このイベントの場所を提供します
次に INReservationのサブクラスの 1つのインスタンスを作成し それをすべて1つにまとめます レストランの予約には INRestaurantReservationを使用します このinitializerにはここには表示されていない オプションの引数がいくつかあり 各サブクラスには 独自のオプションがあります 詳細については ドキュメントをご覧ください
次のステップは 予約の参照を使用して INGetReservationDetailsIntentを 作成することです 次に予約のオブジェクトを使用して INGetReservationDetailsIntent Responseを作成します
次にインテントと応答で INInteractionを作成します
最後に操作のドネートメソッドを 呼び出します
この例はSiri Event Suggestionsを 活用できる ほんのわずかな例です Siri Event Suggestionsを 作成するための詳細については WWDC20「Broaden your reach with Siri Event Suggestions」をご覧ください EventKitUIやSiri Event Suggestionsは イベントの追加において 最高の体験を提供します 書き込み専用アクセスは アプリが カスタム編集UIを表示する必要がある場合 一度に複数のイベントを追加する場合 イベントをユーザー操作なしで 追加したい場合のみに使用します
書き込み専用アクセスを リクエストするには Infoplistに NSCalendarsWriteOnly AccessUsageDescriptionキーを 追加して アプリがアクセスを 必要とする理由を説明します この文字列が要求プロンプトに 表示されます こちらはサンプルアプリのプロンプトです 「選択するカレンダーに 反復のレッスンを保存する」
書き込み専用アクセスには ある制限があります 1つはユーザーにアクセスを拒否する 選択肢があること アクセスが許可されても アプリは 同じアプリから追加されたイベントを含む カレンダーの既存のイベントを 読み込むことができません またアプリはカレンダーリストの読み込みや 新規カレンダーの作成ができません 書き込み専用アクセスは iOS 17と macOS Sonomaの新機能です これが既存のアプリに与える影響の 詳細については 「What's new in Privacy」の 動画をご覧ください
書き込み専用アクセスによる 新規イベントの追加は EventKitUIによる新規イベントの 追加と似ています どちらもイベントストアの作成から 始まります 次に書き込み専用アクセスを リクエストします アクセスが許可されたら 新規イベントを作成し 詳細を入力します 最後にイベントを保存します より詳細に説明しましょう まずイベントストアを作成します requestWriteOnlyAccessToEventsで 書き込み専用アクセスをリクエストします 戻り値によりアクセスが許可されたか どうかがわかります ユーザーはアクセスを拒否できるので その場合は丁寧に対応しましょう ユーザーがアプリのアクセスの 必要性を理解すれば ほとんどの場合アクセスが許可されるので ユーザーがアクセスが必要なアプリを 初めて操作する際にアクセスを リクエストする必要があります 次にイベントを作成し 詳細を入力します ここでも重要な違いがあります EventKitUIを使用する場合 入力した詳細はエディターに表示され 入力されていない内容には デフォルトの値がつきます イベントをEventKitで 直接保存する場合 どの内容も自動的に入力されません 皆さんが設定したものだけが保存されます 入力する必要のあるプロパティが いくつかあります さもないと保存に失敗します
必須のプロパティはカレンダーです イベントストアのdefaultCalendar ForNewEventsプロパティを使用して カレンダーの使用が設定でデフォルトとして 構成されるようにします
タイトル 開始日 終了日も必須の項目です それ以外はすべてオプションとなります しかしできる限り詳細を入力することを おすすめします
詳細の入力が完了したら イベントをイベントストアの 保存メソッドを使用して保存します
EventKitでイベントを追加する 完全な例は 「Accessing Calendar using EventKit and EventKitUI」にあるサンプルプロジェクトで 「RepeatingLessons」ターゲットを ご覧ください
イベントをカレンダーに追加したいアプリは EventKitUI Siri Event Suggestionsまたは 書き込み専用アクセスを使用してください カレンダーデータを読み込む必要のある ごく一部のアプリには 完全アクセスを利用できます
アプリに既存のイベントの表示 更新 削除が必要なコア機能がある場合にのみ 完全アクセスをリクエストします 完全アクセスをリクエストするには Infoplistに NSCalendars FullAccessUsageDescription キーを含みます この文字列が要求プロンプトに 表示されます カレンダーには機密情報が含まれており 完全アクセスへのプロンプトには 含まれているデータの量が説明されます アプリによるカレンダー情報の読み込みを ユーザーが信頼するのは簡単ではありません ユーザーがアプリを信頼しなければ リクエストは拒否される場合があります アプリのコア体験に欠かせない場合にのみ 完全アクセスをリクエストしてください また アクセスが必要な理由が明確である 場合にのみ リクエストしてください
アプリがコア機能に対して 完全アクセスが必要である場合 イベントを取得する必要があるでしょう これを行うには まず イベントストアを作成します 次に完全アクセスをリクエストします 次にpredicate.を作成します 最後にイベントを イベントストアから取得します そのコードについて説明します これまでの例と同じように まずイベントストアを作成します アプリには1つのイベントストアのみが 必要です ですのでこれを再利用します requestFullAccessToEventsメソッドを 呼び出して 完全アクセスを要求します プロンプトが表示され アクセスが 許可されたかどうかを示します 完全アクセスは拒否されることが 多いため うまく対処してください 完全アクセスを取得したら イベントストアの predicateForEventsメソッドを呼出して predicateを作成します predicateはどのイベントを取得するかと 日付範囲およびオプションの カレンダーリストを説明します このコードは当月の範囲を使用しています 最良のパフォーマンスのために 最短の範囲を選択しましょう カレンダーの引数がnilのままの場合 すべてのカレンダーからの結果が 含まれます 最後にpredicateをイベントストアの イベント(マッチング) メソッドに パスしてイベントを取得すると 一致したイベントの配列が返されます この配列のイベントは 順番通りに表示されません 必要に応じてイベントを 並び変えてください イベントの取得に関する 完全な例を見てみたい場合は 「Accessing Calendar using EventKit and EventKitUI」の 「MonthlyEvents」ターゲットを ご覧ください
iOS 17とmacOS Sonoma以前の リリースに対応するには ランタイムの可用性チェックを 一度実行します iOS 17とmacOS Sonoma以降では新しい requestAccessメソッドを呼び出します 旧バージョンのOSでは 古い requestAccessメソッドを呼び出します
iOS 17またはmacOS Sonoma 以前には追加の使用文字列が必要です NSCalendarsUsageDescriptionキーを カレンダーアクセスに含めます EventKitUIを使用するアプリにも NSContactsUsageDescriptionキーを 含める必要があります EventKitUIはアプリに 連絡先のアクセスを要求するからです
アクセスを要求する際に アプリに これらの文字列が欠落している場合 アプリはクラッシュします
これまで イベントの追加方法と 取得方法について説明しましたが カレンダーとの連携は 他の用途でも活用できます アプリが音声通話やビデオ通話に 対応するのなら バーチャル会議拡張を使用すると ユーザーは通話をイベントに直接 追加することができます この拡張を使用する方法は2つあります イベントに場所を追加する場合 バーチャル会議のカスタムオプションが 場所ピッカーに表示されます この例では FaceTimeと Skypeのバーチャル会議拡張が 提供するオプションがあります いずれかをタップすると バーチャル会議がイベントに追加されます バーチャル会議のあるイベントには カスタムの参加オプションが イベント詳細欄に表示されます バーチャル会議拡張の作成は 簡単です まずXcodeで「Virtual Conference Extension」ターゲットを作成します 次に プロトコル拡張で2つのメソッドを 使って実装します fetchAvailableRoomTypesを実装して 利用可能なルームタイプを提供します 次に fetchVirtualConferenceを 実装して 選択したルームタイプのバーチャル会議 オブジェクトを提供します 実演してみましょう
まずXcodeで「Virtual Conference Extension」ターゲットを作成します EKVirtualConferenceProviderの スタブされたサブクラスができます オーバーライドする最初のメソッドは fetchAvailableRoomTypesです 場所ピッカーにルームタイプが表示されます
各ルームタイプに対しタイトルを選択します これはアプリのアイコンの横のUIに 表示されます
また 各ルームタイプに対して 一意のidentifierを選択します identifierはどのルームが選択されたかを Extensionに知らせます
タイトルとidentifierを使用して EKVirtualConferenceRoomTypeDescriptorの インスタンスを作成します アプリが複数のルームタイプに対応する場合 各ルームタイプにインスタンスを作成します 最後にルームタイプの配列を返します
次に実装するメソッドは fetchVirtualConferenceです これはルームタイプの1つが 選択された場合に呼び出されます identifierの引数は どのルームが選択されたかを示します
バーチャル会議には1つ以上のdescriptorがあり これはカレンダーに参加方法を伝えます EKVirtualConferenceURLDescriptorを 作成して URLとオプションのタイトルを開きます
URLにはユニバーサルリングを使用して アプリが直接開かれるのを許可します タイトルは複数の参加オプションを 区別するのに役立ちます ここでは参加方法が1つしかないため 必要ありません
詳細文字列で追加情報を提供します このテキストはイベント詳細UIの 特別バーチャル会議セクションに 表示されます
最後にこれらをすべて1つにまとめて EKVirtualConferenceDescriptorを 作成し 返します ここでのタイトルは複数のルームタイプを 区別するのに役立ちます この例ではルームタイプが1つだけなので タイトルはnilのままです
この2つのメソッドで アプリはカレンダーアプリの 場所ピッカーに バーチャル会議のオプションとして 表示されます
カレンダーとの連携方法を いくつかご紹介したところで 皆さんの アプリがどのように寄与するか考えましょう EventKitUIまたはSiri Event Suggestionsで アクセスの要求なしにイベントを作成 アクセスを要求する必要がある場合は 最小限のアクセスを 必要なときにのみ要求します 音声通話やビデオ通話のアプリの場合は バーチャル会議拡張を実装しましょう 皆さんのアプリがカレンダーと連携するのを 見るのが楽しみです ご視聴ありがとうございます ♪ ♪
-
-
5:49 - Adding an event with EventKitUI
// Create an event store let store = EKEventStore() // Create an event let event = EKEvent(eventStore: store) event.title = "WWDC23 Keynote" let startDateComponents = DateComponents(year: 2023, month: 6, day: 5, hour: 10) let startDate = Calendar.current.date(from: startDateComponents)! event.startDate = startDate event.endDate = Calendar.current.date(byAdding: .hour, value: 2, to: startDate)! event.timeZone = TimeZone(identifier: "America/Los_Angeles") event.location = "1 Apple Park Way, Cupertino, CA, United States" event.notes = "Kick off an exhilarating week of technology and community." // Create a view controller let eventEditViewController = EKEventEditViewController() eventEditViewController.event = event eventEditViewController.eventStore = store eventEditViewController.editViewDelegate = self // Present the view controller present(eventEditViewController, animated: true)
-
9:17 - Siri Event Suggestions
// Create an INReservation let spokenPhrase = “Lunch at Caffè Macs” let reservationReference = INSpeakableString(vocabularyIdentifier: "df9bc3f5", spokenPhrase: spokenPhrase, pronunciationHint: nil) let duration = INDateComponentsRange(start: myEventStart, end: myEventEnd) let location = CLPlacemark(location: myCLLocation, name: "Caffè Macs", postalAddress: myAddress) let reservation = INRestaurantReservation(itemReference: reservationReference, reservationStatus: .confirmed, reservationHolderName: "Jane Appleseed", reservationDuration: duration, restaurantLocation: location) // Create an intent and response let intent = INGetReservationDetailsIntent(reservationContainerReference: reservationReference) let intentResponse = INGetReservationDetailsIntentResponse(code: .success, userActivity: nil) intentResponse.reservations = [reservation] // Create an INInteraction let interaction = INInteraction(intent: intent, response: intentResponse) // Donate the interaction to the system interaction.donate()
-
12:41 - Adding an event with write-only access
// Create an event store let store = EKEventStore() // Request write-only access guard try await store.requestWriteOnlyAccessToEvents() else { return } // Create an event let event = EKEvent(eventStore: store) event.calendar = store.defaultCalendarForNewEvents event.title = "WWDC23 Keynote" event.startDate = myEventStartDate event.endDate = myEventEndDate event.timeZone = TimeZone(identifier: "America/Los_Angeles") event.location = "1 Apple Park Way, Cupertino, CA, United States" event.notes = "Kick off an exhilarating week of technology and community." // Save the event guard try eventStore.save(event, span: .thisEvent) else { return }
-
15:51 - Fetch events
// Create an event store let store = EKEventStore() // Request full access guard try await store.requestFullAccessToEvents() else { return } // Create a predicate guard let interval = Calendar.current.dateInterval(of: .month, for: Date()) else { return } let predicate = store.predicateForEvents(withStart: interval.start, end: interval.end, calendars: nil) // Fetch the events let events = store.events(matching: predicate) let sortedEvents = events.sorted { $0.compareStartDate(with: $1) == .orderedAscending }
-
19:18 - Virtual conference extension
// Create the extension target class VirtualConferenceProvider: EKVirtualConferenceProvider { // Provide the room types override func fetchAvailableRoomTypes() async throws -> [EKVirtualConferenceRoomTypeDescriptor] { let title = "My Room" let identifier = "my_room" let roomType = EKVirtualConferenceRoomTypeDescriptor(title: title, identifier: identifier) return [roomType] } // Provide the virtual conference override func fetchVirtualConference(identifier: EKVirtualConferenceRoomTypeIdentifier) async throws -> EKVirtualConferenceDescriptor { let urlDescriptor = EKVirtualConferenceURLDescriptor(title: nil, url: myURL) let details = "Enter the meeting code 12345 to enter the meeting." return EKVirtualConferenceDescriptor(title: nil, urlDescriptors: [urlDescriptor], conferenceDetails: details) } }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。