mercredi 8 novembre 2017

Pattern when using Realm in a Cocoa Touch Framework

This is a code pattern/design question when using the amazing Realm data base inside a Cocoa Touch Framework. Specifically, a framework that will be distributed as a cocoapod.

Lets say I have a some realm objects inside a framework I am building

public class Dog: Object {
    @objc public private(set) dynamic var name: String?
    public let age = RealmOptional<Int>()
    public private(set) var owner: Person?
}

public class Person: Object {
    @objc public private(set) dynamic var name: String?
    public private(set) var dogs = LinkingObjects(fromType: Dog.self, property: "owner")
}

Now, I want the consumer of my framework to be able to interact with these objects. I don't want to abstract these with an MVVM pattern because I want the user of my framework to be able to take advantage of some great Realm stuff like querying, sorting, and most importantly, Realm change notifications.

So, first question. Should I let users of my framework initialize objects directly? They will have these options already with the Realm initializers and if they choose to use them they are responsible for them. But, I like to go with factory pattern using static methods. Like this:

extension Dog {
    public static func retreiveManagedDog() throws -> Dog {
        let dog = Dog()
        do{
            let realm = try Realm()
            realm.beginWrite()

            realm.add(dog)
            try realm.commitWrite()
        }catch{
            throw error
        }
        return dog
    }
}

Is this a good design pattern for this use case?

Second, the next problem is updating objects. Since all Realm objects have to be updated inside a write transaction I don't want the user of my framework to have to write a bunch of boilerplate code just to change a name. So, I write functions like this:

//MARK: Extension that has functions to update properties
extension Dog {
    public func updateName(_ name: String?) throws {
        do{
            let realm = try Realm()
            realm.beginWrite()
            self.name = name
            try realm.commitWrite()
        }catch{
            throw error
        }
    }
}

Notice my object definitions had private(set) just for this reason. It will help force the user of my framework to use my setter methods.

In general, am I insane for trying to wrap Realm this way? Other great frameworks that persist usually wrap all the SQL Lite/Core Data logic. I would also like suggestions to improve this pattern in general.

Aucun commentaire:

Enregistrer un commentaire