こんにちは、たいちです(@taichi_swift_)

たかしSwiftUIでカレンダーアプリってどうやってつくるんだろ?
SwiftUIでカレンダーアプリってどうやってつくるんだろ?
- SwiftUIでカレンダーアプリを作るために必要になるイベントを追加する方法
- SwiftUIでカレンダーアプリを作りたい人
- SwiftUIでカレンダーアプリの作り方のヒントが分かる
アプリ開発の依頼では、カレンダーにイベントを追加する機能が必要になることが多いです。
例えば、毎日の歩数を記録するアプリや、家計簿アプリ、またオリジナルのカレンダーを作るときには必ず必要になります。
カレンダーへのイベントの追加ができると、開発の幅もグンと広がります。
SwiftUI iOSのカレンダーにイベントを追加する方法
iOS標準のカレンダーにイベントを取得・追加するために必要な手順をまとめました。一番下にソースコードもあるので、コピペして活用してくださいね!
カレンダーアプリに必要な機能をインポート
ファイルの一番上に、カレンダーやリマインダーなどと連携できる機能の大枠をインポートします。(SwiftUIも忘れずに!)
import SwiftUI
import EventKit
info.plistの設定
info.plistというファイルを開き、何もないところを右クリック → Add Row → 左側をPrivacy – Calendars Usage Description 右側にはユーザーにカレンダー機能の許諾を得る時のポップアップの文章を書きます。
左側(Key): Privacy – Calendars Usage Description
右側(Value): (例)カレンダーを使います。


