iosswiftmigrationrealmrealm-migration

How to perform migration of LinkingObject?


I need to perform migration , without losing parent/owner .How to do that in migration block?

Before Migration

class Deck : Object {

    @objc dynamic var name : String = ""

    var cards = List<Card>()

}
class Card : Object {

    @objc dynamic var text = ""

    var parentCategory = LinkingObjects(fromType: Deck.self, property: "cards")

}

After Migration

class Deck : Object {

    @objc dynamic var name : String = ""

    let cards = LinkingObjects(fromType: Card.self, property: "owner")

}
class Card : Object {


    @objc dynamic var text = ""

    @objc dynamic var owner : Deck?


}



In a new version of app when I'm making a new card , I'm setting parent like this :

let decks = realm.objects(Deck.self)
owner = decks[index]

I just need to do this , but in context of Migration :

 let realm = try! Realm()
        let decks : Results<Deck>? = realm.objects(Deck.self)
        do {
            try realm.write {
                if decks!.count > 0 {
                    for indexOfDeck in 0...decks!.count - 1 {
                        if decks![indexOfDeck].cards.count > 0 {
                            for indexOfCard in 0...decks![indexOfDeck].cards.count - 1 {
                                                       decks![indexOfDeck].cards[indexOfCard].owner = decks![indexOfDeck]

                                                   }
                        }

                    }
                }

            }

        }catch{

    } 

Solution

  • The challenge with this question is that LinkingObjects are not persisted - they are calculated so they cannot be directly migrated.

    The second challenge is the original card objects doesn't have any persisted properties other than the 'text' var.

    Here's the code that will does the job. We are getting the new Decks cards property, iterating over them and updating each owner property in the Card object to point back to the new Deck.

    As soon as a card's owner property is set to that deck, that Decks linkingCards property will 'pick that up' because it's calculated.

    let vers = UInt64(1)
    let config = Realm.Configuration( schemaVersion: vers, migrationBlock: { migration, oldSchemaVersion in
         print(oldSchemaVersion)
         if (oldSchemaVersion < vers) {
            print("  performing migration")
    
             migration.enumerateObjects(ofType: Deck.className()) { oldDeck, newDeck in
                let cards = newDeck!["cards"] as! List<MigrationObject>
                for card in cards {
                    card["owner"] = newDeck
                }
             }
         } else {
            print("no migration needed")
        }
     })
    

    My Original objects look like this, which should match the ones in the Question

    class Deck : Object {
        @objc dynamic var name : String = ""
        var cards = List<Card>() //current
    }
    
    class Card : Object {
        @objc dynamic var text = ""
        var parentCategory = LinkingObjects(fromType: Deck.self, property: "cards") //current
    }
    

    and then the updated objects look like this

    class Deck : Object {
        @objc dynamic var name : String = ""
        var cards = List<Card>() //current
        let linkingCards = LinkingObjects(fromType: Card.self, property: "owner") //new
    }
    
    class Card : Object {
        @objc dynamic var text = ""
        var parentCategory = LinkingObjects(fromType: Deck.self, property: "cards") //current
        @objc dynamic var owner : Deck? //new
    }