【初心者でもできるカレンダーアプリ!】イベントを追加する方法 SwiftUI 編

13 min
SwiftUI カレンダーアプリの作り方

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

たかしSwiftUIでカレンダーアプリってどうやってつくるんだろ?


 

SwiftUIでカレンダーアプリってどうやってつくるんだろ?

本記事の内容
  • SwiftUIでカレンダーアプリを作るために必要になるイベントを追加する方法
本記事の読者
  • SwiftUIでカレンダーアプリを作りたい人

 

本記事を読むメリット
  • SwiftUIでカレンダーアプリの作り方のヒントが分かる

 

アプリ開発の依頼では、カレンダーにイベントを追加する機能が必要になることが多いです。

例えば、毎日の歩数を記録するアプリや、家計簿アプリ、またオリジナルのカレンダーを作るときには必ず必要になります。
カレンダーへのイベントの追加ができると、開発の幅もグンと広がります。

SwiftUI iOSのカレンダーにイベントを追加する方法

iOS標準のカレンダーにイベントを取得・追加するために必要な手順をまとめました。一番下にソースコードもあるので、コピペして活用してくださいね!

カレンダーアプリに必要な機能をインポート

ファイルの一番上に、カレンダーやリマインダーなどと連携できる機能の大枠をインポートします。(SwiftUIも忘れずに!)

import SwiftUI
import EventKit

info.plistの設定

info.plistというファイルを開き、何もないところを右クリック → Add Row → 左側をPrivacyCalendars Usage Description 右側にはユーザーにカレンダー機能の許諾を得る時のポップアップの文章を書きます。

左側(Key): PrivacyCalendars 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型 (truefalse, )で返します。

    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)
    }
}

最後まで読んでくれてありがとうございます!

プログラミングの関連記事はこちらからどうぞ

関連記事

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です