I just got stuck on what looks like a SwiftUI bug, executable code reproducing the issue below.
As of Swift 5.1.3 (Xcode 11.3.1), when a SwiftUI View passes an
@ObservedObject to a UIViewRepresentable, and the UIViewRepresentable is a
struct , SwiftUI fails to call the
updateUIView method of the UIViewRepresentable when the observed object changes.
If the UIViewRepresentable is a
final class instead, then its
updateUIView method is correctly called when the observable object changes.
I made a skeleton project reproducing this behavior: GitHub - mediweb/UIViewRepresentableBug
I don't fully grok all the magic behind SwiftUI so I cannot say for sure: can you all confirm this is indeed a bug and not intended behavior? Any other comments or feedback on this, like ways to make it work while keeping a struct?
Here is a wild guess. SwiftUI does not expect to have objects as classes, it breaks. I believe that when your representable us re-created, the old reference that the framework previously stored is destroyed. You can check if that‘s true by adding a
deinit into that type. The struct representable is working as expected. Why you ask? Well as you might notice,
makeUIView isn‘t called another time when the struct itself is re-created, which implies that the framework still stores your uiView and only waits until there is some sort of diff so it can then call
This is only making things weirder... Indeed, usually SwiftUI works with structs, not classes. But check again: here it is the struct that is broken, and the class that works as expected.
As you wrote, fresh new instances of the UIViewRepresentables (both struct and class) are recreated every time the observed object changes. So undoubtedly, the framework keeps references to the uiViews. Yet, only the
updateUIView of the class is called.
PS: @DevAndArtist you tagged this #off-topic. Was it because of the class working / struct broken confusion? Or did I post this in the wrong forum? I am new here, but I read the rules and forums descriptions, and I thought this was the right forum to post to. If I was mistaken, can you please point me to a better place to discuss this issue? Thank you!
I did it because
SwiftUI is still a close-source framework from Apple and not related to the open source project or any related projects (#related-projects). We usually don't delete any threads like this, and as the only signal to other forum readers we tend to tag such posts with #off-topic. This however does not mean that you won't ever get an answer to your question, but you also shouldn't expect an answer as there are platforms dedicated to this (e.g. stack overflow, Apple Developer forums).
PS: Personally my fingers are crossed that
Combine will be eventually open sourced (in some or another form, maybe like
SwiftCrypto) and would be home in this forum.
Alright I did some experiments and I think I identified two bugs. I believe your code works as expected, but I found a case which is not working in Xcode 11.3.1 but is already fixed in 11.4 beta 1.
See my gist: swiftui_update_inconsistency.swift · GitHub
V_Struct_Representable_ObservedObject does not update, but it really should (FIXED ISSUE).
V_Class_ObservedObject produce runtime crashes.
Oh I see, I had no idea
SwiftUI was not at home here. Thank you for the clarification @DevAndArtist!
And thank you so much for the extensive testing!
So I guess I don't need to report the "
V_Struct_Representable_ObservedObject does not update" bug to Apple: they already fixed it.
I am now downloading Xcode 11.4 beta 1 to confirm, I will report back here.
Unfortunately, with the skeleton project I posted in the question, the issue is still happening when using Xcode 11.4-beta.
I will take this to the Apple Developer forums since that seems like the better place to discuss this. Thanks again for your help @DevAndArtist!
There is a discussion on the Apple Developer Forum for SwiftUI, about what seems like the same or a closely related issue:
I posted there but my post is still pending moderation.
I have not tested other property wrappers, but your example isn‘t bugged, but is expected behavior. The difference between your example and the fixed bug is that the bugged view had to have an
ObservedObject, while yours does not have it and therefore shouldn‘t update as the reference itself has not changed.
Please check out my gist and compare the different view types closely. Some of the struct views are expected not to update.
Imagine a struct view with a
State<Void>. Even if you set it and cause a mutation, your view never will update because of that, because raw memory diffing won‘t recognize any change.