• Jennifer Eve Vega

Finding what changed in SwiftUI

In SwiftUI, when your State or Published variables got updated, it will redraw the screen. But what if you can't find what caused your screen to update?


You just need to add this inside your body.

var body: some View {
    let _ = Self._printChanges()
}

This is only available starting iOS15.0, so you might need to add some checking if you support older iOS versions.


But I found something else that is useful aside from this and I accidentally found this solution when I encountered a bug in our app.


But first, let me show you a small diagram. Since we use SwiftUI in our application, we have a separate repository for our SwiftUI elements.


MVVM


We also use MVVM in our app with this template:

Screen:

struct Screen<Model>: View where Model: ViewModelProtocol {
    @StateObject var viewModel: Model
    
    var body: some View {
        Text("Hello, World!")
    }
}

ViewModel:

protocol ViewModelProtocol: ObservableObject {
    
}

class ViewModel: ViewModelProtocol {
    
}


What's the bug?

The bug happens when app from background comes into foreground. Example, I have a toggle which is part of our SwiftUI component library (for demonstration purpose, and for simplicity's sake), let's call it ToggleComponent. And I'm using my ToggleComponent in my Main App's screen. I turned it ON before the app went to background mode. And when I open the app again, the toggle has been turned OFF.


protocol ViewModelProtocol: ObservableObject {
    var isOn: Bool { get set }
}
class ViewModel: ViewModelProtocol {
    @Published var isOn = false
}

struct Screen<Model>: View where Model: ViewModelProtocol {
    @StateObject var viewModel: Model
    
    var body: some View {
	    let _ = Self._printChanges()
        ToggleComponent(title: "Toggle",
                        isOn: $viewModel.isOn)
    }
}

Now, I keep seeing in our console that my ViewModel has changed. But I couldn't figure out what changed it or which particular value has changed. I have checked all the codes that may have updated the variables on my viewModel, but it didn't hit my breakpoints, which means, none of those codes caused the changes.


Added willSet in all Published variables in ViewModel:

I couldn't figure out which variable in my ViewModel has changed, so I had no choice but to add willSet{...} in all @Published variables in my ViewModel, and a breakpoint inside.


class ViewModel: ViewModelProtocol {
    @Published var isOn = false {
        willSet {
            debugPrint("is about to change")
        }
    }
}

I ran the app again, and replicated the bug and it hit my breakpoint inside willSet. I can now see in the Debug Navigator who called and set a new value on my ViewModel. And I did not expect that the culprit was inside the Component Library. There was a code inside my ToggleComponent that resets the value.


So if you also encounter the same problem I hope this solution helps you find who called and updated what in your code, and help you fix the problem.

48 views0 comments

Recent Posts

See All