Migration Realm: ajout d'une nouvelle propriété dans la class

Bonjour,

je continue le cours sur Realm. J’ai crée une class Coureurs avec comme « objets name, prénom, classe et sexe ». J’ai ma classe coureursManager qui permet les ajouts et tout et tout.
Je veux ajouter un nouvel objet « time » à ma classe Coureurs. Je vu qu’il faut faire une migration.
https://realm.io/docs/swift/latest/#migrations

// Inside your application(application:didFinishLaunchingWithOptions:)
let config = Realm.Configuration(
// Set the new schema version. This must be greater than the previously used
// version (if you’ve never set a schema version before, the version is 0).
schemaVersion: 1,

// Set the block which will be called automatically when opening a Realm with
// a schema version lower than the one set above
migrationBlock: { migration, oldSchemaVersion in
    // We haven’t migrated anything yet, so oldSchemaVersion == 0
    if (oldSchemaVersion < 1) {
        // Nothing to do!
        // Realm will automatically detect new properties and removed properties
        // And will update the schema on disk automatically
    }
})

// Tell Realm to use this new configuration object for the default Realm
Realm.Configuration.defaultConfiguration = config

// Now that we’ve told Realm how to handle the schema change, opening the file
// will automatically perform the migration
let realm = try! Realm()

En regardant la doc Realm j’ai essayé de modifier le code en mettant à jour et en ajoutant mon nouvel objet « time » mais ça ne marche pas. Je l’ai mis dans init de ma classe timeManager (est-ce bien là?)

init() {
Realm.Configuration.defaultConfiguration = Realm.Configuration(
schemaVersion: 1,
migrationBlock: { migration, oldSchemaVersion in
if (oldSchemaVersion < 1) {
// The enumerateObjects(ofType:_: ) method iterates
// over every Person object stored in the Realm file
migration.enumerateObjects(ofType: coureurs.className()) { oldObject, newObject in
// combine name fields into a single field
newObject![« time »] = «  »
}
}
})

Il faut que tu mettes les infos de migration dans la Configuration utilisée pour créer tes objets Realm.
Si tu les créés sans aucun paramètre dans ton app (let realm = try! Realm()) alors il te faut faire en sorte de changer la config par défaut avant ta première création d’objet realm avec cette ligne Realm.Configuration.defaultConfiguration = config

Si, avant de créer cette configuration, tu as créé un objet Realm et qu’une migration est nécessaire alors tu auras une exception. Comme je ne sais pas quand est utilisée ta fonction init() de TimeManager je ne peux pas te garantir que le timing soit bon.

La solution la plus sûre est de déclencher cette création de config dans ton AppDelegate dans la fonction application(application:didFinishLaunchingWithOptions:)

J’ai un soucis avec cette méthode.
En effet, mon app démarre directement sur une sous-classe de TableViewController qui contient une variable de type Realm.
Et il semble que cet object soit créé avant que soit appelée application:didFinishLaunchingWithOptions:
Du coup, la migration n’a pas lieu et l’application plante.
J’ai vu qu’il existe une fonction application:applicationWillFinishLaunchingWithOptions:
Y a-t-il une raison qui m’empêcherait d’effectuer la migration dans applicationWillFinishLaunchingWithOptions plutôt que dans applicationDidFinishLaunchingWithOptions ?

Je suppose que ta variable de type Realm est remplie dès l’initialisation de la classe ? Les view controlleurs sont initialisés avant les callbacks du app delegate donc une solution serait d’utiliser la fonction viewDidLoad de ton TableViewController plutôt que de créer ton Realm en même temps que la variable. Le viewDidLoad est appelé plus tard donc ce sera bon. Tu devras par contre créer ta variable en type optionnel si tu attends le viewDidLoad pour la remplir

J’ai essayé cette option (déclaration d’une variable optionnelle puis création de cette variable dans le viewDidLoad) mais j’ai le même problème.
En effet, il semble que le viewDidLoad du ViewController initial soit appelé avant la migration du AppDelegate.

J’y comprend rien…

De ma classe initiale Coureurs
class Coureurs: Object { @objc dynamic private var _name = "" @objc dynamic private var _prenom = "" @objc dynamic private var _sexe = "" @objc dynamic private var _classe = "" @objc dynamic private var _time = ""

Je veux ajouter:
@objc dynamic private var _team = “”

j’ai donc fait dans l’AppDelegate, en regardant sur Realm.io:

import UIKit

import RealmSwift

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?


func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
   
    Realm.Configuration.defaultConfiguration = Realm.Configuration(
        schemaVersion: 1,
        migrationBlock: { migration, oldSchemaVersion in
            if (oldSchemaVersion < 1) {
                // The enumerateObjects(ofType:_:) method iterates
                // over every Person object stored in the Realm file
                migration.enumerateObjects(ofType: Coureurs.className()) { oldObject, newObject in
                    // combine name fields into a single field
                    let name = oldObject!["_name"] as! String
                    let prenom = oldObject!["_prenom"] as! String
                    let sexe = oldObject!["_sexe"] as! String
                    let classe = oldObject!["_classe"] as! String
                    let time = oldObject!["_time"] as! String
                    newObject!["_team"] = "_team"
                }
            }
    })
    return true
}

Mais ça ne fonctionne pas.

Désolé, je me suis trompé.
Mon premier ViewController est en fait embedded dans un TabBarController.
Il faut donc faire la modification dans tous les ViewControllers qui y sont liés.
Merci Maxime, ça fonctionne maintenant.

1 « J'aime »

Salut Jean-charles,

A quoi te servent toutes ces affectations ? J’ai l’impression que tu n’utilises pas ces variables name, prenom, etc.

Quel nom d’équipe tu voudrais affecter lors de la migration ? Si tu veux commencer avec un nom vide, tu n’as rien à faire de spécial à part donner un bloc de migration vide.
Si tu veux y affecter le contenu de ta variable _team alors il ne te faut pas de guillemets autour sinon tu enregistres _team comme nom d’équipe.
Quelle est la valeur de cette variable d’ailleurs car je ne vois pas son code de déclaration ni d’affectation ?