realm-swift
realm-swift copied to clipboard
SwiftUI + Realm Sync Crashes Due to Incorrect Migration Assertion
How frequently does the bug occur?
All the time
Description
I use Realm Sync. Currently in developer mode and partition-based. I added a new property to one of my model objects:
@Persisted var isLocked: Bool = false
The vast majority of my app is built using AppKit. I load the Realm normally and everything works perfectly. However, one window in my app uses SwiftUI. I pass the already-opened Realm into SwiftUI as an environment variable. This is now crashing my app and telling me that I have to migrate the realm because the isLocked
property is new.
That's at odds with the Realm Sync documentation, which says migrations are NOT necessary and that older clients will simply ignore missing properties, etc. So...I'm at a loss for how to solve this. Please advise.
The docs specifically state that passing an already-opened realm is supported: https://www.mongodb.com/docs/realm/sdk/swift/examples/swiftui-guide/#inject-an-opened-realm
The Crash:

Passing the realm into SwiftUI:

Can you reproduce the bug?
Yes, always
Reproduction Steps
- Have a synced realm already opened in an AppKit controller.
- Pass that opened realm into SwiftUI.
- Add a new property to a model object.
- Observe Realm + SwiftUI crash.
Version
10.28.1
What SDK flavour are you using?
MongoDB Realm (i.e. Sync, auth, functions)
Are you using encryption?
No, not using encryption
Platform OS and version(s)
macOS 12.4
Build environment
Xcode version: 14.0 Beta 1
Dependency manager and version: SPM
Again, the REST of the app works 100% fine. It's only the SwiftUI-driven part that is crashing with this exception.
I've tried opening the realm separately using @asyncOpen
(rather than passing in the realm I've already opened on ModelController
) and I get the exact same crash:
What am I missing?

I do see this block in the documentation, but it's unclear if it applies to synced Realms:
https://www.mongodb.com/docs/realm/sdk/swift/examples/swiftui-guide/#swiftui-realm-migrations
I was under the impression that migrations were not required for synced Realms and, indeed, opening the synced realm in my AppKit controllers works 100% fine with no schema version specified in the configuration. It's only Realm's SwiftUI implementation that has this problem.
Is it the case that when adding new properties, a schema version must be specified for SwiftUI + Realm? If so, that's currently not clear in the documentation.
Adding a schemaVersion
to my config had no effect. Same crash.
var config = self.realmApp.currentUser!.configuration(partitionValue: self.globalPartitionKey, clientResetMode: .discardLocal(nil, nil))
config.schemaVersion = 2
This config is used to open the realm in my AppKit controller (modelController
). I then tried to pass this already-open realm into SwiftUI and got the same crash. I guess I can save the configuration object and re-attempt the @asyncOpen
approach using that config, but it looks like the getter/setter where the crash is happening already grabs the configuration of the realm and stores it, so I don't have much hope that this will work.
Okay, so if I store the configuration that I use to open my synced realm in modelController
(the AppKit-based controller), ensure there's a schemaVersion
in that configuration, then pass that configuration into SwiftUI via environment variable and use @AsyncOpen()
, the realm will now open correctly in SwiftUI.
However, that's super annoying for two reasons:
- There's now an ugly delay while the SwiftUI view reopens the Realm that's already opened in
modelController
. - The app is now wasting resources, since the same realm is opened twice needlessly.
I'm sure there's a reason for this song-and-dance, but it seems like Realm's SwiftUI extension ought to be able to accept an opened, synced realm and...just use it. That IS the case, until you add a new property to a model object. It seems silly that a synced Realm can handle that automatically in AppKit, but falls on its face in SwiftUI unless you jump through hoops with schemaVersion
and know the magic incantation to make it work.
Until Realm's SwiftUI extension is fixed to resolve this, the docs should be updated. The "don't worry about migrations for synced realms" appears to be wrong for SwiftUI. And the "you can pass in an already-opened realm" appears to mean: "you can pass in a realm you already opened in another SwiftUI view, but god have mercy on your soul if you opened that realm somewhere else outside of SwiftUI."
Hey @bdkjones - I can't speak to the implementation, but I work on the docs for the Swift SDK, and I've made a ticket to update them based on your feedback here. Sorry they led you astray! I appreciate you taking the time to share your experience here - hopefully the subsequent docs updates can help other developers who might stumble across this same issue.
I don't use sync but getting the same crash on EnvironmentValues
RealmSwift/SwiftUI.swift:1481: Fatal error: 'try!' expression unexpectedly raised an error: Error Domain=io.realm Code=10 "Migration is required due to the following errors:
Notice deleteRealmIfMigrationNeeded: true
and Realm.Configuration.defaultConfiguration = config
which I figured would be enough, unless swiftUI is not actually using this default. configure()
is ran early in didFinishLaunchingWithOptions
Maybe there is a UIKit/SwiftUI race condition or misread the docs.
public static func configure() {
var config = Realm.Configuration(
schemaVersion: 1,
deleteRealmIfMigrationNeeded: true
)
config.shouldCompactOnLaunch = { totalBytes, usedBytes in
let oneHundredMB = 100 * 1024 * 1024
return (totalBytes > oneHundredMB) && (Double(usedBytes) / Double(totalBytes)) < 0.5
}
config.fileURL = config.fileURL!.deletingLastPathComponent().appendingPathComponent("foo").appendingPathExtension("realm")
Realm.Configuration.defaultConfiguration = config
}
Figured it out, in a @main
modifier I was accessing @Environment(\.realm) private var realm
which probably created the race condition calling the getter before it could be set.
Okay, so if I store the configuration that I use to open my synced realm in
modelController
(the AppKit-based controller), ensure there's aschemaVersion
in that configuration, then pass that configuration into SwiftUI via environment variable and use@AsyncOpen()
, the realm will now open correctly in SwiftUI.However, that's super annoying for two reasons:
- There's now an ugly delay while the SwiftUI view reopens the Realm that's already opened in
modelController
.- The app is now wasting resources, since the same realm is opened twice needlessly.
I'm sure there's a reason for this song-and-dance, but it seems like Realm's SwiftUI extension ought to be able to accept an opened, synced realm and...just use it. That IS the case, until you add a new property to a model object. It seems silly that a synced Realm can handle that automatically in AppKit, but falls on its face in SwiftUI unless you jump through hoops with
schemaVersion
and know the magic incantation to make it work.Until Realm's SwiftUI extension is fixed to resolve this, the docs should be updated. The "don't worry about migrations for synced realms" appears to be wrong for SwiftUI. And the "you can pass in an already-opened realm" appears to mean: "you can pass in a realm you already opened in another SwiftUI view, but god have mercy on your soul if you opened that realm somewhere else outside of SwiftUI."
Im having the same issue here.. but unable to fix it, every time I add a new property to a model, and adjust the JSON schema in atlas, it crashes... I'm always getting the below RealmSwift/SwiftUI.swift:1417: Fatal error: 'try!' expression unexpectedly raised an error: Error Domain=io.realm Code=10 "Migration is required due to the following errors:
- Property 'File.date' has been added." UserInfo={Error Name=SchemaMismatch, NSLocalizedDescription=Migration is required due to the following errors:
- Property 'File.date' has been added., Error Code=10}