AttributeGraph precondition failure: setting value during update using Realm in SwiftUI
AttributeGraph precondition failure: setting value during updateusing Realm in SwiftUI
Synced Realm on iOS with SwiftUI using Sign-in with Apple for Authentication
With catalyst's beta version 0.46.1, fatal
AttributeGraph precondition failure: setting value during update errors started popping up in Sentry. Most of the time, the error occurred during the start of catalyst but sometimes also at a later point in other views of the app. The error message stayed the same and always pointed to the very same line of third-party code, though. Regardless, I did not immediately jump to investigating this issue since only a marginal fraction of users was affected.
Around the same time, Apple published iOS 16 together with XCode 14. This new version of Xcode caused the warning
Publishing changes from within view updates is not allowed, this will cause undefined behavior to pop up a lot during the runtime of catalyst. I was not alone in this, though, so I did not pay a lot of attention to the warning in the beginning.
On October 28, a user of catalyst reported the
AttributeGraph precondition failure: setting value during update error because it consistently crashed catalyst for them after updating to the beta version 0.49.3. Oddly enough, these crashes did not happen for them on version 0.46.8 which they upgraded from. So, I started my journey investigating this issue from the bug report.
Making the connection
Unfortunately, though, I could neither reproduce the error on my iPhone running iOS 16 nor on the simulator running iOS 15.5 or iOS 16 in either version of catalyst’s beta. Hence, I pointed my attention back to Sentry which had a dozen records of the error along with stack traces. As mentioned earlier, they all pointed to the very same line of code in Realm. This line of code is part of Realm’s
@ObservedResults wrapper that enables seamlessly querying data from Realm within SwiftUI views. As such, the fatal error seemed to occur in all views that utilize Realm’s wrapper. Looking at the error message, I guessed that the wrapper was updating its variable during an update of the SwiftUI view which ultimately led to the crash. Why couldn’t I reproduce this scenario, though?
This line of thought reminded me of the runtime warnings in Xcode. Their message (i.e.,
Publishing changes from within view updates is not allowed, this will cause undefined behavior) now sounded very familiar given the fatal error’s message. Back in Xcode, it turned out that the runtime warnings indeed pointed to the very same line of code in Realm that the stack trace in Sentry pointed to. While a lot of people on Apple’s developer forum and elsewhere labeled the runtime warnings as a bug, I was worried that there is some truth to these warnings as they seemed to cause a crash in the case of catalyst.
With this worry, I turned to Realm’s GitHub issues and found that other developers already reported the runtime warning in Xcode when using Realm. More importantly, the runtime warnings they observed pointed to the same line of code that I observed the crash in catalyst point to. However, since no one observed any undefined behavior, I reported my observations on the issue. In particular, this includes the following partial stack trace that is common to all crashes in catalyst caused by the discussed error:
Exception Type: EXC_CRASH (SIGABRT)Crashed Thread: 0Application Specific Information:AttributeGraph precondition failure: %s.> AttributeGraph precondition failure: setting value during update: 89784.>Stack overflow in _Z23RLMAddNotificationBlockI10RLMResultsEP20RLMNotificationTokenPT_U13block_pointerFvP11objc_objectP19RLMCollectionChangeP7NSErrorEP7NSArrayIP8NSStringEPU28objcproto17OS_dispatch_queue8NSObjectThread 0 Crashed:0 libsystem_kernel.dylib 0x37656abbc __pthread_kill1 libsystem_pthread.dylib 0x3b7a67850 pthread_kill2 libsystem_c.dylib 0x3162756a8 abort3 AttributeGraph 0x36c141510 AG::precondition_failure4 AttributeGraph 0x36c1256a4 AG::Graph::value_set5 SwiftUI 0x310a1f870 Attribute.setValue6 SwiftUI 0x3100b1290 GraphHost.flushTransactions7 SwiftUI 0x3100e0af8 GraphHost.asyncTransaction<T>8 SwiftUI 0x3100e618c AttributeInvalidatingSubscriber.invalidateAttribute9 SwiftUI 0x3100b7f70 AttributeInvalidatingSubscriber.receive10 SwiftUI 0x31021df7c AttributeInvalidatingSubscriber<T>11 SwiftUI 0x3100b8b80 SubscriptionLifetime.Connection.receive12 Combine 0x32ed98784 AnySubscriberBox.receive13 Combine 0x32ed87a64 AnySubscriber<T>14 catalyst 0x20049cb04 [inlined] ObservableStoragePublisher.send (SwiftUI.swift:205)15 catalyst 0x20049cb04 [inlined] Sequence.forEach16 catalyst 0x20049cb04 ObservableStoragePublisher.send17 catalyst 0x20049dac8 ObservableStorage.value.willset (SwiftUI.swift:241)18 catalyst 0x2004ad448 ObservableStorage.value.setter19 catalyst 0x20049ef0c [inlined] ObservableStorage.value.setter20 catalyst 0x20049ef0c ObservedResults.Storage.setupValue (SwiftUI.swift:435)21 catalyst 0x2004a0704 ObservedResults.wrappedValue.getter (SwiftUI.swift:519)[...]
Since then, a few other GitHub users chimed in and noted that they encountered the same problem. It does not seem that any of them faced actual application crashes, though. While a developer of Realm noticed the issue, it does not seem that there is an imminent solution to the problem. Hence, I will cut back on my efforts in fixing the fatal error in catalyst for now, waiting for further statements from Realm. While this is unfortunate, the fatal error only affects less than 5% of catalyst’s users and I can now direct my focus on improving and implementing new features for the app.
As soon as I can achieve significant progress on the matter, I will update this article. ✌🏼
Since reporting this problem, there have been efforts to fix the discussed warnings through the pull request #8068. In particular, the PR's changeset addresses the warnings in relation to uses of
@ObservedResults. On January 4, 2023, the PR was merged and later released as Realm's version 10.34.0. After updating to this release of Realm in catalyst, the crash described above no longer occurs.