Realm migration when using non-default configurations - ios

In the realm docs I see the code example for migrations
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
and then it says to use let realm = try! Realm() to get the realm instance. However, in our application we are using our own realm configurations with something like
let realm = try! Realm(configuration: RealmConfig.getConfig(typeURL: .userData)!)
We have a few different configurations besides .userData. My question is, how does one go about doing migrations with these non-default configurations? The code example really only shows how to set the default configuration which is insignificant for my use. I couldn't find anything like
Realm.Configuration.userData = config
Does something like this exist that I am missing? Or is there another way I'm supposed to go about this?

You can instantiate different Realm instances with different configurations like this:
let userDataConfiguration = Realm.Configuration(...)
let userDataRealm = try! Realm(configuration: userDataConfiguration)
Learn more in docs at https://realm.io/docs/swift/latest/#realms

Related

RealmSwift downgrade migration

Is it possible to detect the user is installing an app version with an older db schema version? (downgrading the app version basically)
Want to know if it is possible to detect and delete the current db file so that the app can still recover from it instead of just crashing.
It is an odd scenario but it is happening (some beta testers and such).
Yes, you have access to the old schema version in the migrationBlock of Realm.Configuration, so just check whether the oldSchemaVersion stored on the device is actually higher than the one your current app version has and if so, delete all Realm files using Realm.deleteFiles(for:).
let currentSchemaVersion = 1
let config = Realm.Configuration(
schemaVersion: currentSchemaVersion,
migrationBlock: { migration, oldSchemaVersion in
if (oldSchemaVersion > currentSchemaVersion) {
Realm.deleteFiles(for: Realm.Configuration.defaultConfiguration)
}
}
)
// Then set the config and create your `Realm` instance
Realm.Configuration.defaultConfiguration = config
let realm = try! Realm()

Fatal Error :Can't open realm

I am working on IOS. I am using realm database in the frontend. It was working fine until I made some changes to the realm model and all files related to it. I just added one field to these files.
Now I am getting an error "Fatal error: Can't open realm" in the following code
fileprivate func getRealm() -> Realm {
// get default configuration realm
do {
return try Realm()
} catch {
Swift.fatalError("Can't open realm") //Fatal Error :Can't open realm
}
}
Can anyone tell what might be the possible causes for this error.
Thanks in advance.
If you made changes to the realm model, you need to increase your schema version and you may or may need to provide a migration block. See the official documentation for details.
// Inside your [AppDelegate didFinishLaunchingWithOptions:]
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
// 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).
config.schemaVersion = 1;
// Set the block which will be called automatically when opening a Realm with a
// schema version lower than the one set above
config.migrationBlock = ^(RLMMigration *migration, uint64_t oldSchemaVersion) {
// 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
[RLMRealmConfiguration setDefaultConfiguration:config];
// Now that we've told Realm how to handle the schema change, opening the file
// will automatically perform the migration
[RLMRealm defaultRealm];
The above issue is caused due to inconsistency in the Realm database. I changed the structure of the model by adding one field. So, the model was having 10 fields but the realm database in my app was having 9 fields, as the app was built before the changes.
I solved the issue by just reinstalling the app which regenerated the realm database in the phone, thereby having a consistent model and database.
The above workaround might be good if your app is yet to be live. But, if your app is already being used by other users, then they will have to reinstall the app on update, which might result in bad user experience for the user.
Ideally, you should be writing a migration block for the new changes.
Hope, this is clear to all.

Realm migration inadequately documented. Can anyone clarify?

Performing migrations on a Realm database is poorly documented and the documentation seems to be out-of-date. There are two areas explaining how to migrate data:
-- The simple example on the Realm website: https://realm.io/docs/swift/latest/
-- A more detailed example in the Github examples section: https://github.com/realm/realm-cocoa/blob/master/examples/ios/swift-3.0/Migration/AppDelegate.swift
Neither of these examples adequately explain how to migrate data between schema versions. I've tried playing around with the examples and have yet to get any migrations working. As well, I've had problems with app crashes when upgrading to newer Realm versions without schema changes and data changes, which don't occur in the Simulator but occur when installing the app from TestFlight or the App Store.
Seems like the Realm documentation and examples detailing migrations are due for a refresh. My areas of interest are:
Upgrading to a newer Realm version without a schema change in the database. Unclear whether I should continue using the default.realm file generated with a previous version, or whether I need to re-generate the default.realm file using the newer Realm framework version.
Adding a new attribute to a Realm object.
New objects ("rows") added to an existing class without any schema change.
No schema changes to existing classes in the database, but addition of an entirely new class or classes.
Any combination of the above.
Thanks!
Sorry the docs are not sufficient. We appreciate the feedback and will use it to improve them. In the mean time, let me answer your questions:
You do not need to do anything when you upgrade the SDK. Sometimes, we upgrade the core database file format, but this migration happens automatically when you open the Realm (Realm()) so you don't have to worry about it.
When you add a new property to an object you can just follow this code snippet.
Nothing is needed in the migration block since this block is simply to apply data transformations between versions. All you need to do is increment the schemaVersion
// 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()
Adding objects to a Realm does not affect the schema so a migration is not relevant.
This is the same as 2, you simply need to increment the schemaVersion but you don't have to do anything in the migration block since Realm handles everything. The migration block is for custom migration logic where, for example, you want to transform firstName and lastName from schemaVersion=0 to fullName when updating to schemaVersion=1. In this case, you could get the data from the old version and concatenate the strings into the new fullName property within the migration block.
Hope this helps!
Unfortunately, that doesn't work, at all. There is a block of code I had to add to iOS apps several months ago in order to get Realm to work, but it doesn't solve the migration problems I've experienced:
config.fileURL = config.fileURL!.deletingLastPathComponent().appendingPathComponent("default-v1.realm")
Realm.Configuration.defaultConfiguration = config
// copy over old data files for migration
//let defaultURL = Realm.Configuration.defaultConfiguration.fileURL!
let defaultURL = Realm.Configuration.defaultConfiguration.fileURL!
//print("Realm.Configuration.defaultConfiguration.fileURL! = \(defaultURL)")
//let defaultParentURL = defaultURL.URLByDeletingLastPathComponent
if let realmURL = bundleURL("default-v1") {
do {
//print("Bundle location for default.realm = \(realmURL)")
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = URL(fileURLWithPath: path)
let filePath = url.appendingPathComponent("default-v1.realm").path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath) {
print("FILE INSTALLED")
print("Documents location for default.realm = \(filePath)")
} else {
//print("FILE NOT INSTALLED, COPYING default.realm from bundle to Documents directory.")
try FileManager.default.copyItem(at: realmURL, to: defaultURL)
}
} catch {}
}
I suspect that a new default-v1.realm file may clobber an existing default-v1.realm file on a user's device, using this code with the "Nothing is needed" migration method.
Part of the confusion stems from the example migration code at https://github.com/realm/realm-cocoa/tree/master/examples/ios/swift-3.0/Migration where data from the v1 file is getting copied over to the v2 file. Regardless, I've tried the "Nothing is needed" code you've posted and the app doesn't find any Realm data then crashes.
It looks like I could splice the code you've posted into the code I've been using then perhaps that would solve the problem. Will look into that next week.
Thanks for your response!

Switching between realms (iOS / Swift 3)

I was trying to log in or register a user and connect them to an existing realm. Then, depending on the info stored in that realm, I may need them to instead connect to a different realm.
Is it not possible to try! Realm with a different configuration after it is initially configured? Is it discouraged? Does it need to be done outside of the initial DispatchQueue?
Here is the code:
SyncUser.logIn(with: usernameCredentials, server: URL(string: "http://11.22.333.0:9080")!) {
user, error in
guard let user = user else {
fatalError(String(describing: error))
}
DispatchQueue.main.async {
let configuration = Realm.Configuration(
syncConfiguration: SyncConfiguration(user: user, realmURL: URL(string: "realm://11.22.333.0:9080/ab56realmURL/NameOfRealm1")!)
)
self.realm = try! Realm(configuration: configuration)
if (someCheckOfData in realm) {
let configuration2 = Realm.Configuration(
syncConfiguration: SyncConfiguration(user: user, realmURL: URL(string: "realm://11.22.333.0:9080/ab56realmURL/NameOfRealm2")!)
)
self.realm = try! Realm(configuration: configuration2)
}
}
}
Thanks so much for any help!
No, it's not discouraged. All you're doing here is creating 2 discrete copies of Configuration, which will then subsequently be creating 2 separate Realm instances on your server.
The two will be completely separate, so there's no chance of causing an exception by incorrectly changing the configuration after it was used to create an initial Realm instance.
One thing we do recommend though is not holding onto specific Realm references like that though. They are not thread safe, and GCD isn't guaranteed to execute the same queues on the same threads, so you may be setting yourself up for a future exception.
If this is going to be your primary Realm, it's usually recommended to set that Configuration as the default Realm one. Otherwise, since Configuration is thread-safe (Assuming you don't modify it later), you can hold onto that, and use it to try! Realm(configuruation:) whenever you actually need to use Realm.

AppStore update and Realm

I have application in Appstore based on Realm Mobile Database. I'd like to prepare version 1.1. Will update delete all data in user's local database ?
The upgrade won't delete anything on your current database. The migration is automatic. If you need to change one or more field in your current model, you need to upgrade the database schema version. Place this code into the application:didFinishLaunchingWithOptions: method in your AppDelegate:
//Realm migration
let config = Realm.Configuration(
schemaVersion: 2, //here's the schema version you need to change
migrationBlock: { migration, oldSchemaVersion in
if (oldSchemaVersion < 2) {
//if you want to perform particular tasks
//while migrating, place your code here.
}
})
Realm.Configuration.defaultConfiguration = config
_ = try! Realm()
Absolutely not, It won't delete existing database, that's why Realm support automatic/custom migration.

Resources