Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

warning run: Publishing changes from within view updates is not allowed, this will cause undefined behavior. #7

Open
degtiarev opened this issue Sep 30, 2022 · 6 comments

Comments

@degtiarev
Copy link

Xcode Version 14.0.1 (14A400) started to show this warning
Screenshot 2022-09-30 at 16 22 57

@Amzd
Copy link
Owner

Amzd commented Sep 30, 2022

Is there any chance the warning is on the wrong line and you are publishing changes within view updates on your side?

Could you give a small isolated expample off when this shows? Maybe I'll have time to look into this soon.

@degtiarev
Copy link
Author

degtiarev commented Oct 28, 2022

Sorry for a very late reply. The warning appears pretty much all the time, even in the simplest case. For example, here:

import SwiftUI

struct ContentView: View {
    
    @StateObject var observableObject: SomeObservableObject
    init(text: String = "Old text") {
        _observableObject = StateObject(wrappedValue: SomeObservableObject(text: text))
    }
    
    var body: some View {
        VStack {
            Text(observableObject.text)
            Button {
                observableObject.text = "new text"
            } label: {
                Text("Button")
            }
        }
        .padding()
    }
}

class SomeObservableObject: ObservableObject {
    @Published var text: String
    
    init(text: String) {
        self.text = text
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

It seems it is Xcode 14 bug as this issue was not presented in Xcode 13.4.1, but I did not test the beta 3 or any RC version of Xcode 14.1 yet...

@Amzd
Copy link
Owner

Amzd commented Oct 30, 2022

In that example there isn't much point to pass the text into a state object as every time the view inits you create a new one.

@degtiarev
Copy link
Author

That's just simple example when the warning appears. It did not appear before on the old Xcode version. It appears also in for example, this example:

import SwiftUI

struct ContentView: View {
    
    @StateObject var observableObject: SomeObservableObject = SomeObservableObject()
    
    var body: some View {
        VStack {
            Text(observableObject.text)
            Button {
                observableObject.text = "new text"
            } label: {
                Text("Button")
            }
        }
        .padding()
    }
}

class SomeObservableObject: ObservableObject {
    @Published var text: String
    
    init(text: String = "Old text") {
        self.text = text
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

And this one:

import SwiftUI

struct ContentView: View {
    
    @StateObject var observableObject: SomeObservableObject
    init(text: String = "Old text") {
        _observableObject = StateObject(wrappedValue: SomeObservableObject(text: text))
    }
    
    var body: some View {
        ZStack {
            ExtractedView(text: $observableObject.text)
        }
    }
}

struct ExtractedView: View {
    @Binding var text: String
    
    var body: some View {
        VStack {
            Text(text)
            Button {
                text = "new text"
            } label: {
                Text("Button")
            }
        }
        .padding()
    }
}


class SomeObservableObject: ObservableObject {
    @Published var text: String
    
    init(text: String) {
        self.text = text
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Which are little bit modified versions of the first one. If I set Minimum Deployments to iOS 14 and delete the library, then it does not show any warning. If you think that it is expected behaviour for these cases, please, could you give any simple example that will not have this warning.

@Amzd
Copy link
Owner

Amzd commented Nov 1, 2022

Hmm, you're right. I haven't looked into it but I assume it has to do with the projected value. In the init I call publisher.send() twice. The first issue I see is possible is that the queue switch there to wait for the change comes back during the view runloop. The second possible issue could be calling the publisher.send() synchronously in the init. Not sure. I'll look into it if I have time.

@degtiarev
Copy link
Author

degtiarev commented Nov 16, 2022

It seems putting parentObject?.objectWillChange.send() inside DispatchQueue.main.async in setParent<Parent: ObservableObject> function fixes the problem with warning.

  private func setParent<Parent: ObservableObject>(_ parentObject: Parent) where Parent.ObjectWillChangePublisher == ObservableObjectPublisher {
        guard parent.objectWillChange == nil else { return }
        parent.objectWillChange = { [weak parentObject] in
            DispatchQueue.main.async{
                parentObject?.objectWillChange.send()
            }
        }
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants