Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write a better data access layer with Realm

I've been using Realm in a few small projects and I quite like it. I'm hoping to move on to using it in bigger projects and I'm looking for better structure my data access layer.

I came across this similar question and tried to build up on the information I found there. The approach discussed there is the DAO pattern so I gave a shot at that.

This is my model class.

class Chat: Object {
    dynamic var id: String = ""
    dynamic var createdAt: Date = Date()
    dynamic var creatorId: String = ""
    dynamic var title: String?
    let chatMessages = List<ChatMessage>()

    override static func primaryKey() -> String? {
        return "id"
    }

    convenience init(fromJSON json: JSON) {
        self.init()
        // ...
    }
}

Then I created a ChatDAOProtocol to hold all the convenience helper methods.

protocol ChatDAOProtocol {
    func addMessage(_ message: ChatMessage)
    func getChatThumbnail() -> UIImage
    func getParticipants(includingMe: Bool) -> [Participant]?
    static func getChat(fromId id: String) -> Chat?
    static func getChat(fromCreatorId id: String) -> Chat?
}

Lastly I created another class called ChatHelper that implemented all those protocol methods.

class ChatHelper: ChatDAOProtocol {
    func addMessage(_ message: ChatMessage) {

    }

    func getChatThumbnail() -> UIImage {
        return UIImage()
    }

    func getParticipants(includingMe: Bool) -> [Participant]? {
        return nil
    }

    static func getChat(fromId id: String) -> Chat? {
        return nil
    }

    static func getChat(fromCreatorId id: String) -> Chat? {
        return nil
    }

}

This already seems better than sprinkling all the database related code all over the VCs and stuff. But I still have some doubts.

For example, say if I need to get all the participants of a chat, now I have to call the method on the ChatHelper class. And if I want to get simply the chat title, I call the title property of the Chat object itself. Doesn't seem like a very unified interface. Should I include getters and setters for all the properties in the helper as well. So the Chat object is never directly called (except for maybe creating an instance).

Or

Should I make the Chat object itself conform to the ChatDAOProtocol protocol? So all the convenience methods as well as the properties are directly accessible from the Chat object straight up?

Or is there a better way than both of these?

like image 906
Isuru Avatar asked Feb 01 '17 08:02

Isuru


1 Answers

This is pretty tricky sort of question since it really depends on how much you want to abstract away from directly interacting with Realm, and how much you want to compromise with Realm's performance.

Personally, I think it is fine if you are abstracting away query and write logic, but still directly reading from Realm model objects. If you moved to another object-based database (Like Core Data), then while you would refactor the parent class these objects belonged to something else (eg, RLMObject to NSManagedObject), the way your business logic read from these objects wouldn't change.

One thing you definitely need to be careful though is abstracting the logic in such a way that will utilize Realm very inefficiently.

The main example of this I can see is in your getParticipants method, you're returning a standard Swift array. Converting a Realm Results object to such would result in paging every object in memory (as opposed to lazy-loading on request), so you would lose a lot of Realm performance benefits. But since Results objects behave like standard arrays, you wouldn't need to change your business logic if you directly returned one.

Another consideration: if you're updating a single property on a batch of objects, you would be much better off ensuring all of the objects are updated in a single write transaction, instead of the helper class internally opening a write transaction each time the helper method is called.

like image 151
TiM Avatar answered Nov 07 '22 16:11

TiM