Realm.io RealmSwift 其他领域迁移

Realm.io RealmSwift Other Realms Migration

我需要为我的 Other Realm 进行迁移。

我通过这种方法获得了我的领域路径 (AppDelegate)。如果用户之前登录过,我将检索用户的领域,否则我将只使用 Default Realm.

 func getRealmPath() -> String {
    let preferences : NSUserDefaults = NSUserDefaults()
    let username = preferences.objectForKey(usernameKey) as! String?

    if username != nil {
        let realmName =  ("\(username!).realm")

        print("RealmName: \(realmName)", terminator: "")

        let documents = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString
        return documents.stringByAppendingPathComponent(realmName)
    }else{
        return Realm.Configuration.defaultConfiguration.path!
    }  
}

我通过这种方法进行了迁移(在 AppDelegate:didFinishLaunchingWithOptions 内部调用)。

func updateRealm(){

    let config = Realm.Configuration(path: getRealmPath(), schemaVersion: 2, migrationBlock: { (migration, oldSchemaVersion) -> Void in

        print("oldSchemaVersion \(oldSchemaVersion)") 

         migration.create("RLMStringTimestamp", value: ["pKey": NSUUID().UUIDString, "value": "", "updatedAt": NSDate(), "createdAt": NSDate(), "deletedAt": Date().getInitDate(), "updatedBy" : " ", "syncedAt": NSDate() ])           

        if oldSchemaVersion < 2  {

           //MIGRATION
           let firstNameTimeStamp =  RLMStringTimestamp(newValue: oldObject!["firstName"] as? String)
           migration.create("RLMStringTimestamp", value: firstNameTimeStamp)
           newObject!["firstName"] = firstNameTimeStamp

        }
      }

      Realm.Configuration.defaultConfiguration = config

      //EDIT 2 
      Realm.Configuration.defaultConfiguration.path = getRealmPath()

     //EDIT 1
     //<strike>let realm = try! Realm(path: getRealmPath())</strike>

     //EDIT 4 
     print(Realm.Configuration.defaultConfiguration)

     //EDIT 3
     let realm = try! Realm()
     }

For my RLMCustomer Object, i modified var firstName: String = "" to var firstName: RLMStringTimeStamp!

即使我将 schemaVersion 更改为非常高的值,migrationBlock 也没有接到电话。谁能帮我找出我遗漏了什么或做错了什么?

应用 运行 后,它崩溃 bad excess, code = 257

编辑 1:

error: 'try!' expression unexpectedly raised an error: Error Domain=io.realm Code=0 "Provided schema version 0 is less than last set version 1." UserInfo=0x170660c00 {NSLocalizedDescription=Provided schema version 0 is less than last set version 1.}: file /Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-700.0.59/src/swift/stdlib/public/core/ErrorType.swift, line 50

似乎读取了错误的配置文件,我怀疑错误是由于 Realm.Configuration.defaultConfiguration = config 如何设置 Other Realm 的配置?

编辑 2:

I make my default realm to contains the name and path of my other realm

编辑 4:

看来配置文件是正确的。如果没有来自旧领域的客户记录,我可以 运行 该应用程序而不会出现问题。它只会在旧领域有客户记录时崩溃。我可以从 oldObject["firstName"]

中获取值

print(Realm.Configuration.defaultConfiguration)

Realm.Configuration { path = /var/mobile/Containers/Data/Application/8670C084-75E7-4955-89FB-137620C9B00D/Documents/perwyl.realm; inMemoryIdentifier = (null); encryptionKey = (null); readOnly = 0; schemaVersion = 2; migrationBlock = <NSMallocBlock: 0x170451220>; dynamic = 0; customSchema = (null); } oldSchemaVersion 0

非常感谢!!!

编辑 5:解决我的问题

I'm not sure why it crashes if i assign StringTimestamp object directly to newObject.

let firstName = (oldObject!["firstName"] as? String)!
let firstNameTimeStamp =  StringTimestamp(newValue: firstName)
let testName = migration.create("StringTimestamp",value:     firstNameTimeStamp)
newObject!["firstName"] = firstNameTimeStamp //Crashes
newObject!["firstName"] = testName //works 

感谢大家的指导! :)

为什么不让 realm 使用默认配置?由于您将默认配置中的路径设置为 getRealmPath(),因此应该可以这样做:

 let realm = try! Realm()

通过使用 Realm(path: getRealmPath()) 实例化 领域 ,您将覆盖之前设置的 defaultConfiguration。也就是说,领域的路径变为 getRealmPath(),但您在 config 中设置的所有其他属性都将丢失,取而代之的是使用默认值。这包括 schemaVersion = 0migrationBlock = nil.

Realm(path:)Realm(configuration:) 等初始化器的目的是允许您使用默认配置以外的替代配置。如果您想要使用默认配置的修改版本,那么您需要执行以下操作:

// Get a copy of the default configuration
var otherConfig = Realm.Configuration.defaultConfiguration
// Update the copy with what you need
otherConfig.path = "/some/path/otherRealm.realm"
// Use the updated configuration to instantiate a realm
let otherRealm = try! Realm(configuration: otherConfig)

调试领域配置问题的一种巧妙方法是在实例化领域之前设置断点或打印日志。 运行 下面的代码让我知道要使用的默认配置是什么。

print(Realm.Configuration.defaultConfiguration))
let realm = try! Realm()

输出类似于

Realm.Configuration {
    path = /Users/<full-path-omitted>/edman.realm;
    schemaVersion = 2;
    migrationBlock = <__NSMallocBlock__: 0x7faee04ac590>;
    // other properties...
}

通过查看它,我确定我的 领域 是用 edman.realmschemaVersion = 2 的路径实例化的,并且它有非零 migrationBlock.

每当您的架构更新时,您都必须在领域配置中将架构版本更新为更大的值。也就是说,在您的 updateRealm() 函数中

let config = Realm.Configuration(
    path: getRealmPath(),
    schemaVersion: 3, // any value larger than previous version
    migrationBlock: { /* ... */}
})

Realm.Configuration.defaultConfiguration = config
let realm = try! Realm()

来自realm docs.

You define a migration and the associated schema version by setting Realm.Configuration.schemaVersion and Realm.Configuration.migrationBlock.

When creating a Realm with this configuration, the migration block will be applied to update the Realm to the given schema version if a migration is needed.

您正确设置了迁移块,但没有更新架构版本。这就是迁移块未被执行的原因,尽管它已在您的配置中提供。

磁盘中的版本与您配置中的版本之间的差异是触发 migrationBlock 的原因。

Swift 3.0 解决方案

class func realmConfigs(_ realmName: String) -> RealmSwift.Realm.Configuration?{
    var config = Realm.Configuration()
    config.readOnly = true
    // Use the default directory, but replace the filename with the username
    let fileURLString = Bundle.main.path(forResource: realmName, ofType: "realm")
    guard fileURLString != nil else {
        return nil
    }
    config.fileURL = URL(string: fileURLString!)
    config.schemaVersion = UInt64(1.0)
    // Set this as the configuration used for the default Realm

    return config
}

并且会这样使用

    var realm : Realm?
    do{

        let config = realmConfigs("FILE_NAME")
        Realm.Configuration.defaultConfiguration = config!
        realm = try Realm(fileURL: realmURL)
    }
    catch {
        print("Opening realm file error: \(error)")
    }