Search code examples
iosswiftarchitecturerealmswiftbond

What is the best way to connect Realm and SwiftBond


I love Realm and I love Bond. Both of them makes app creation a joy. So I was wondering what is the best way to connect Realm and Bond?
In Realm we can store basic types such as Int, String, e.g. But in Bond we work with Dynamics and Bonds. The only way that I found to connect Realm and Bond is following:

class TestObject: RLMObject {

   dynamic var rlmTitle: String = ""
   dynamic var rlmSubtitle: String = ""

   var title: Dynamic<String>
   var subtitle: Dynamic<String>

   private let titleBond: Bond<String>!
   private let subtitleBond: Bond<String>!

   init(title: String, subtitle: String) {
      self.title = Dynamic<String>(title)
      self.subtitle = Dynamic<String>(subtitle)

      super.init()

      self.titleBond = Bond<String>() { [unowned self] title in self.rlmTitle = title }
      self.subtitleBond = Bond<String>() { [unowned self] subtitle in self.rlmSubtitle = subtitle }

      self.title ->> titleBond
      self.subtitle ->> subtitleBond
   }
}

But it surely lacks simplicity and elegance and produces a lot of boiler code. Is there any way to do this better?


Solution

  • I've been thinking about this for three days and came up with nearly perfect solution, which does not employ any boilerplate code. First of all I have created a super class for a realm model's wrapper:

    class BondRealmBaseClass {
    
       private var realmModel: RLMObject!
       private let realm = RLMRealm.defaultRealm()
       private var bonds = NSMutableArray()
    
       init(){
          realmModel = createRealmModel()
          realm.beginWriteTransaction()
          realm.addObject(realmModel)
          realm.commitWriteTransaction()
          createBonds()
       }
    
       init(realmModel: RLMObject){
          self.realmModel = realmModel
          createBonds()
       }
    
       func createBondFrom<T>(from: Dynamic<T>, toModelKeyPath keyPath: String){
          from.value = realmModel.valueForKeyPath(keyPath) as T
          let bond = Bond<T>() { [unowned self] value in
             self.realm.beginWriteTransaction()
             self.realmModel.setValue(value as NSObject, forKey: keyPath)
             self.realm.commitWriteTransaction()
          }
          from ->| bond
          bonds.addObject(bond)
       }
    
       //MARK: - Should be overriden by super classes
       func createBonds(){ fatalError("should be implemented in supreclass") }
       func createRealmModel() -> RLMObject{ fatalError("should be implemented in supreclass") }
    }
    

    After that for each realm model I create two classes, first is the actual realm model, which stores all properties:

    class RealmTodoModel: RLMObject { 
       dynamic var title = ""
       dynamic var date = NSDate()
    }
    

    and a second one is the wrapper around realm model:

    class TodoModel : BondRealmBaseClass{
       let title = Dynamic("")
       let date = Dynamic(NSDate())
    
       override func createBonds(){
          createBondFrom(title, toModelKeyPath: "title")
          createBondFrom(date, toModelKeyPath: "date")
       }
    
       override func createRealmModel() -> RLMObject { return RealmTodoModel() }
    }
    

    And this two classes is actually all is needed to link Realm and Bond: creating new TodoModel will actually add to Realm new RealmTodoModel and all changes made with TodoModel's title and date will be automatically saved to corresponding Realm model!

    EDIT

    I added some functionality and posted this as a framework on GitHub. Here is the link.