Swiftでカレンダーのイベントを取得
iOSやmacOSのカレンダーに入力している予定をアプリから読み込む手順。
予定を読み込むにはユーザの許可が必要
カレンダーの予定はアプリが勝手に読み込むことはできない。写真を読み込む場合のようにユーザの許可が必要になる。
Privacy - Calendars Usage Description
というわけでいつもの様にまずはinfo.plistに読み込みが必要だという情報を入れる必要がある。
カレンダーの予定の読み込みに必要なのはPrivacy - Calendars Usage Description
。TypeはStringで値として読み込みが必要な理由を記述する。
今回は簡単に下記の様にしてみた。
状態を調べてユーザに許可を得る
次はカレンダーの予定の読み込みを許可されているかどうかを確認して、許可されていない場合は許可を得るダイアログを表示するコードを書く。
まずは最初にEKEventStore
クラスのインスタンスを作成しておく。これを使って許可を得たりイベントの一覧を取得したりするs。
self.eventStore = EKEventStore()
次は読み込みが許可されているかどうかを確認するコード。
func authorizationStatus() -> Bool {
let status = EKEventStore.authorizationStatus(for: .event)
switch status {
case .authorized:
print("Authorized")
return true
case .notDetermined:
print("Not determined")
return false
case .restricted:
print("Restricted")
return false
case .denied:
print("Denied")
return false
@unknown default:
print("Unknown default")
return false
}
}
この例ではcase
を全部書いているが.authorized
だけ確認すれば問題ない。(Xcodeが全部書いてくれるのでそのまま使ってる。更に@unknown defaultもワーニングでFixすると入れてくれる!)
上記コードで許可されていない(false
が返ってきた)場合は下記コードで許可を得るダイアログを表示する。
eventStore.requestAccess(to: .event, completion: { granted, error in
if granted {
print("allowed now")
return
}
else {
print("Not allowed")
}
})
ちなみに上記コードでダイアログを表示してユーザが拒否をした場合、一度アプリを終了して再度アプリを起動してもダイアログは表示されなくなるので注意。拒否しているのに毎回ダイアログを表示されるのはユーザとしても鬱陶しいだろう…。
以上でカレンダーの予定を読み込む準備は終了。
カレンダーの予定を読み込む
上記で許可を得る時に使ったEKEventStore
を使ってカレンダーを読み込む。許可を取る時に使ったものと違うEKEventStore
を使うと読み込めないので注意。(違ってもそちらでも許可を得れていれば大丈夫だろうけど確認はしていない)
読み込みはEKEventStore
のevents(matching: NSPredicate)
を使って読み込む。
ここで指定するNSPredicate
は検索条件。EKEventStore.predicateForEvents
で作成できる。
let calendars = eventStore.calendars(for: .event)
let predicate = eventStore.predicateForEvents(withStart: Calendar.current.date(byAdding: .month, value: -3, to: Date())!, end: Date(), calendars: calendars)
上記の例だと日付の範囲をstartDate
とendDate
を使って指定、更に検索をするカレンダーを指定して予定を読み込んでいる。またカレンダーはeventStore.calendars(for: .event)
で全てのカレンダーを指定しているが、検索するカレンダーを指定したい場合は必要なカレンダーの配列を作ってcalendars
に渡せば良い。
ちなみに、考えると当たり前のことだがstartDate
はendDate
より前の日付になる。「今日から3ヶ月前まで」と考えてstartDate
を今日、endDate
を-3
にしても何も読み込めないので注意。(自分はちょっとハマった…)
新規イベントを作成する
必要だったのはイベントを取得することだけだったのだが、せっかくなので新規イベントを作成するコードも書いてみた。
func newEvent(title: String) {
let event = EKEvent(eventStore: eventStore)
event.title = title
event.startDate = Date()
event.endDate = Calendar.current.date(byAdding: .minute, value: 30, to: Date())!
event.calendar = eventStore.defaultCalendarForNewEvents
try! eventStore.save(event, span: .thisEvent)
ここでもやはりEKEventStore
は許可を得たものを使用する。
イベントを削除する
ここまで来たのでイベントを削除するコードも。
try! eventStore.remove(event, span: .thisEvent)
remove(_,span:)
に削除したいイベントを渡すだけ。span
は芋づる式にイベントが繋がっていなければ.thisEvent
で問題ない。
サンプル
簡単なサンプルを作成してみた。
起動するとユーザの許可を得て3ヶ月前までの予定をリスト表示するだけのもの。
New Eventボタンをタップすると新しく30分間のイベントを作成する。
また、イベントをスワイプして削除することも可能。
最後に
カレンダーから予定を取得するのは非常に簡単。なのでサンプル書き始めてつい新しいイベントの作成と削除も加えてしまった。
が、イベントの追加と削除はエラーハンドリングをしっかりやらないとトラブルの元になる。サンプルでは!
で落としているがそうしたくない場合には失敗したことを返してテーブルビューのセルを削除しないようにする必要がある。
というわけで、カレンダーの予定を扱うのは簡単で良いのだが、日付に絡むコードは本当に面倒。タイムゾーンとか年号とか止めてよ!ってなる…
Discussion