Итак, у меня есть следующий объект области в схеме версии 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](https://i.stack.imgur.com/rPdtA.png)
Итак, как можно было бы протестировать успешность миграции для объектной модели после добавления в эту модель нового поля?Как упоминалось выше: я хочу проверить, что миграция на новую модель прошла успешно и что существующий объект в моем файле области успешно перенесен (добавлено новое поле)
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)
}
, что не то, что я хочу.
Некоторые рабочие примеры того, как этого можно достичь, будут очень благодарны.Спасибо.