プロパティー(値)を定義する
ビューの作動に必要なプロパティ(値)と、取得したカレンダーのイベントを格納する配列型の変数を用意します。
struct(構造体ともいう)は基本的に自分のプロパティを変更できません。
そのためSwiftUIで追加された @State をつけます。こうすることで、変数の変更が検知された時にビューも再描画されるようになります。
@State private var isShownReadCalendarView: Bool = false
@State private var title = ""
@State private var startDate: Date = Date()
@State private var endDate: Date = Calendar.current.date(byAdding: .hour, value: 1, to: Date())!
///上3つの@State変数は、カレンダーにイベントを追加するときに使用します。
@State private var titles: [String] = []
@State private var startDates: [Date] = []
@State private var endDates: [Date] = []
///上3つのArray配列を使用して、カレンダーやリマインダーに応用する。
let eventStore = EKEventStore()
///EKEventStoreのインスタンスを生成する。
アプリのビューを作る
var body: some View {} の中に、ビューを宣言します。
今回は、iOSの設定画面風のUIにするために Form {}と、Section {}を使いました。それぞれの説明は別の記事に書きます。
var body: some View {
Form {
Section {
TextField("タイトル", text: self.$title)
DatePicker(selection: self.$startDate, in: Date()..., displayedComponents: .date, label: { Text("StartDate") })
DatePicker(selection: self.$endDate, in: Date()..., displayedComponents: .date, label: { Text("EndDate") })
}
Section {
Button(action: {
if self.title != "" {
self.addEvent(startDate: self.startDate, endDate: self.endDate, title: self.title)
} else {
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.error)
}
}) {
Text("add Event")
}
}
Section {
Button(action: {
self.isShownReadCalendarView = true
self.readEvents()
}) {
Text("Read Calendar")
.sheet(isPresented: self.$isShownReadCalendarView) {
ForEach(0..<self.titles.count, id: \.self) { i in
Text(self.titles[i])
}
}
}
}
.onAppear(perform: self.allowAuthorization)
}
}
カレンダー機能の使用の許可を取る
getAuthorization_statusというfunc(関数)を宣言します。カレンダーの使用の許諾が取られているかを、Bool型 (trueかfalse, 真か偽)で返します。
func getAuthorization_status() -> Bool {
// 認証ステータスを取得
let status = EKEventStore.authorizationStatus(for: .event)//.reminderで、リマインダーの方も指定できる。
// ステータスの表示が許可されている場合のみtrueを返す
switch status {
case .notDetermined:
print("NotDetermined")
return false
case .denied:
print("Denied")
return false
case .authorized:
print("Authorized")
return true
case .restricted:
print("Restricted")
return false
@unknown default:
literalExpression()
fatalError("カレンダーの認証部分でエラー @unknown default")
}
}
次に、allowAuthorizationというfunc(関数)を宣言します。カレンダーの使用の許諾が得られてなければ、許可を取る処理を行います。
func allowAuthorization() {
if getAuthorization_status() {
// 許可されているので、何もしない。
return
} else {
// 許可されていないので、ユーザーに許可を得ます。
eventStore.requestAccess(to: .event, completion: { (granted, error) in
if granted {
return
} else {
print("Not allowed")
}
})
}
}
カレンダーにイベントを追加する関数(メソッド)を作る
addEventという関数を宣言します。引数(関数を呼び出すときに渡す値)は、startDate: Date (日付情報), endDate: Date?(日付情報), title: String (タイトル:文字列型)です。空のイベントを作成し、開始時間・終了時間・タイトルをセットします。do {} catch {}で、カレンダーにイベントを保存します。
func addEvent(startDate: Date, endDate: Date?, title: String) {
// イベントの情報を準備
let startDate = startDate
let endDate = endDate
let title = title
let defaultCalendar = eventStore.defaultCalendarForNewEvents
// イベントを作成して情報をセット
let event = EKEvent(eventStore: eventStore)
event.title = title
event.startDate = startDate
event.endDate = endDate
event.calendar = defaultCalendar
// イベントの登録
do {
try eventStore.save(event, span: .thisEvent)
} catch let error {
print(error)
}
}
カレンダーに登録されているイベントをリスト(一覧)で取得する
readEventsという関数を作成します。eventStoreと言う名前で、EKEventStoreのインスタンスを生成します。
calendarsという変数に、スマホに登録されているカレンダーの一覧を取得します。for clendar in calendarsを使い、カレンダーの数だけ処理を行うようにします。ここで、カレンダーの名前を指定することもできます。
if calendar.source.title = “カレンダーの名前” {}を挟みます。
1ヶ月前と1ヶ月後の日付をDate型で定義します。
定数 predicate にカレンダーと取得するイベントの範囲をセットします。
定数 events に、predicateにセットされた情報にマッチするイベントを代入します。for event in events {} で、取得したイベントの数だけ処理を行うようにします。
最後に、{}の中で最初の方に定義したtitles, startDates, endDatesにそれぞれイベントのタイトル、開始時間、終了時間をappend(追加)します。
func listEvents() {
// 検索条件
let startDate = Date()
let endDate = Date()
let dC = eventStore.defaultCalendarForNewEvents
guard let defaultCalendar = dC else {
return
}
let predicate = eventStore.predicateForEvents(withStart: startDate, end: endDate, calendars: [defaultCalendar])
// イベントを検索
let events = eventStore.eventStoreIdentifier
}
まとめ・ソースコード
基本であるイベントの追加ができれば、カレンダーを指定したり、イベントを消したりするのも簡単にできます!ソースコードもおまけで作ってみました。
自信がある方はアレンジしてみてください!
import SwiftUI
import EventKit
struct CalendarView: View {
@State private var isShownReadCalendarView: Bool = false
@State private var title = ""
@State private var startDate: Date = Date()
@State private var endDate: Date = Calendar.current.date(byAdding: .hour, value: 1, to: Date())!
///上3つの@State変数は、カレンダーにイベントを追加するときに使用します。
@State private var titles: [String] = []
@State private var startDates: [Date] = []
@State private var endDates: [Date] = []
///上3つのArray配列を使用して、カレンダーやリマインダーに応用する。
let eventStore = EKEventStore()
var body: some View {
Form {
Section {
TextField("タイトル", text: self.$title)
DatePicker(selection: self.$startDate, in: Date()..., displayedComponents: .date, label: { Text("StartDate") })
DatePicker(selection: self.$endDate, in: Date()..., displayedComponents: .date, label: { Text("EndDate") })
}
Section {
Button(action: {
if self.title != "" {
self.addEvent(startDate: self.startDate, endDate: self.endDate, title: self.title)
} else {
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.error)
}
}) {
Text("add Event")
}
}
Section {
Button(action: {
self.isShownReadCalendarView = true
self.readEvents()
}) {
Text("Read Calendar")
.sheet(isPresented: self.$isShownReadCalendarView) {
ForEach(0..<self.titles.count, id: \.self) { i in
Text(self.titles[i])
}
}
}
}
.onAppear(perform: self.allowAuthorization)
}
}
func allowAuthorization() {
if getAuthorization_status() {
// 許可されているので、何もしない。
return
} else {
// 許可されていないので、ユーザーに許可を得ます。
eventStore.requestAccess(to: .event, completion: { (granted, error) in
if granted {
return
} else {
print("Not allowed")
}
})
}
}
// 認証ステータスを確認する
func getAuthorization_status() -> Bool {
// 認証ステータスを取得
let status = EKEventStore.authorizationStatus(for: .event)//.reminderで、リマインダーの方も指定できる。
// ステータスの表示が許可されている場合のみtrueを返す
switch status {
case .notDetermined:
print("NotDetermined")
return false
case .denied:
print("Denied")
return false
case .authorized:
print("Authorized")
return true
case .restricted:
print("Restricted")
return false
@unknown default:
literalExpression()
fatalError("カレンダーの認証部分でエラー @unknown default")
}
}
func listEvents() {
// 検索条件
let startDate = Date()
let endDate = Date()
let dC = eventStore.defaultCalendarForNewEvents
guard let defaultCalendar = dC else {
return
}
let predicate = eventStore.predicateForEvents(withStart: startDate, end: endDate, calendars: [defaultCalendar])
// イベントを検索
let events = eventStore.eventStoreIdentifier
}
func addEvent(startDate: Date, endDate: Date?, title: String) {
// イベントの情報を準備
let startDate = startDate
let endDate = endDate
let title = title
let defaultCalendar = eventStore.defaultCalendarForNewEvents
// イベントを作成して情報をセット
let event = EKEvent(eventStore: eventStore)
event.title = title
event.startDate = startDate
event.endDate = endDate
event.calendar = defaultCalendar
// イベントの登録
do {
try eventStore.save(event, span: .thisEvent)
} catch let error {
print(error)
}
}
func deleteEvent(event: EKEvent) {
do {
try eventStore.remove(event, span: .thisEvent)
} catch let error {
print(error)
}
}
func readEvents() {
let eventStore = EKEventStore()
let calendars = eventStore.calendars(for: .event)
for calendar in calendars {
//if calendar.source.title == "カレンダーの名前" {
///カレンダーの名前を指定することができる。
let oneMonthAgo = Calendar.current.date(byAdding: .month, value: -1, to: Date())!
let oneMonthAfter = Calendar.current.date(byAdding: .month, value: +1, to: Date())!
let predicate = eventStore.predicateForEvents(withStart: oneMonthAgo, end: oneMonthAfter, calendars: [calendar])
let events = eventStore.events(matching: predicate)
for event in events {
self.titles.append(event.title)
self.startDates.append(event.startDate)
self.endDates.append(event.endDate)
}
//}
}
}
func literalExpression() {
print("__FILE__",#file)
print("__LINE__",#line)
print("__COLUMN__",#column)
print("__FUNCTION__",#function)
}
}
最後まで読んでくれてありがとうございます!
プログラミングの関連記事はこちらからどうぞ