Xamarin.iOS の EventKit
iOS には、カレンダー アプリケーションとリマインダー アプリケーションという 2 つのカレンダー関連アプリケーションが組み込まれています。 カレンダー アプリケーションを使ってカレンダー データを管理する方法を理解するのは簡単ですが、リマインダー アプリケーションはそれほどわかりやすくありません。 リマインダーには、期限や完了日などの日付を関連付けることができます。そのため、iOS では、カレンダー イベントであれリマインダーであれ、すべてのカレンダー データは "カレンダー データベース" という 1 つの場所に格納されます。
EventKit フレームワークは、カレンダー データベースが保存しているカレンダー、カレンダー イベント、およびリマインダー データにアクセスする方法を提供します。 カレンダーおよびカレンダー イベントへのアクセスは iOS 4 以降で利用可能になっていますが、リマインダーへのアクセスは iOS 6 での新機能です。
このガイドでは、以下について取り扱います。
- EventKit の基本 – ここでは、主要なクラスを通じて EventKit の基本部分を紹介し、その使用方法を理解していきます。 このセクションは、ドキュメントの次の部分に取り組む前に読む必要があります。
- 一般的なタスク – 一般的なタスクのセクションは、カレンダーの列挙、カレンダー イベントとリマインダーの作成や保存と取得、カレンダー イベントの作成と変更のための組み込みコントローラーの使用など、一般的な操作を行う方法のクイック リファレンスとしての使用を目的としています。 このセクションは、特定のタスクの参照を目的としているため、最初から順番に読む必要はありません。
このガイドのすべてのタスクには、対応するサンプル アプリケーションが用意されています。
要件
EventKit は iOS 4.0 で導入されていましたが、リマインダー データへのアクセスは iOS 6.0 で導入されたものです。 そのため、一般的な EventKit 開発を行うには、ターゲットとして少なくともバージョン 4.0 (リマインダーの使用には 6.0) が必要となります。
さらに、シミュレーターではリマインダーのアプリケーションを使用できません。つまり、最初に追加しない限り、リマインダー データも使用できません。 加えて、アクセス要求は、実際のデバイス上のユーザーにのみ表示されます。 そのため、EventKit 開発のテストは、デバイス上で行なうのが最良です。
イベント キットの基本
EventKit を使用する際には、一般的なクラスとその使用方法を把握することが重要です。 これらのクラスはすべて、EventKit
および EventKitUI
(EKEventEditController
向け) 内で見つかります。
EventStore
EventStore クラスは EventKit での任意の操作を実行するために必要となる、EventKit 内で最も重要なクラスです。 これは、すべての EventKit データの永続ストレージ、つまりデータベース エンジンだと考えることができます。 EventStore
からは、カレンダー アプリケーションのカレンダーとカレンダー イベントの両方にアクセスでき、またリマインダー アプリケーションのリマインダーにもアクセスできます。
EventStore
はデータベース エンジンに似ているため、その寿命は長くなります。つまり、この作成および破棄の回数は、アプリケーション インスタンスの存続期間内で可能な限り少なくする必要があります。 実際には、アプリケーション内に EventStore
のインスタンスを 1 つ作成したら、それについての参照は (以後必要としないことを確認できる場合を除き)、アプリケーションの存続期間全体にわたって保持することをお勧めします。 さらに、すべての呼び出しは単一の EventStore
インスタンスに向かう必要があります。 このため、使用可能なインスタンスを 1 つに保つために、シングルトンのパターンが推奨されます。
イベント ストアを作成する
次のコードは、EventStore
クラスのインスタンスを 1 つ作成し、アプリケーションの内部から静的に使用できるようにするための、効率的な方法を示しています。
public class App
{
public static App Current {
get { return current; }
}
private static App current;
public EKEventStore EventStore {
get { return eventStore; }
}
protected EKEventStore eventStore;
static App ()
{
current = new App();
}
protected App ()
{
eventStore = new EKEventStore ( );
}
}
上記のコードでは、シングルトン パターンを使用して、アプリケーションが読み込まれる際に、EventStore
のインスタンスをインスタンス化します。 その後は、次のように、アプリケーション内から EventStore
に対し、グローバルにアクセスできます。
App.Current.EventStore;
ここでの例はすべてこのパターンを使用しているため、App.Current.EventStore
経由で EventStore
が参照されていることに注意してください。
カレンダーとリマインダー データへのアクセスを要求する
EventStore を介してデータへのアクセス許可を取得する前に、アプリケーションはまず、カレンダー イベント データまたはアラーム データの必要な方に対するアクセスを要求する必要があります。 これを容易にするために、EventStore
は RequestAccess
という名前のメソッドを公開しています。このメソッドは (呼び出された際に)、カレンダー データまたはアラーム データのどちらかへのアクセスをアプリケーションが要求していることを (どの EKEntityType
が渡されたかに応じて)、ユーザーに通知するアラート ビューを表示します。 この呼び出しは非同期であり、これによりアラート ビューが立ち上がるため、NSAction
(またはラムダ) として渡された完了ハンドラーが呼び出されます。このハンドラーでは、アクセスが許可されたかどうかのブール値と NSError
、null でない場合は要求内のエラー情報を含んでいる、2 つのパラメータを受け取ります。 たとえば、次のコードでは、カレンダー イベント データへのアクセスを要求し、その要求が許可されない場合はアラート ビューを表示します。
App.Current.EventStore.RequestAccess (EKEntityType.Event,
(bool granted, NSError e) => {
if (granted)
//do something here
else
new UIAlertView ( "Access Denied",
"User Denied Access to Calendar Data", null,
"ok", null).Show ();
} );
一度要求が許可された後は、アプリケーションがデバイスにインストールされている限り、その許可は記憶され、ユーザーにアラートがポップアップ表示さることはありません。 ただし、このアクセス権は、カレンダー イベントまたはリマインダーどちらかの、許可された種類のリソースにのみ付与されます。 アプリケーションが両方にアクセスする必要がある場合は、両方に対し要求する必要があります。
アクセス許可は記憶され、毎回要求を行っても比較的コストは小さいため、操作を実行する前には、常にアクセスを要求することをお勧めします。
さらに、完了ハンドラーは別の (UI 以外の) スレッドで呼び出されるため、完了ハンドラー内の UI に対するすべての更新は、InvokeOnMainThread
を介して 呼び出される必要があります。それ以外の場合は例外がスローされ、これがキャッチされない場合はそのアプリケーションはクラッシュします。
EKEntityType
EKEntityType
は、EventKit
の項目またはデータの型を記述する列挙体です。 これには、2 つの値 (Event
と Reminder) があります。 この列挙型は、アクセスまたは取得する対象データの種類を EventKit
に知らせる EventStore.RequestAccess
など、さまざまなメソッドで使用されます。
EKCalendar
EKCalendar は、カレンダー イベントのグループを含んでいるカレンダーを表します。 カレンダーは、iCloud、または Exchange Server や Google などのサード パーティのプロバイダーの場所など、さまざまな場所でローカルに保存できます。イベントを検索する場所や、イベントを保存する場所を EventKit
に知らせるためなど、EKCalendar
は多くの回数使用されます。
EKEventEditController
EKEventEditController は、カレンダー イベントの編集または作成に使用できる組み込みのコントローラーで、EventKitUI
名前空間にあります。 組み込みのカメラ コントローラーと同様に、EKEventEditController
は、UI の表示と保存の処理での複雑な作業を行ないます。
EKEvent
EKEvent はカレンダー イベントを表します。 EKEvent
と EKReminder
の両方とも EKCalendarItem
から継承されており、Title
、Notes
その他のフィールドを持っています。
EKReminder
EKReminder はリマインダーの項目を表します。
EKSpan
EKSpan は、繰り返し発生する可能性があるイベントを変更するときのイベントのスパンを記述する列挙型であり、ThisEvent と FutureEvents の 2 つの値を持ちます。 ThisEvent
は、参照されている系列内の特定のイベントに対してのみ変更が行われるのに対し FutureEvents
では、そのイベントと、今後繰り返されるすべてのイベントに影響を与えます。
タスク
使いやすさのために、次のセクションの説明では、EventKit の使用方法を一般的なタスクに細分化しています。
カレンダーを列挙する
デバイス上でユーザーにより構成されたカレンダーを列挙するには、EventStore
で GetCalendars
を呼び出 し、受信するカレンダーの型 (リマインダーまたはイベント) を渡します。
EKCalendar[] calendars =
App.Current.EventStore.GetCalendars ( EKEntityType.Event );
組み込みコントローラーを使用してイベントを追加または変更する
カレンダー アプリケーションの使用時にユーザーに表示されるのと同じ UI でイベントを作成または編集する場合、EKEventEditViewController により多くの作業が行われます。
使用するには、これをクラス レベルの変数として宣言し、メソッド内で宣言された場合にガベージ コレクションを受け取らないようにします。
public class HomeController : DialogViewController
{
protected CreateEventEditViewDelegate eventControllerDelegate;
...
}
その後これを起動するには、インスタンス化を行ない、EventStore
への参照を付与し、EKEventEditViewDelegate の委任を接続 して、PresentViewController
によりこれを表示します。
EventKitUI.EKEventEditViewController eventController =
new EventKitUI.EKEventEditViewController ();
// set the controller's event store - it needs to know where/how to save the event
eventController.EventStore = App.Current.EventStore;
// wire up a delegate to handle events from the controller
eventControllerDelegate = new CreateEventEditViewDelegate ( eventController );
eventController.EditViewDelegate = eventControllerDelegate;
// show the event controller
PresentViewController ( eventController, true, null );
必要に応じて、イベントを事前に設定したい場合は、まったく新しいイベントを (次のように) 作成するか、保存されたイベントを取得できます。
EKEvent newEvent = EKEvent.FromStore ( App.Current.EventStore );
// set the alarm for 10 minutes from now
newEvent.AddAlarm ( EKAlarm.FromDate ( DateTime.Now.AddMinutes ( 10 ) ) );
// make the event start 20 minutes from now and last 30 minutes
newEvent.StartDate = DateTime.Now.AddMinutes ( 20 );
newEvent.EndDate = DateTime.Now.AddMinutes ( 50 );
newEvent.Title = "Get outside and exercise!";
newEvent.Notes = "This is your reminder to go and exercise for 30 minutes.”;
UI を事前に設定したい場合は、コントローラーでの Event プロパティの設定が必要です。
eventController.Event = newEvent;
既存のイベントを使用するには、後の「ID でイベントを取得する」セクションを参照してください。
委任は、ユーザーがダイアログを終了したときにコントローラーによって呼び出される、Completed
メソッドをオーバーライドする必要があります。
protected class CreateEventEditViewDelegate : EventKitUI.EKEventEditViewDelegate
{
// we need to keep a reference to the controller so we can dismiss it
protected EventKitUI.EKEventEditViewController eventController;
public CreateEventEditViewDelegate (EventKitUI.EKEventEditViewController eventController)
{
// save our controller reference
this.eventController = eventController;
}
// completed is called when a user eith
public override void Completed (EventKitUI.EKEventEditViewController controller, EKEventEditViewAction action)
{
eventController.DismissViewController (true, null);
}
}
}
必要に応じ委任では、Completed
メソッドの Action を確認 してイベントを変更および再保存したり、取り消された場合に他の操作を行ったりできます。
public override void Completed (EventKitUI.EKEventEditViewController controller, EKEventEditViewAction action)
{
eventController.DismissViewController (true, null);
switch ( action ) {
case EKEventEditViewAction.Canceled:
break;
case EKEventEditViewAction.Deleted:
break;
case EKEventEditViewAction.Saved:
// if you wanted to modify the event you could do so here,
// and then save:
//App.Current.EventStore.SaveEvent ( controller.Event, )
break;
}
}
プログラムでイベントを作成する
コード内でイベントを作成するには、EKEvent
クラスで FromStore ファクトリ メソッドを使用し、そのクラスにデータを設定します。
EKEvent newEvent = EKEvent.FromStore ( App.Current.EventStore );
// set the alarm for 10 minutes from now
newEvent.AddAlarm ( EKAlarm.FromDate ( DateTime.Now.AddMinutes ( 10 ) ) );
// make the event start 20 minutes from now and last 30 minutes
newEvent.StartDate = DateTime.Now.AddMinutes ( 20 );
newEvent.EndDate = DateTime.Now.AddMinutes ( 50 );
newEvent.Title = "Get outside and do some exercise!";
newEvent.Notes = "This is your motivational event to go and do 30 minutes of exercise. Super important. Do this.";
イベントを保存するカレンダーを設定する必要がありますが、特に希望がない場合は、既定値のまま使用できます。
newEvent.Calendar = App.Current.EventStore.DefaultCalendarForNewEvents;
イベントを保存するには、EventStore
で SaveEvent メソッドを 呼び出します。
NSError e;
App.Current.EventStore.SaveEvent ( newEvent, EKSpan.ThisEvent, out e );
イベントが保存された後、 EventIdentifier プロパティは一意の識別子で更新され、これは後にイベントを取得するために使用できます。
Console.WriteLine ("Event Saved, ID: " + newEvent.CalendarItemIdentifier);
EventIdentifier
は文字列形式の GUID です。
プログラムでリマインダーを作成する
コード内でのリマインダーの作成は、カレンダー イベントの作成とほぼ同じです。
EKReminder reminder = EKReminder.Create ( App.Current.EventStore );
reminder.Title = "Do something awesome!";
reminder.Calendar = App.Current.EventStore.DefaultCalendarForNewReminders;
保存するには、EventStore
で SaveReminder メソッドを 呼び出します。
NSError e;
App.Current.EventStore.SaveReminder ( reminder, true, out e );
ID でイベントを取得する
イベントを ID で取得するには、EventStore
で EventFromIdentifier メソッドを使用し、これにイベントからプルされた EventIdentifier
を渡 します。
EKEvent mySavedEvent = App.Current.EventStore.EventFromIdentifier ( newEvent.EventIdentifier );
イベントには他に 2 つの識別子プロパティがありますが、取得のために機能するのは EventIdentifier
のみです。
ID でリマインダーを取得する
リマインダーを取得するには、EventStore
で GetCalendarItem メソッドを使用 して、これに CalendarItemIdentifier を渡します。
EKCalendarItem myReminder = App.Current.EventStore.GetCalendarItem ( reminder.CalendarItemIdentifier );
GetCalendarItem
は EKCalendarItem
を返すので、これを EKReminder
にキャストする必要があります (リマインダー データに アクセスする必要がある場合、または後でこのインスタンスを EKReminder
として使用する必要がある場合)。
記述する際に、GetCalendarItem
をカレンダーのイベントには使用 しないでください。これは機能しません。
イベントを削除する
カレンダー イベントを削除するには、使用している EventStore
でRemoveEvent を呼び出 し、対象のイベントへの参照と、適切な EKSpan
を渡します。
NSError e;
App.Current.EventStore.RemoveEvent ( mySavedEvent, EKSpan.ThisEvent, true, out e);
ただし、イベントが削除された後、そのイベントへの参照は null
になる点に注意してください。
リマインダーを削除する
リマインダーを削除するには、EventStore
で RemoveReminder を呼び出し、そのリマインダーへの参照を渡します。
NSError e;
App.Current.EventStore.RemoveReminder ( myReminder as EKReminder, true, out e);
上記のコードには、取得に GetCalendarItem
が使用されたため、EKReminder
へのキャストがあることに注意してください
イベントを検索する
カレンダー イベントを検索するには、EventStore
上の PredicateForEvents メソッドを 使用して、NSPredicate オブジェクトを作成する必要があります。 NSPredicate
は、iOS が一致を検索するために使用するクエリ データ オブジェクトです。
DateTime startDate = DateTime.Now.AddDays ( -7 );
DateTime endDate = DateTime.Now;
// the third parameter is calendars we want to look in, to use all calendars, we pass null
NSPredicate query = App.Current.EventStore.PredicateForEvents ( startDate, endDate, null );
NSPredicate
を作成したら、EventStore
の EventsMatching メソッドを 使用します。
// execute the query
EKCalendarItem[] events = App.Current.EventStore.EventsMatching ( query );
このクエリは同期 (ブロック) であり、クエリによっては長い時を要する可能性があるため、新しいスレッドまたはタスクをスピン アップした方が良い場合もあります。
リマインダーを検索する
リマインダーの検索はイベントに対するのと類似です。これには述語が必要ですが、元から呼び出しは非同期であるため、スレッドのブロックについて心配する必要はありません。
// create our NSPredicate which we'll use for the query
NSPredicate query = App.Current.EventStore.PredicateForReminders ( null );
// execute the query
App.Current.EventStore.FetchReminders (
query, ( EKReminder[] items ) => {
// do someting with the items
} );
まとめ
このドキュメントでは、EventKit フレームワークの重要な部分と、最も一般的なタスクの両方について、概要を説明しました。 ただし、EventKit フレームワークは非常に大規模で強力であり、ここでは導入されていない機能も包含されています。それらの機能にはバッチ更新、アラームの構成、イベントの繰り返しの構成、カレンダー データベースでの変更の登録とリッスン、GeoFences の設定などがあります。 詳細情報については、Apple の「カレンダーとリマインダーのプログラミング ガイド」を参照してください。