如何执行更好的 SwiftRealm 迁移值

问题描述

我在 iOS 和 Android 上使用 Realm Swift 和 realm-java。我需要实现数据库迁移来更新数据库中的值。 在 Swift 上,迁移按如下方式完成:currentVersion -> latestVersion。在 Android 上:v1 -> v2 -> latestVersion。

以下示例显示了 iOS 上的迁移 0 -> 1 和 1 -> 2:

  • 0 -> 1 = V1:创建一个新的奇幻电影类别(由 两部电影已经存在于数据库中)
  • 1 -> 2 = V2:添加新的 创造了“神奇”类别的电影

迁移 0 -> 1 :



    /// Function to migrate version 0 to version 1.
    /// Create Fantastic film category and associated 
    ///
    /// - Parameter migration: Migration instance.
    static func migrateFrom0To1(_ migration: Migration) {
        // Film to add for Fantastic category.
        let existingFilmToAdd: [String] = ["STAR_WARS","HARRY_PottER"]
        // Get stored films.
        var filmToSet: [MigrationObject] = []
        migration.enumerateObjects(ofType: Film.className()) { _,filmDb in
            let key = filmDb!["filmKey"] as! String
            if existingFilmToAdd.contains(key) {
                filmToSet.append(filmDb!)
            }
        }
        // Create Fiction Category.
        let fantasticCategory = migration.create(Category.className())
        fantasticCategory["categoryKey"] = "FANTASTIC"
        fantasticCategory["name"] = "Fantastic"
        fantasticCategory["films"] = filmToSet
    }

迁移 0/1 -> 2



    /// Function to migrate version 0 to version 1.
    /// Create Fantastic film category and associated 
    ///
    /// - Parameter migration: Migration instance.
    static func migrateFrom0To1(_ migration: Migration) {
        // Film to add for Fantastic category.
        let existingFilmToAdd: [String] = ["STAR_WARS","HARRY_PottER"]
        // Create Superman Film
        let superman = migration.create(Film.className())
        superman["filmKey"] = "SUPERMAN"
        superman["name"] = "Superman"
        superman["time"] = 2 
        // Get stored films.
        var filmToSet: [MigrationObject] = []
        migration.enumerateObjects(ofType: Film.className()) { _,filmDb in
            let key = filmDb!["filmKey"] as! String
            if existingFilmToAdd.contains(key) {
            filmToSet.append(filmDb!)
            }
        }
        filmToSet.append(superman)
        // Create Fiction Category.
        let fantasticCategory= migration.create(Category.className())
        fantasticCategory["categoryKey"] = "FANTASTIC"
        fantasticCategory["name"] = "Fantastic"
        fantasticCategory["films"] = filmToSet
    }
    
    /// Function to migrate version 1 to version 2.
    /// Add film in Fantastic Category 
    ///
    /// - Parameter migration: Migration instance.
    static func migrateFrom1To2(_ migration: Migration) {
        // Create Superman Film
        let superman = migration.create(Film.className())
        superman["filmKey"] = "SUPERMAN"
        superman["name"] = "Superman"
        superman["time"] = 2 
        // Get stored films.
        var filmToSet: [MigrationObject] = []
        // Update Fantastic film
        migration.enumerateObjects(ofType: Category.className()) { _,migrationCategory in
            let categoryKey = migrationMarket!["categoryKey"] as! String
            if categoryKey == "FANTASTIC" {
                let oldFilm = migrationCategory!["films"] as! List
                for film in oldFilm {
                   filmsToSet.append(film)
                }
                filmsToSet.append(superman)
                migrationCategory!["films"] = filmsToSet
            }
        }
    }

  • 如果应用程序已经安装在V1,迁移1 -> 2将被执行,很简单,这个迁移创建一个新的超人电影并将其添加到之前存储在数据库中的Fantastic Category中。

  • 如果是第一次安装: 0 -> 2 将被执行。 Fantastic Category 是在 0 -> 1 中创建的,但在迁移 1 -> 2 中尚不可用,因为迁移尚未完成。我必须修改函数0 -> 1迁移函数来创建超人电影并在创建时添加到Fantastic Category。

这个例子很简单,因为有简单的数据,但是当我们有更多的价值要添加修改时,它会使迁移变得越来越复杂。

在 Android 上没有问题,迁移会相互进行,因为数据在每个迁移功能结束时都可以在数据库中使用。 Swift 上的迁移实施变得越来越复杂。

我想让 Swift 迁移像 Android 迁移一样运行。查看文档和不同的票后,我找不到解决方案。您有解决问题的解决方案或新架构吗?

注意:解决方案必须始终为我提供迁移数据库架构的可能性

解决方法

您可以使用类似的方法来迁移您的领域。

Realm.Configuration.defaultConfiguration = Realm.Configuration(schemaVersion: 2,migrationBlock: { (migration,oldSchemaVersion) in
                                                    migrate(oldScema: oldSchemaVersion,migration: migration)
                                                })

在 migrate(oldScema:,migration:) 函数中,您可以检查旧模式版本并添加相应的操作。例如:

func migrate(oldScema: Int,migration: Migration) {
    if oldScema < 1 {
        // Migrate 0 -> 1
    }

    if oldScema < 2 {
        // Migrate 1 -> 2
    }
    
    // etc for other migrations
}
,

我找到了答案: 我通过在循环中使用 performMigration 从 oldSchemaVersion 开始逐个到新的 Schema 版本来进行多次小迁移。

/// Define migration block.
private let migrationBlock: MigrationBlock = { migration,oldSchemaVersion in
    switch oldSchemaVersion {
    case 0:
        Migrations.migrateFrom0To1(migration)
    case 1:
        Migrations.migrateFrom1To2(migration)
    case 2:
        Migrations.migrateFrom2To3(migration)
    case 3:
        Migrations.migrateFrom3To4(migration)
    case 4:
        Migrations.migrateFrom4To5(migration)
    case 5:
        Migrations.migrateFrom5To6(migration)
    default:
        break
    }
}

/// Initialize realm configuration.
///
/// - Throws: If error during realm configuration.
private func initializeRealm(realmConfiguration: Realm.Configuration?) throws {
    // Init Realm configuration.
    var config: Realm.Configuration
    if realmConfiguration == nil {
        config = Realm.Configuration(schemaVersion: currentSchemaVersion)
        // Get current schema version of the database before migration.
        let oldSchemaVersion = try schemaVersionAtURL(config.fileURL!)
        print("oldSchemaVersion: \(oldSchemaVersion) currentSchemaVersion: \(currentSchemaVersion)")

        // Perform all migrations one by one
        // (old version to new version) and keep last config.
        if oldSchemaVersion < currentSchemaVersion {
            for tmpCurrentSchemaVersion in oldSchemaVersion...currentSchemaVersion {
                config = Realm.Configuration(schemaVersion: tmpCurrentSchemaVersion,migrationBlock: migrationBlock)
                try! Realm.performMigration(for: config)
            }
        }
    } else {
        config = realmConfiguration!
    }

    self.realm = try! Realm(configuration: config)
}