SwiftでRealmを使う時のTips(1) アクセサとエンティティ編

  • 25
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

目次

SwiftでRealmを使う時のTips まえがき
SwiftでRealmを使う時のTips(1) アクセサとエンティティ編
SwiftでRealmを使う時のTips(2) 生成とオートインクリメント編
SwiftでRealmを使う時のTips(3) NSPredicate編

アクセサ(DAO)とエンティティ(DTO)

データを扱うときは DAODTO という設計概念がありますね
厳密に正しいかどうかわかりませんが
ざっくりいうと「データを管理するヤツ」 と 「データそのもの」 は分離させる設計概念です

まぁ、健全なみなさんはちゃんとやってますよね・・・
これがたまにごっちゃにして書いている人がいるので
たまにソースを読んでてこちらもごっちゃになります

Realm自体には、こいつらを分離させるためのクラス体系はありません
なので、最初に自分で分離させておくことは少なくとも大事だと感じました

NBRealmEntity.swift
public class NBRealmEntity: RealmSwift.Object {

    public static let IDKey       = "id"
    public static let CreatedKey  = "created"
    public static let ModifiedKey = "modified"

    public dynamic var id : Int64 = 0 // = NBRealmEntity.IDKey

    public dynamic var created = NSDate() // = NBRealmEntity.CreatedKey

    public dynamic var modified = NSDate() // = NBRealmEntity.ModifiedKey

    public override static func primaryKey() -> String? {
        return NBRealmEntity.IDKey
    }
}
NBRealmAccessor.swift
import UIKit
import RealmSwift

public class NBRealmAccessor<T: NBRealmEntity> {

    public typealias Entity = T

    public var realm: Realm { return try! Realm() }

    public init() {}
}

実装は上記のとおり2種類のクラスを用意します

ちなみにプレフィックスのNBは自分の屋号からとってきたもので特に意味はありません
(外してもらってもいいです)

エンティティ

データそのものを指すクラスは "エンティティ" という名前にしています
他にも「オブジェクト」や「VO」「データ」「レコード」といった名前で
呼ぶ人もおられると思いますが、
ここではそのように名づけました

IDInt64型にしていますが
よほどのことがない限りはIntでもリソースの枯渇はないと思います
なので Int64型 にこだわる必要はまったくないですが
ここではそのままにしています

アクセサ

データを管理するクラスは "アクセサ" という名前にしています

基底クラスでジェネリクスをとらせてますので
こいつは「あるエンティティ専用のアクセサ」として使うことができます

こんな感じですね


class UserEntity: NBRealmEntity {
    // something
}

class UserAccessor: NBRealmAccessor<UserEntity> {
    // something
}

UserAccessorUserEntity に関するお仕事しかしないと
これで決めてやったも当然です
責務が狭まったこのクラスは、これであまりソースが散らかることがなくなります (本当かな?)

で、このアクセサは、狭義のモデルクラスになります
オブジェクトの死滅が早いコントローラに、モデルを所持させてはよくないです
ゆえに、呼び出しは下記のように実装すべきですねぇ

// こちらはダメな例
class ViewController: UIViewController {
    public override func viewDidLoad() {
        super.viewDidLoad()

        let userAccessor = UserAccessor()
        userAccessor.hoge()
    }
}
// こういう感じにしておきたい
class Model {
    static let userAccessor = UserAccessor()
}

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        Model.userAccessor.hoge()
    }
}

両者の関係性

ここでエンティティとアクセサの両者を使う時のルールを確認します

  • エンティティは必ずアクセサによって作られる
let user = UserEntity()

みたいなことができるのは唯一アクセサクラスだけというのを徹底します
たとえば、コントローラやビューに書いていては行けませんし、
モデルクラスであっても無関係な処理をしてるクラスでは絶対にインスタンス化をしません

これで 「UserEntity をうんぬんかんぬんしてるってことは・・・、UserAccessor だ!」と
メンテナンスの時に安心できますよね

  • エンティティをデータベースに保存するのは必ずアクセサ

エンティティはただの変数の器。データの固まり
こいつ自身が単独で恒久的データをいじってしまうのはあまりよくないです

なかなか悩ませるのがRealmのオシャレな(?)機能である
リアルタイムアップデートというやつです
オブジェクトの値を変えると、KVOによってそのままデータベースも書き変わるというもの
まぁ便利なんだけど、逆に不便な場面が色々と出てきました

そこは場面によって使い分けも必要かと思いますが、
原則論としては、データベースの書き換えはアクセサ経由でないとやらせない
というルールをしっかりしておけば
CRUDが散らかることが減るかなぁと個人的には思っております