Firebase Model Framework Saladaを使ってアプリを作る
Saladaを利用頂いてありがとうございます。利用方法についてご質問を頂くことが増えましたので、もう少し使い方を説明しようと思い記事にしました。
こちらにサンプルコードをおいておきます。
https://github.com/1amageek/SaladaSample
Saladaにできること
Saladaには大きく4つの機能があります。
- Modelを作る
- Model単位でDataを取得する
- クエリを管理する
- リレーションシップを簡略化する
それぞれの機能は以下のクラスで利用できます。
Modelを作る
モデルを作るにはObjectクラスを継承します。Userモデルを作ってみましょう。
以下のように様々なデータ型のプロパティを持つことが可能です。
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に注目してください。プロパティの中には
@objc dynamic var location: CLLocation?
CLLocationが含まれています。CLLocationはCoreLocationに含まれるクラスのため、Saladaではサポートしていません。 ここで利用するのがencodeとdecodeです。
それぞれFirebaseサイドとクライアントサイドでどういった型として取り扱うのかを定義しています。
@objc dynamic var type: UserType = .first
UserTypeはenumで定義した型ですが、これもencodeとdecodeを利用することで定義可能です。
Model単位でDataを取得する
Objectを継承したModelは、Referenceableに準拠しています。
Userは以下のように取得できます。
一度だけ値を取得する
User.get(id, eventType: .value) { (user) in
    guard let user: User = user else {
        return
    }
    
}
監視し続ける
let disposer = User.listen(id, eventType: .value) { (user) in
    guard let user: User = user else {
        return
    }
    
}
// 破棄する
disposer.dispose()
監視を行う場合は、不要になれば必ず破棄してください。
クエリを管理する
クエリはDataSourceクラスが管理します。
DataSourceクラスは、Realmのような振る舞いをすることができます。もしくは、NSFetchResultControllerを想像してもらうほうが理解できるかもしれません。
DataSourceを利用する機会のほとんどは、TableView CollectionViewであることを想定しており、UIの簡単に操作できるように設計しています。Realmを利用している方にとっては馴染みが深い処理になるはずです。
DataSourceは受けっとったreferenceのデータをOptionsで指定したクエリを下に取得してきます。
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クラスを定義します。定義をするだけで大丈夫です。
class Follower: Relation<User> { }
FollowerクラスをUserモデルのプロパティーに定義しましょう。
class User: Object {
    @objc dynamic var name: String?
    let followers: Follower = []
}
これでUser.followersが定義できました。使い方もとてもシンプルです。
let a: User = User()
let b: User = User()
a.followers.insert(b)
a.save()
これで、aとbのリレーションシップが形成されます。
ユーザーモデルの定義に監視てはこちらをご参考ください。
Firebase Model Framework Saladaを使ってUser Modelを作る
その他ご質問があればぜひ、コメントください!
お待ちしております!