Firebase Model Framework Saladaを使ってアプリを作る

# Firebase Model Framework Saladaを使ってアプリを作る

[Salada](https://github.com/1amageek/Salada)を利用頂いてありがとうございます。利用方法についてご質問を頂くことが増えましたので、もう少し使い方を説明しようと思い記事にしました。

こちらにサンプルコードをおいておきます。
https://github.com/1amageek/SaladaSample

## Saladaにできること
Saladaには大きく4つの機能があります。

- Modelを作る
- Model単位でDataを取得する
- クエリを管理する
- リレーションシップを簡略化する

それぞれの機能は以下のクラスで利用できます。

### Modelを作る

モデルを作るには`Object`クラスを継承します。Userモデルを作ってみましょう。
以下のように様々なデータ型のプロパティを持つことが可能です。

``` swift
class User: Object {
    
    override class var _version: String {
        return "v1"
    }
    
    @objc dynamic var name: String?
    @objc dynamic var age: Int = 0
    @objc dynamic var gender: String?
    @objc dynamic var groups: Set<String> = []
    @objc dynamic var items: [String] = []
    @objc dynamic var location: CLLocation?
    @objc dynamic var url: URL?
    @objc dynamic var birth: Date?
    @objc dynamic var thumbnail: File?
    @objc dynamic var cover: File?
    @objc dynamic var type: UserType = .first
    @objc dynamic var testItems: Set<String> = []
    let followers: Follower = []
    
    var tempName: String? 
    
    override var ignore: [String] {
        return ["tempName"]
    }
    
    override func encode(_ key: String, value: Any?) -> Any? {
        if key == "location" {
            if let location = self.location {
                return ["latitude": location.coordinate.latitude, "longitude": location.coordinate.longitude]
            }
        } else if key == "type" {
            return self.type.rawValue as AnyObject?
        }
        return nil
    }
    
    override func decode(_ key: String, value: Any?) -> Any? {
        if key == "location" {
            if let location: [String: Double] = value as? [String: Double] {
                self.location = CLLocation(latitude: location["latitude"]!, longitude: location["longitude"]!)
                return self.location
            }
        } else if key == "type" {
            if let type: Int = value as? Int {
                self.type = UserType(rawValue: type)!
                return self.type
            }
        }
        return nil
    }
}
```

`encode`と`decode`に注目してください。プロパティの中には

``` swift
@objc dynamic var location: CLLocation?
```

`CLLocation`が含まれています。`CLLocation`はCoreLocationに含まれるクラスのため、Saladaではサポートしていません。　ここで利用するのが`encode`と`decode`です。
それぞれFirebaseサイドとクライアントサイドでどういった型として取り扱うのかを定義しています。

``` swift
@objc dynamic var type: UserType = .first
```

`UserType`は`enum`で定義した型ですが、これも`encode`と`decode`を利用することで定義可能です。


### Model単位でDataを取得する

Objectを継承したModelは、`Referenceable`に準拠しています。
Userは以下のように取得できます。

**一度だけ値を取得する**

``` swift
User.get(id, eventType: .value) { (user) in
    guard let user: User = user else {
        return
    }
    
}
```

**監視し続ける**

``` swift
let disposer = User.listen(id, eventType: .value) { (user) in
    guard let user: User = user else {
        return
    }
    
}

// 破棄する
disposer.dispose()
```

監視を行う場合は、不要になれば必ず破棄してください。

### クエリを管理する

クエリはDataSourceクラスが管理します。
DataSourceクラスは、[Realm](https://realm.io/jp/)のような振る舞いをすることができます。もしくは、NSFetchResultControllerを想像してもらうほうが理解できるかもしれません。

DataSourceを利用する機会のほとんどは、`TableView` `CollectionView`であることを想定しており、UIの簡単に操作できるように設計しています。Realmを利用している方にとっては馴染みが深い処理になるはずです。

DataSourceは受けっとった`reference`のデータを`Options`で指定したクエリを下に取得してきます。

``` swift
let options: Options = Options()
options.limit = 10
options.predicate = NSPredicate(format: "age == 21")
options.sortDescirptors = [NSSortDescriptor(key: "age", ascending: false)]

let user: User = User(id: "USER_ID")!

self.dataSource = DataSource(reference: user.items.ref, options: options) { [weak self] (changes) in
    guard let tableView: UITableView = self?.tableView else { return }

    switch changes {
    case .initial:
        tableView.reloadData()
    case .update(let deletions, let insertions, let modifications):
        tableView.beginUpdates()
        tableView.insertRows(at: insertions.map { IndexPath(row: $0, section: 0) }, with: .automatic)
        tableView.deleteRows(at: deletions.map { IndexPath(row: $0, section: 0) }, with: .automatic)
        tableView.reloadRows(at: modifications.map { IndexPath(row: $0, section: 0) }, with: .automatic)
        tableView.endUpdates()
    case .error(let error):
        print(error)
    }
}
```

### リレーションシップを簡略化する

リレーションシップを行うためにはRelationクラスを利用します。
Relationクラスを継承することで、簡単にリレーションシップを作ることが可能です。ユーザーのフォロー機能を参考にしてみましょう。
まず`Follower`クラスを定義します。定義をするだけで大丈夫です。

``` swift
class Follower: Relation<User> {　}
```

`Follower`クラスをUserモデルのプロパティーに定義しましょう。

``` swift
class User: Object {
    @objc dynamic var name: String?
    let followers: Follower = []
}
```

これでUser.followersが定義できました。使い方もとてもシンプルです。

``` swift
let a: User = User()
let b: User = User()
a.followers.insert(b)
a.save()
```
これで、`a`と`b`のリレーションシップが形成されます。
ユーザーモデルの定義に監視てはこちらをご参考ください。

[Firebase Model Framework Saladaを使ってUser Modelを作る](https://qiita.com/1amageek/items/4fbba1b22e81b650d656)

その他ご質問があればぜひ、コメントください！
お待ちしております！