David Cordero

iOS and tvOS developer at @Zattoo. Amateur Triathlete. Passionate about coding and lifelong learning.

Data Sources in Swift… or how to avoid that this new trendy persistence framework determines the architecture of your App

13 Jun 2016 » iOS, swift

There are really good frameworks to deal with the persistency of our data in the world of iOS.

Core Data for instance is a great solution offered by Apple that provides an incredible good performance even when dealing with a really huge amount of data. But there are also, some other alternatives poping around. One of these alternatives is Realm which is said to offer even better performance than Core Data, with a simpler syntax.

image

But sadly, not eveything is as wonderful as expected in this world of persistency. There are also plenty of problems. I have had really bad experiences with Core Data, Realm or many of these other trendy persistence frameworks out there, of course not only because of the frameworks itself, but because of how they are applied to the projects. The main problem is that, due to the complexity of the problem that these frameworks solve, they tend to have a quite complex syntax. And if the scope of the framework is not clearly defined, their objects in the end tend to be scattered all around the project, finding UIViews taking data from NSManagedObjects, UICollectionViews populating cells using RLMObjects, etc… 😨🔫

This should never happen, the persistency of the App should be something very internal, hidden in the deepest layer of the core part of our App and of course without any relashionship with our UI Layer. And the most important part, it should be very easy to replace all this part of the code to use any other solution or framework in the future. This sounds like utopia, right? But… How can we get this? We basically just need an abstract solution which defines an interface to deal with the persistence of our data. I mean using the Data Sources.

Note: Please do not mix up this concept of Data Sources with the ones used by iOS to populate some TableViews, CollectionViews, etc…

Data Sources

Data Sources abstract the persistence logic from the core business logic and allow the data to be accesed regardless of the specific details of the frameworks.

We can build an interface to manage our Data Sources with a very simple protocol in Swift, which provides a complete CRUD (Create Read Update Delete) interface.

protocol DataSource {
    associatedtype T
    
    func getAll() -> [T]
    func getById(id: String) -> T
    func insert(item: T)
    func update(item: T)
    func clean()
    func deleteById(id: String)
}

But what the heck is this T ?

Well, this T should be the object we are expecting to persist or to retrieve from our persistence framework. But I mean the object we are expecting, I don’t mean the object that the framework is expecting. These types of objects are usually called Entities and they are usually Plain Objects (objects without any associated logic)

Basically this T should NOT be a NSManagedObject, RLMObject, Archivable, etc…

Let’s follow with an example. Imagine we have been recruited by Amazon and we have been asked to persist the list of books on their catalog. (easy task 🙂)

Our Book entity could be something as:

struct BookEntity {
    let authors: [AuthorEntity]
    let isbn: String
}

struct AuthorEntity {
    let name: String
    let surname: String
}

As I said, it doesn’t contain anything regarding persistency frameworks, just the information we want to persist. Then, how do we persist these data? Let’s do it using Realm for the example:

We can start implementing the protocol previously defined for Realm. Here we can already start dealing with the specific implementation details of Realm:

import RealmSwift

class BooksRealmDataSource: DataSource {
    private let realm = try! Realm()

    func getAll() -> [Book] {
        return realm.objects(RealmBook.self).map { $0.entity }
    }

    func getById(id: String) -> Book {
        return realm.object(RealmBook.self).filter(title == %@”, id)
    }

    func insert(item: Book) {
        try! realm.write {
            realm.add(BookEntity(book: Book))
        }
    }

    func clean() {
        try! realm.write {
            realm.delete(realm.objects(RealmBook.self))
        }
    }

    func deleteById(id: String) {
        let object = getById(id)
        try! realm.write {
            realm.delete(object)
        }
    }
}

But wait, did you see that? I am cheating here, although the offered interface works with the object ‘Book’, internally it is in fact saving and retrieving a different object, it is… ‘RealmBook’.

What are then these ‘RealmBook’ (and ‘RealmAuthor’ as you will see) objects?

They are basically 1 to 1 mapping objects of our Entities, but… the main difference is that they can contain all the “dirty” specific details of our frameworks that we don’t want to propagate around our App. These are our NSManagedObjects or in the case of RealmObjects (forced to be classes, inheritance from Object, forced to use List instead of Arrays, etc…). We can keep all of these things right here hidden in our RealmBook object.

class RealmBook: Object {
    dynamic var title: String = “”
    dynamic var isbn: String = “”
    dynamic var publishDate: NSDate = NSDate()
    var authors = List<RealmAuthor>()

    convenience init(book: Book) {
        self.init()
        title = book.title
        isbn = book.isbn
        publishDate = book.publishDate
        authors = List(book.authors.map { RealmAuthor(author: $0) })
    }

    var entity: Book {
        return Book(title: title,
                    isbn: isbn,
                    publishDate: publishDate,
                    authors: authors.map { $0.entity })
    }
}

class RealmAuthor: Object {
    dynamic var let name: String = “”
    dynamic var let surname: String = “”

    convenience init(author: Author) {
        self.init()
        name = author.name
        surname = author.surname
    }

    var entity: Author {
        return Author(name: name,
                      surname: surname)
    }
}

And from the rest of our App, we can simply work with the protocol ‘DataSource’ using our Entities, no matter how persistence is internally implemented. Just as magic…

image

And just as a disclaimer, I wanted to keep it simple for the example, but did you hear this BooksRealmDataSource? no? It is screaming really loud for Generics isn’t it? 🙃

Repository Pattern

Now that we don’t have to worry about frameworks… Imagine the scenario of having all the data sources of your App implementing the previous protocol.

I mean having always access to the information in exactly the same way, no matter from where it is in the end coming. Disk, Memory, or even Network. All of them coming from exactly the same protocol.

It would be super easy to build a Repository Pattern which iterating through the different Data Sources could get the data from the fastest or more appropriate source for each case.

The implementation of our Repositories is again asking for generics, just receiving the list of Data Sources that it should manage and a strategy to apply to them.

For example a Repository to get our list of Books in the previous example, could have 3 different Data Sources: Memory, Disk and Network. With an strategy of first match.

But a Repository to do an user login, would have only one Data Source (the Network) to assure that we are always validating the credentials with our backend.

image

In the end, from the rest of our App, it means just getting data as magic, no matter from where the data is coming, or what frameworks are being used internally. Just what we want, the data.

And as I said, the best part is that following this pattern we get a huge modularity and we could change really easily our internal implementation, right when we want. Do you want to give X new trendy framework a try? well, just change the implementation of one of your Data Sources and the rest of the App won’t requiere any change at all.