dbmrq
dbmrq

Reputation: 1461

Copying all objects (preserving their relationships) from one Realm to another

tl;dr: I'm trying to copy every single object from one Realm to another, but I get twice as many objects or 4 times as many objects as I should (because of their relationships, I presume). (Edit: I actually get many many more! Check my last edit at the bottom.)

I'm trying to allow my users to backup and restore their Realm databases.

I have a Book class and a ReadingSession class. A Book can have many ReadingSessions:

class Book: Object {
    // (…)
    var readingSessions: [ReadingSession] {
        return linkingObjects(ReadingSession.self, forProperty: "book")
    }
}

class ReadingSession: Object {
    // (…)
    var book: Book?
}

To restore from the backup I tried doing this:

func restoreBackupFile(backupFileToRestore: String) {
    // (…) I omitted the NSFileManager related part.
    let config = Realm.Configuration(path: "\(tmp)/ReadingLog.realm", readOnly: true)
    let backupRealm = try! Realm(configuration: config)
    let defaultRealm = try! Realm()

    let results = backupRealm.objects(Book)

    try! defaultRealm.write {

        for result in results {
            defaultRealm.create(Book.self, value: result)
        }
    }

That copied all the Book objects alright, but not the ReadingSessions related to those Books. So I added some code to copy every ReadingSession too:

    // (…)
    let bookResults = backupRealm.objects(Book)
    let sessionResults = backupRealm.objects(ReadingSession)

    try! defaultRealm.write {

        for result in bookResults {
            defaultRealm.create(Book.self, value: result)
        }
        for result in sessionResults {
            defaultRealm.create(ReadingSession.self, value: result)
        }
    }

And that gave my defaultRealm 4 times as much books as it should! I used a database with 10 books to test it out, and after running that code my default Realm had 40 books, 20 with the right ReadingSessions associated to them and 20 without any ReadingSessions at all.

I tried copying just the ReadingSessions to see if the related Books would be created too, and then I got twice as many Books as I should, half of them with the right ReadingSessions and half of them without any.

So how can I do what I want? Copy every single object from a Realm to another keeping their relationships intact and without getting duplicates like I am now?

(I know I can just replace the database files, but I want my users to be able to restore data from one database without losing the data from the other.)

Thanks in advance,

Daniel


Edit: I've been experimenting some more and it seems that if I copy just the ReadingSessions it creates a book for each reading session copied, even if they're related to the same book. If I have a book with 60 reading sessions, for instance, that book will be created 60 times. So my problem is even worse than I thought. When I copy the Books they don't come with their related ReadingSessions. When I copy the ReadingSessions they create many repeated books.

Upvotes: 2

Views: 1067

Answers (1)

marius
marius

Reputation: 7806

When you create new objects on base of objects from another Realm, then linked objects are copied as well and recursed into. But backreferences are just convenience getters on your object and neither known to the schema nor accessible (for Swift), so they are not traversed at all. Even though linked objects are recursed into, they are not de-duplicated at all automatically. So this would loop for circular relations.

You might want to configure a primary key on Book and use create(… update: true) to copy your ReadingSessions. When you copy both, you ensure, that unread books are copied as well, if that's a valid case for your schema.

Upvotes: 2

Related Questions