如何在应用程序开始运行代码之前运行迁移?

我在一个快速的应用程序中使用realm.io。 这是我第一次运行迁移,因为我有一个应用程序正在生产中。 我更改了其中一个模型并添加了几个额外的字段。

我按照文档中的示例进行操作,然后在不起作用的情况下引用了github repo的示例。 我认为它可能比文档中的示例更加复杂。

这是我在appdelegate.swift文件中的内容:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { print ("i'm in here") // Override point for customization after application launch. // Inside your application(application:didFinishLaunchingWithOptions:) window = UIWindow(frame: UIScreen.mainScreen().bounds) window?.rootViewController = UIViewController() window?.makeKeyAndVisible() // copy over old data files for migration let defaultPath = Realm.Configuration.defaultConfiguration.path! let defaultParentPath = (defaultPath as NSString).stringByDeletingLastPathComponent if let v0Path = bundlePath("default-v0.realm") { do { try NSFileManager.defaultManager().removeItemAtPath(defaultPath) try NSFileManager.defaultManager().copyItemAtPath(v0Path, toPath: defaultPath) } catch {} } 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 } }) // define a migration block // you can define this inline, but we will reuse this to migrate realm files from multiple versions // to the most current version of our data model let migrationBlock: MigrationBlock = { migration, oldSchemaVersion in if oldSchemaVersion < 1 { } print("Migration complete.") } Realm.Configuration.defaultConfiguration = Realm.Configuration(schemaVersion: 1, migrationBlock: migrationBlock) // 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 _ = try! Realm() return true } 

print从未运行,我觉得很奇怪。 我搞砸了什么? 我假设我一定是。

这是文档说要做的,我不确定他们是否留下了一些东西:

 // 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() 

我有什么想法我做错了吗?

这最终成为了解决方案。 我不能说我自己想出来因为我没有。 我得到了一位名叫克莱尔的杰出工程师的出色帮助。

这是需要做的事情:

 class RoomsViewController: UIViewController, UITableViewDelegate { var activeRoom = -1 var room: Room? = nil var array = [Room]() lazy var realm:Realm = { return try! Realm() }() var notificationToken: NotificationToken? @IBOutlet weak var roomsTable: UITableView! override func viewDidLoad() { super.viewDidLoad() array = Array(realm.objects(Room.self)) setupUI() // Set realm notification block notificationToken = realm.addNotificationBlock { [unowned self] note, realm in // TODO: you are going to need to update array self.roomsTable.reloadData() } } 

这是第一个加载的视图控制器,它立即查询领域数据库以构建数组。 根据Claire的建议,Realm是懒惰加载(或尝试过),构建数组的代码被移动到viewDidLoad方法中,而在顶部调用它之前。

这使得领域可以向前运行。 正如您可能想象的那样,我老实说假设视图从未加载到AppDelegate加载的applicationfunction之后。 但是,我猜我错了。

所以,这将解决它。 如果您遇到同样的问题,请试一试。

更新:

以下是appDelegate函数现在的样子:

 func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { // 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) { migration.enumerate(Inventory.className()) { oldObject, newObject in // No-op. // dynamic properties are defaulting the new column to true // but the migration block is still needed } migration.enumerate(Profile.className()) { oldObject, newObject in // No-op. // dynamic properties are defaulting the new column to true // but the migration block is still needed } migration.enumerate(Room.className()) { oldObject, newObject in // No-op. // dynamic properties are defaulting the new column to true // but the migration block is still needed } migration.enumerate(Box.className()) { oldObject, newObject in // No-op. // dynamic properties are defaulting the new column to true // but the migration block is still needed } } }) // 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 do { _ = try Realm() } catch let _ as NSError { // print error } return true } 

看起来你似乎将Realm Swift’Migration’示例中的代码复制出来并逐字粘贴。 很多代码是每次运行示例应用程序时实际“设置”一个新的演示迁移,而实际上并不需要正常的Realm迁移。

Realm迁移有两个组件:

  1. 将模式版本号压缩在Configuration对象中。 领域文件从版本0开始,每次要进行新迁移时,将其增加1。

  2. 提供一个迁移块,该块将针对Realm架构版本的每个增量执行多次。 虽然块本身是必需的,但其目的是让您复制/转换旧Realm文件中的任何数据。 如果您只是添加新字段,则可以将该块留空。

如果您的UIViewController在其实现中完全使用Realm,最好将迁移代码放在UIWindow代码之前,以确保在UIViewController开始使用它之前已经发生了迁移(否则您将获得exception)。

由于你所做的只是为它添加一些字段,这就是你需要的所有代码:

 let migrationBlock: MigrationBlock = { migration, oldSchemaVersion in //Leave the block empty } Realm.Configuration.defaultConfiguration = Realm.Configuration(schemaVersion: 1, migrationBlock: migrationBlock) window = UIWindow(frame: UIScreen.mainScreen().bounds) window?.rootViewController = UIViewController() window?.makeKeyAndVisible() 

这可以按预期工作,在降级时抛出exception(如果使用版本不同的分支)等。也会显示一些示例迁移。

  Realm.Configuration.defaultConfiguration = Realm.Configuration( // Update this number for each change to the schema. schemaVersion: 4, migrationBlock: { migration, oldSchemaVersion in if oldSchemaVersion < 2 { migration.enumerate(SomeObject.className()) { oldObject, newObject in newObject!["newColumn"] = Enum.Unknown.rawValue } } if oldSchemaVersion < 3 { migration.enumerate(SomeOtherObject.className()) { oldObject, newObject in newObject!["defaultCreationDate"] = NSDate(timeIntervalSince1970: 0) } } if oldSchemaVersion < 4 { migration.enumerate(SomeObject.className()) { oldObject, newObject in // No-op. // dynamic properties are defaulting the new column to true // but the migration block is still needed } } }) 

显然,不会像SomeObject等那样编译,但你明白了:)