Closed drmarkpowell closed 1 year ago
It looks like that at some point there is a sync realm configuration whose path is set to the local default realm path. Like the error mentions, realm configurations with sync enabled can't open non-sync realm files.
If there's a place in your app where you're reassigning paths for this environment realm var, I'd check to make sure the fileURL
is expected.
Closing this for now...there's a good chance that I was coding improperly. In order to have ViewModel classes in my app, I made a RealmController class that kept references to open Realms. Since the realms don't exist until after the user authenticates and opens them, I was initializing my RealmController with placeholder Realm instances that...I thought...did nothing. Post-open, I was overwriting these realm instances with the newly opened ones with the intent to throw away the placeholders.
It would seem that the placeholder realms were interfering with my intended good realms. I removed the placeholders, and this crash seems to have disappeared.
Would be great to discuss with one of y'all how I could have done this properly if I were to try it again in the future...
Turns out that this bug is not fixed...it is still occurring in our latest code, just less frequently...but it still crashes nonetheless.
Here is what I currently have:
struct OpenSyncedRealmView: View {
@StateObject var progressViewModel = SyncProgressViewModel()
@AutoOpen(
appId: AppController.realmAppId,
partitionValue: "abc",
configuration: RealmController.realmConfiguration("abc"),
timeout: 5000
) var abcRealm
var body: some View {
switch abcRealm {
case .connecting:
ProgressView("Connecting...")
case .waitingForUser:
loginView()
case .open(let realm):
tabView(realm)
.environmentObject(AppController.shared.app)
case .progress(let progress):
PleaseWaitLoadingDataView(progress: progressViewModel)
.task { @MainActor in
progressViewModel.progress = progress
}
case .error(let error):
Text(error.localizedDescription)
}
}
func tabView(_ realm: Realm) -> some View {
RealmController.shared.realm = realm
return ABCTabView()
.environment(\.realm, realm)
.environment(\.realmConfiguration, RealmController.realmConfiguration("abc"))
}
...
}
class RealmController: ObservableObject {
...
static func realmConfiguration(_ partitionId: String) -> Realm.Configuration {
if let user = AppController.shared.app.currentUser {
var realmConfiguration = user.configuration(
partitionValue: partitionId,
clientResetMode: .discardLocal(nil, nil)
)
realmConfiguration.schemaVersion = AppController.SCHEMA_VERSION
return realmConfiguration
}
return Realm.Configuration(
schemaVersion: AppController.SCHEMA_VERSION
)
}
...
}
This code crashes for some users at the return try! Realm(...)
in EnvironmentValues
I included in the previous comment.
We are running on iOS 10.5.X, building with Xcode 13.4.1 with RealmSwift 10.28.6.
Your latest snippet looks fine to me.
If you're able to reproduce this on your own machine, could you try setting a breakpoint in the setter for the environment configuration? Something like -
extension EnvironmentValues {
/// The current `Realm.Configuration` that the view should use.
public var realmConfiguration: Realm.Configuration {
get {
return self[RealmEnvironmentKey.self]
}
set {
if (Realm.Configuration().fileURL == newValue.fileURL) {
print("default path being set") // breakpoint here
}
self[RealmEnvironmentKey.self] = newValue
}
}
Realm.Configuration() is the default configuration, so Realm.Configuration().fileURL should be equal /var/mobile/Containers/Data/Application/***-***-***-***-****/Documents/default.realm
that you're seeing in the exception log.
If the breakpoint is hit, look up the stack trace and hopefully we'll see how that path is being set.
Or you could do if (newValue.syncConfiguration == nil)
I haven't been able to reproduce this yet on my machine. I can however use a breakpoint to see when I'm using a RealmConfiguration that has the fileUrl set to .../default.realm. I have found that doing this:
@AutoOpen(
appId: AppController.realmAppId,
partitionValue: "abc",
configuration: RealmController.realmConfiguration("abc"),
timeout: 5000
) var abcRealm
Implicitly creates a Realm instance with the fileUrl set to .../default.realm. So it would appear that my code is "not ok". I do need to set the clientResetMode in my RealmConfiguration to .discardLocal(nil, nil), however.
It would appear that we are at in impasse, here...I can't set the RealmConfiguration to handle client reset prior to \@AutoOpen-ing the realm instance without opening the default realm? Is it possible to set the client reset mode in the RealmConfiguration after it is open?
I think I was mistaken...it's not creating a Realm instance as I said, but it is using a RealmConfiguration with the fileUrl default value with .../default.realm. Perhaps that is related to the problem in some way. When the 'case .open(let realm)' fires, the fileURL is set to the synced realm path.
Perhaps that is related to the problem in some way.
Yes could be. And this exception happens intermittently? I'm wondering if return try! Realm(configuration: self[RealmEnvironmentKey.self])
is somehow getting called before update()
, and the default config environmentValue isn't being overwritten.
I'm going to try to reproduce and check with the team.
I would say that it is intermittent, yes. It's never happened to me, but I'm getting logs from production users who are impacted by it. Following up with them, when they relaunch our app a second time following a crash, it does not re-occur. I released an update yesterday that simplified some of our app initialization and one user who had seen the crash regularly no longer did. Unfortunately, the crash reports are continuing to appear from other users as well.
I think this is the bug -
The realm is opened twice with autoOpen. First when initializing the wrapper, and then when update()
is executed. The first open is happening with the default config, then with the correct one.
@dianaafanador3 has a potential fix for this:
https://github.com/realm/realm-swift/pull/7756
This is very exciting! Thank you, Diana and the team for responding on this so quickly.
@drmarkpowell As mentioned above we have a PR in the making for deferring opening the realm when everything is set for @AsyncOpen and @AutoOpen. But looking at your code, I'll recommend to change the following code
static func realmConfiguration(_ partitionId: String) -> Realm.Configuration {
if let user = AppController.shared.app.currentUser {
var realmConfiguration = user.configuration(
partitionValue: partitionId,
clientResetMode: .discardLocal(nil, nil)
)
realmConfiguration.schemaVersion = AppController.SCHEMA_VERSION
return realmConfiguration
}
return Realm.Configuration(
schemaVersion: AppController.SCHEMA_VERSION
)
}
To this
static func realmConfiguration(_ partitionId: String) -> Realm.Configuration? {
if let user = AppController.shared.app.currentUser {
var realmConfiguration = user.configuration(
partitionValue: partitionId,
clientResetMode: .discardLocal(nil, nil)
)
realmConfiguration.schemaVersion = AppController.SCHEMA_VERSION
return realmConfiguration
}
return nil
}
The previous code was injecting a configuration to the property wrapper (@ AutoOpen) that has set the fileUrl to default.realm
, in cases where the currentUser is nil, given that in SwiftUI a view loads before is even showed, in the current implementation of the property wrapper is calling Realm.asyncOpen a first time with this configuration.
Diana, Thanks for following up on this! This is really helpful.
Question for you on how I'd incorporate the changes at the App level:
var body: some Scene {
return WindowGroup {
VStack {
if login.token.isEmpty {
LoginView(login: login)
.frame(height: 0)
} else if let realmConfig = RealmController.realmConfiguration("abc") {
OpenSyncedRealmView(idToken: login.token)
.environment(\.realmConfiguration, realmConfig)
} else {
OpenSyncedRealmView(idToken: login.token)
}
}
Apparently the SwiftUI Environment won't allow us to set optional values, and we're now returning a Realm.Configuration?
. So, when we modify the app (this is a lot like the code in the Realm SwiftUI quick start example BTW), I suppose we need to do something like the code above? If so, when the View is built before the Realm.Configuration is available, does this create a View with a default realm? Is that an empty realm, if the nominal realm instance uses a synced partition ("abc", above) to access data?
I have done as you recommended: make the realmconfiguration nil if there is no current user in the Realm App.
Now we are seeing another kind of crash on initialization of the Realm sync:
Crashed: Thread
0 libsystem_kernel.dylib 0x71e0 (Missing UUID f9830013bade3606bbbb23fcc37931b7)
1 libsystem_pthread.dylib 0x71ac (Missing UUID 0cb9ebb417eb386bb1e04cea7f3ca5af)
2 libsystem_c.dylib 0x20c8c abort + 180
3 OurApp 0x723cf4 realm::util::terminate(char const*, char const*, long, std::initializer_list<realm::util::Printable>&&) + 147 (terminate.cpp:147)
4 OurApp 0x724034 realm::util::terminate_internal(std::__1::basic_stringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) + 142 (terminate.cpp:142)
5 OurApp 0x723db0 realm::util::terminate(char const*, char const*, long, std::initializer_list<realm::util::Printable>&&) + 152 (terminate.cpp:152)
6 OurApp 0x706530 realm::util::network::Service::BasicStreamOps<realm::util::network::ssl::Stream>::BufferedReadOperBase::advance() + 80 (initializer_list:80)
7 OurApp 0x705fa8 void realm::util::network::Service::BasicStreamOps<realm::util::network::ssl::Stream>::async_buffered_read<realm::util::UniqueFunction<void (std::__1::error_code, unsigned long)> >(realm::util::network::ssl::Stream&, char*, unsigned long, int, realm::util::network::ReadAheadBuffer&, realm::util::UniqueFunction<void (std::__1::error_code, unsigned long)>&&) + 1854 (network.hpp:1854)
8 OurApp 0x701fa0 realm::util::websocket::(anonymous namespace)::EZSocketImpl::async_read(char*, unsigned long, realm::util::UniqueFunction<void (std::__1::error_code, unsigned long)>) + 315 (unique_ptr.h:315)
9 OurApp 0x728540 (anonymous namespace)::WebSocket::frame_reader_loop() + 315 (unique_ptr.h:315)
10 OurApp 0x706798 realm::util::UniqueFunction<void (std::__1::error_code, unsigned long)>::operator()(std::__1::error_code, unsigned long) const + 94 (functional.hpp:94)
11 OurApp 0x7066d8 void realm::util::network::Service::AsyncOper::do_recycle_and_execute<realm::util::UniqueFunction<void (std::__1::error_code, unsigned long)>, std::__1::error_code&, unsigned long&>(bool, realm::util::UniqueFunction<void (std::__1::error_code, unsigned long)>&, std::__1::error_code&, unsigned long&) + 315 (unique_ptr.h:315)
12 OurApp 0x706254 realm::util::network::Service::BasicStreamOps<realm::util::network::ssl::Stream>::BufferedReadOper<realm::util::UniqueFunction<void (std::__1::error_code, unsigned long)> >::recycle_and_execute() + 2717 (network.hpp:2717)
13 OurApp 0x71d3ec realm::util::network::Service::Impl::run() + 1757 (network.hpp:1757)
14 OurApp 0x689688 realm::sync::Client::run() + 997 (atomic:997)
15 OurApp 0x6031dc void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, realm::_impl::SyncClient::SyncClient(std::__1::unique_ptr<realm::util::Logger, std::__1::default_delete<realm::util::Logger> >, realm::SyncClientConfig const&, std::__1::weak_ptr<realm::SyncManager const>)::'lambda0'()> >(void*) + 191 (tuple:191)
16 libsystem_pthread.dylib 0x16cc (Missing UUID 0cb9ebb417eb386bb1e04cea7f3ca5af)
17 libsystem_pthread.dylib 0xba4 (Missing UUID 0cb9ebb417eb386bb1e04cea7f3ca5af)
This is a very internal Realm termination. What would give rise to this?
Fix merged, will be available on the next release
How frequently does the bug occur?
Sometimes
Description
I'm getting crash reports from many users from inside of Realm SwiftUI and it's intermittent.
The code crashes at this
try!
in RealmSwift SwiftUI:Stacktrace & log output
Can you reproduce the bug?
Not yet
Reproduction Steps
No response
Version
10.26.0
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)
iOS 15.5+
Build environment
Xcode version: 13.4.1 Dependency manager and version: SPM