FSCalendarのカレンダーに点マークを表示する

#はじめに
カレンダー付きToDoアプリを制作する際にFSCalendarというライブラリを使用しましたので備忘録として投稿します。
初学者ですので訂正点ございましたら、ご指摘よろしくお願いします。


#概要
FSCalendarではカレンダーの日付の下に任意の条件で点マークを表示することができます。
制作したアプリの用途に絡めると「ToDoの登録がある日付に点マークを表示」になります。

今回はRealmSwiftを組み合わせての実装になりますのでRealmSwiftに関しましては[こちら](https://realm.io/jp/docs/swift/latest/)参照ください。
また、FSCalendarの導入に関しましては[こちら](https://github.com/WenchaoD/FSCalendar)を参照ください。


#実行環境
【Xcode】 Version 11.7
【Swift】 version 5.2.4
【CocoaPods】version 1.9.3
【RealmSwift】 version 5.3.2
【FSCalendar】version 2.8.1


#実装コード
全体のコードになります。
サンプルコードではなく、自作アプリのコードになりますので関連箇所を抜粋しております。
また、FSCalendarのDelegateとDataSourceはstoryboard上で追加しております。

```swift:Todo.swift
import Foundation
import RealmSwift

class Todo: Object {
    # ・・・省略・・・
    @objc dynamic var dateString: String!
    # ・・・省略・・・
}
```

```swift:MainViewController.swift
import UIKit
import FSCalendar
import CalculateCalendarLogic
import RealmSwift

class MainViewController: UIViewController {

    @IBOutlet weak var calendar: FSCalendar!
    # ・・・省略・・・
    var datesWithEvents: Set<String> = []
    # ・・・省略・・・

    override func viewDidLoad() {
        super.viewDidLoad()
        # ・・・省略・・・
    }
}

extension MainViewController: FSCalendarDelegate, FSCalendarDataSource, FSCalendarDelegateAppearance {
    // 任意の日付に点マークをつける
    func calendar(_ calendar: FSCalendar, numberOfEventsFor date: Date) -> Int{
        
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy/MM/dd"
        formatter.calendar = Calendar(identifier: .gregorian)
        formatter.timeZone = TimeZone.current
        formatter.locale = Locale.current
        let calendarDay = formatter.string(from: date)
        
        // Realmオブジェクトの生成
        let realm = try! Realm()
        // 参照（全データを取得）
        let todos = realm.objects(Todo.self)
        
        if todos.count > 0 {
            for i in 0..<todos.count {
                if i == 0 {
                    datesWithEvents = [todos[i].dateString]
                } else {
                    datesWithEvents.insert(todos[i].dateString)
                }
            }
        } else {
            datesWithEvents = []
        }
        return datesWithEvents.contains(calendarDay) ? 1 : 0 
    }
}

```


#実装方法
以下のメソッドを用いることで点マークがつけられるようになります。

```swift:
func calendar(_ calendar: FSCalendar, numberOfEventsFor date: Date) -> Int{
    return 1 //ここに入る数字によって点の数が変わる
}
```

画面に表示されている月の日付（Date型）がループで投げられます。自動的にFor-in構文のようにdateの数だけこのメソッドが呼ばれるイメージです。
しかしこのまま用いても全ての日付の下に点マークが付くようになるだけになります。
これをRealmSwiftと組み合わせることで、ToDoの登録がある日付にだけ点マークを表示するようにしました。

####１．上記メソッドで呼ばれる日付（Date型）と照合させるためのデータ（dateString）を用意し、RealmSwiftに登録
- RealmSwiftに登録するTodoクラスに下記変数を用意します。

```swift:
@objc dynamic var dateString: String!
```
- Realm Studioで確認すると赤枠の箇所になります。
![スクリーンショット 2020-09-08 22.17.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692635/b8390e07-e3f8-49b5-fdcb-81db0451590a.png)

- また、メソッドで呼ばれる日付（Date型）をRealmSwiftに保存したデータと同じフォーマットに変換します。

```swift:
formatter.dateFormat = "yyyy/MM/dd"
let calendarDay = formatter.string(from: date)
```

####２．RealmSwiftからデータを取得
```swift:
// Realmオブジェクトの生成
let realm = try! Realm()
// 参照（全データを取得）
let todos = realm.objects(Todo.self)
```

####３．取得したデータの中から必要なデータ（dateString）をピックアップして変数に格納
- RealmSwiftに登録データがある場合は、変数「datesWithEvents」に「dateString」の内容を格納。登録データがない場合は、変数「datesWithEvents」には何も入れません。

```swift:
if todos.count > 0 {
    for i in 0..<todos.count {
        if i == 0 {
            datesWithEvents = [todos[i].dateString]
        } else {
            datesWithEvents.insert(todos[i].dateString)
        }
    }
} else {
    datesWithEvents = []
}
```
- 変数「datesWithEvents」は配列のSetクラス（集合型）になります。Arrayクラスと異なる箇所としましてはインデックス番号が存在せず、重複が許されない型ということ違いがあります。今回RealmSwiftから取得するデータが重複する必要がなかったためSetクラス（集合型）にしております。詳細は[こちら](https://mayugezio.com/started/set/)参照ください。

```swift:
var datesWithEvents: Set<String> = []
```

####４．日付と照合させ、Int型を返す
- 三項演算子を用いて、任意の文字列(calendarDay)が含まれているか判断し、含まれている場合は「１」を、含まれていない場合は「０」を返します。
- 三項演算子の気をつける点としまして、半角スペースを置かずに「?」をつけると「オプショナル型の宣言」と解釈されエラーになりますのでご注意下さい。

```swift:
return datesWithEvents.contains(calendarDay) ? 1 : 0 
```

####5．実装後の画面
![スクリーンショット 2020-09-08 23.33.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692635/0db85e89-674c-8d4f-e39b-29b46ea2fce2.png)


#参考
- [【Swift4】FSCalendarのカレンダーに表示する点マークとテーブルのセルの数を対応させる
](https://qiita.com/naipaka/items/21ffc252ea73beb7a61e)


#おわりに
自作アプリを制作している際にFSCalendarについての記事はいくつかあり、重宝させて頂きましたが、カレンダーに点マークを表示させるメソッドの紹介記事等で終わっているものが多く、実装に絡めた記事が少なかったので初学者の私からするとイメージがつきにくいなと感じておりました。コードの簡略化等を踏まえると未熟な内容になってしまいますが、誰かのお力になれれば幸いです。