Sponsor Link
環境&対象
- macOS Ventura 13.4 Beta
- Xcode 14.3
- iOS 16.4 beta
Reminderアプリ
Reminder アプリは、iOS / macOS に付属してくる以下のアプリです。

いわゆる TODO 項目を管理するためのアプリです。
Reminder にプログラムからアクセスする
Reminder アプリに登録した TODO 項目へ プログラム的にアクセスしていきます。
EventKit
Reminder のデータへアクセスするために、EventKit を使用します。
通常、EventKit はカレンダーに登録した要素へのアクセスに使用されますが、Reminder へのアクセスにも EventKit が使用されます。
Apple のドキュメントは、こちら。
EventKit を使って、Reminder にアクセス
EventKit を使用して、Reminder へアクセスするためには、事前準備が必要となります。
準備
EventKit を使って、データへアクセスするためには、事前にユーザーからの許諾を取る必要があります。
# Calendar へのアクセスと同じです。
以下の記事を参考に、Info.plist を設定し、requestAccess して、ユーザーからアクセスの許諾を得ます。
[iOS][macOS][EventKit] EventKit のセットアップ async/await 対応板
Calendarへのアクセスとの相違点は、アクセス要求の対象が、.event ではなく、.reminder となることです。
let access = try await store.requestAccess(to: .reminder)
EKReminder 取得
Reminder の要素は、EKReminder という class です。
Apple のドキュメントは、こちら。
EKCalendarItem を 継承している class で、Reminder 要素として、完了しているかを表す isCompleted 等が追加されています。
completion handler経由で取得
EKEvent は、enumerateEvents で取得しましたが、EKReminder は、fetchReminders で取得します。
let predicate: NSPredicate = store.predicateForReminders(in: nil)
store.fetchReminders(matching: predicate, completion: { reminders in
// reminders: [EKReminder]?
guard let reminders = reminders else { return }
for reminder in reminders {
// process reminder
}
})
上記のように、取得する EKReminder の条件を predicate で与えると、該当する EKReminder は、completion に [EKReminder] として渡されてきます。
2023.4 時点では、いわゆる async/await 対応した API は提供されていません。
async/await で取得する
せっかくなので(?)、async/await で取得できるように拡張してみます。
enumerateEvents では、1つづつ順番に EKCalendarItem を取得していたために、AsyncStream を使っていましたが、EKReminder の API は、まとめて [EKReminder]? を返してくる仕様です。
ですので、AsyncStream ではなく、単純に withCheckedContinuation を使って async/await への拡張を行います。
extension EKEventStore {
public func reminder(predicate: NSPredicate) async -> [EKReminder]? {
return await withCheckedContinuation({ continuation in
self.fetchReminders(matching: predicate) { reminders in
continuation.resume(returning: reminders)
}
})
}
}
SwiftUI で、EKReminder を表示
せっかく取得したので、EKReminder を表示するアプリを作ります。
2つの class で構成されています。
EventKitStore: ViewModel です。
ContentView: View です。
ViewModel(EventKitStore)
EventKitStore ではこれまでに説明したコードを 1つの class にまとめています。
アクセス許諾されているかを表す accessToEvent というプロパティを作成し、EKEventStore.authorizationStatus の返り値を保存しています。
この プロパティが true になったときに、EKEventStore から EKReminder を取得します。
取得した EKReminder は、reminders という変数に保存しています。直接 UI 表示に使用しているので、@Published と @MainActor を付与しています。
class EventKitStore: ObservableObject {
let store: EKEventStore
var accessToEvent: Bool = false {
didSet {
if accessToEvent {
Task { await fetchReminder() }
}
}
}
@MainActor
@Published var reminders: [EKReminder] = []
init() {
store = EKEventStore()
if EKEventStore.authorizationStatus(for: .event) != .authorized {
Task {
do {
self.accessToEvent = try await store.requestAccess(to: .reminder)
} catch {
print("\(error)")
}
}
} else {
accessToEvent = true
}
}
func fetchReminder() async {
let predicate: NSPredicate = store.predicateForReminders(in: nil)
let passedReminders = await store.reminder(predicate: predicate)
guard let check = passedReminders else { return }
Task { @MainActor in
self.reminders = check
}
}
}
extension EKEventStore {
public func reminder(predicate: NSPredicate) async -> [EKReminder]? {
return await withCheckedContinuation({ continuation in
self.fetchReminders(matching: predicate) { reminders in
continuation.resume(returning: reminders)
}
})
}
}
View(ContentView)
View は、単純に ViewModel が持っている [EKReminder] をリスト表示しています。
取得がうまくいっているか確認したかったので、EKReminder の数を タイトル代わりに表示しています。
次に、取得できた EKReminder のそれぞれの title をリスト表示しています。
なお、EKReminder は、識別用に calendarItemIdentifier というプロパティを持っているので、SwiftUI の List 要素の id にはそのプロパティを指定しています。
struct ContentView: View {
@StateObject var store = EventKitStore()
var body: some View {
VStack {
Text("# of reminder \(store.reminders.count)")
.font(.title)
List {
ForEach(store.reminders, id: \.calendarItemIdentifier) { reminder in
Text(reminder.title)
}
}
}
.padding()
}
}
アプリ外観
なお、EventKit は、iOS / macOS の両方に存在するので、同じコードで iOS / macOS の両方で動作します。(まさしく、マルチプラットフォームです)
# EventKit は、iOS / macOS / watchOS 向けに用意されています。
上記の画像も、iOS / macOS どちらも同じ アカウント上で動作させているので、表示される EKReminder も同じものになっています。
まとめ
Reminder 要素取得の方法
- EventKit を使用する
- 使用許諾をリクエストするのは EKEvent と同様
- 取得するための API は、async/await 対応されていない
- async/await 対応は、withCheckedContinuation を使う
- EventKit は、iOS/macOS 両対応しているので、マルチプラットフォームしやすい
説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。
SwiftUI おすすめ本
SwiftUI を理解するには、以下の本がおすすめです。
SwiftUI ViewMatery
SwiftUI で開発していくときに、ViewやLayoutのための適切なmodifierを探すのが大変です。
英語での説明になってしまいますが、以下の”SwiftUI Views Mastery Bundle”という本がビジュアル的に確認して探せるので、便利です。
英語ではありますが、1ページに コードと画面が並んでいるので、非常にわかりやすいです。
View に適用できる modifier もわかりやすく説明されているので、ビューの理解だけではなく、どのような装飾ができるかも簡単にわかります。
超便利です
販売元のページは、こちらです。
SwiftUI 徹底入門
# SwiftUI は、毎年大きく改善されていますので、少し古くなってしまいましたが、いまでも 定番本です。
Swift学習におすすめの本
詳解Swift
Swift の学習には、詳解 Swift という書籍が、おすすめです。
著者は、Swift の初期から書籍を出していますし、Swift の前に主力言語だった Objective-C という言語についても同様の書籍を出しています。
最新版を購入するのがおすすめです。
現時点では、上記の Swift 5 に対応した第5版が最新版です。
Sponsor Link