Как добавить новое поле в объект области во время миграции и проверить успешность миграции? - PullRequest
0 голосов
/ 14 сентября 2018

Итак, у меня есть следующий объект области в схеме версии 3 моего приложения:

public class MyObjContent: Object {
    @objc public dynamic var id: String?
    @objc public dynamic var userID: String?
    @objc public dynamic var compoundKey: String = "-"
    @objc public dynamic var tileId: String?
    @objc public dynamic var detailTileId: String?
    @objc public dynamic var pushMessage: String?
    @objc public dynamic var exposeDate: Date?
    @objc public dynamic var lastShown = Date.distantPast
    @objc public dynamic var shownCount = 0

    ...
}

В моей новой версии приложения я обновляю до версии схемы 4 и добавьте это новое поле:

@objc public dynamic var url: String?

, и моя модель теперь:

public class MyObjContent: Object {
        @objc public dynamic var id: String?
        @objc public dynamic var userID: String?
        @objc public dynamic var compoundKey: String = "-"
        @objc public dynamic var tileId: String?
        @objc public dynamic var detailTileId: String?
        @objc public dynamic var pushMessage: String?
        @objc public dynamic var exposeDate: Date?
        @objc public dynamic var lastShown = Date.distantPast
        @objc public dynamic var shownCount = 0
        @objc public dynamic var url: String?

        ...
    }

Я хочу проверить, успешно ли выполнен переход на новую модель и что существующий объект в моемфайл области успешно перенесен (добавлено новое поле): вот что я пробовал:

func testMigration_PropertiesSuccess() {
    // given

    let config = getRealmConfiguration(schemaVersion: 3, realmFileName: testRealm)

    do {
        try autoreleasepool {

            let realm = try Realm(configuration: config)

            let content = MyObjContent()
            content.id = "1"
            content.userID = testUser
            content.pushMessage = "Msg before migration"

            try realm.write {
                realm.add(content)
            }
            let prop = RLMProperty(name: "url", type: RLMPropertyType.string, objectClassName: nil,
                                   linkOriginPropertyName: nil, indexed: false, optional: true)

            _ = addPropertiesToObjectSchema(className: "MyObjContent", realm: realm, properties: [prop])
        }
    } catch {
        XCTFail(error.localizedDescription)
    }

    // when
    migrateAndTestRealm(testRealmURL(), schemaVersion: 4) { migration, _ in

        // then
        print("------- old schema: \(migration.oldSchema.objectSchema)\n")
        print("------- new schema: \(migration.newSchema.objectSchema)\n")

        XCTAssertGreaterThan(migration.newSchema.objectSchema.count, 1)
        XCTAssertEqual(migration.oldSchema["MyObjContent"]?.properties.count, 9)
        XCTAssertEqual(migration.newSchema["MyObjContent"]?.properties.count, 10)
        XCTAssertEqual(migration.newSchema["MyObjContent"]?["url"]?.type, PropertyType.string)

    }

    // read data from realm on disk

    do {
        try autoreleasepool {
            let config = getRealmConfiguration(schemaVersion: 4, realmFileName: testRealm)
            let realm = try Realm(configuration: config)
            print("\n------existing objects: \(String(describing: realm.objects(MyObjContent.self)))")
        }
    } catch {
        XCTFail(error.localizedDescription)
    }
}

, где :

private func addPropertiesToObjectSchema(className: String, realm: Realm, properties: [AnyObject]) -> RLMRealm {
        let existingSchema = realm.schema.objectSchema
        guard let objectSchema = existingSchema.first(where: { $0.className == className }) else {
            let config = RLMRealmConfiguration()
            config.fileURL = realm.configuration.fileURL
            config.schemaVersion = realm.configuration.schemaVersion
            //swiftlint:disable force_try
            return try! RLMRealm(configuration: config)
            //swiftlint:enable force_try
        }
        var objectProperties = objectSchema.properties as [AnyObject]
        print("object properties: \(objectProperties)")
        objectProperties.append(contentsOf: properties)

        let newObjSchema = RLMObjectSchema(className: className, objectClass: MigrationObject.self, properties: objectProperties)
        let schema = RLMSchema()
        schema.objectSchema = [newObjSchema]
        let config = RLMRealmConfiguration()
        config.schemaVersion = realm.configuration.schemaVersion
        config.fileURL = realm.configuration.fileURL
        config.customSchema = schema
        //swiftlint:disable force_try
        return try! RLMRealm(configuration: config)
        //swiftlint:enable force_try
}

и

private func migrateAndTestRealm(_ fileURL: URL,
                                     shouldRun: Bool = true,
                                     schemaVersion: UInt64 = 1,
                                     autoMigration: Bool = false,
                                     block: MigrationBlock? = nil) {
        var didRun = false
        let config = Realm.Configuration(fileURL: fileURL, schemaVersion: schemaVersion,
                                         migrationBlock: { migration, oldSchemaVersion in
                                            if let block = block {
                                                block(migration, oldSchemaVersion)
                                            }
                                            didRun = true
                                            return
        })

        if autoMigration {
            autoreleasepool {
                _ = try? Realm(configuration: config)
            }
        } else {
            try? Realm.performMigration(for: config)
        }

        XCTAssertEqual(didRun, shouldRun)
}

Проблема в том, что я получаю это исключение:

failed: caught "NSInvalidArgumentException", "-[_SwiftValue setIndex:]: unrecognized selector sent to instance 0x60800024a6e0"
(
    0   CoreFoundation                      0x0000000113fc11e6 __exceptionPreprocess + 294
    1   libobjc.A.dylib                     0x00000001135fe031 objc_exception_throw + 48
    2   CoreFoundation                      0x0000000114042784 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
    3   CoreFoundation                      0x0000000113f43898 ___forwarding___ + 1432
    4   CoreFoundation                      0x0000000113f43278 _CF_forwarding_prep_0 + 120
    5   Realm                               0x000000010e7dd9e2 -[RLMObjectSchema _propertiesDidChange] + 610
    6   Realm                               0x000000010e7dd6a3 -[RLMObjectSchema setProperties:] + 99
    7   Realm                               0x000000010e7dd4e7 -[RLMObjectSchema 

Более точно:

-[_SwiftValue setIndex:]: unrecognized selector sent to instance 0x60c00044ed60

Прикрепленный снимок экрана: enter image description here

Итак, как можно было бы протестировать успешность миграции для объектной модели после добавления в эту модель нового поля?Как упоминалось выше: я хочу проверить, что миграция на новую модель прошла успешно и что существующий объект в моем файле области успешно перенесен (добавлено новое поле)

RealmSwift используемая версия: 3.9,Я попытался выполнить быстрые действия: Как выполнить модульное тестирование миграций Realm? и это: Как правильно выполнить модульное тестирование миграций Realm

и пришел к выводу, чтодля RealmSwift вы не можете добавить новое поле в объектную модель во время блока миграции;Вы можете создать только новый объект, используя

create @discardableResult
    public func create(_ typeName: String, value: Any = [:]) -> MigrationObject {
        return unsafeBitCast(rlmMigration.createObject(typeName, withValue: value), to: MigrationObject.self)
    }

, что не то, что я хочу.

Некоторые рабочие примеры того, как этого можно достичь, будут очень благодарны.Спасибо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...