2

How can a View being re initialised in SwiftUI but the body not recomputed and the view re rendered?


Example

import SwiftUI

struct ContentView: View {
    var body: some View {
        SubView()
            .padding()
    }
}

struct SubView: View {
    
    @State private var items = [Int]()
    
    init() {
        print("SubView init")
    }
    
    var body: some View {
        Text("\(items.count)")
        Button("Add item") { items.append(Int.random(in: 0..<10)) }
        Divider()
        SubSubView()
        SubSubView()
    }
}

struct SubSubView: View {
    init() {
        print("SubSubView init")
    }
    
    var body: some View {
        Text("I'm a subview") (>> _breakpoint here_)
    }
}

If I tap 4 times on the button, the view is the following:

    enter image description here

But we have that in the console:

SubView init (>> app launch)
SubSubView init (>> app launch)
SubSubView init (>> app launch)
SubSubView init
SubSubView init
SubSubView init
SubSubView init
SubSubView init
SubSubView init
SubSubView init
SubSubView init

The SubSubView body property is not recomputed (using a breakpoint). Is this SubSubView re rendered only when there is any change in the view dependency, even if it's re initialized again?

alpennec
  • 1,864
  • 3
  • 18
  • 25
  • 2
    This is SwiftUI renderer optimization - it creates view value (as it is struct), but re-renders it (calling body) only if there were some changes since last draw. – Asperi Nov 14 '20 at 19:37
  • It would be more helpful if you could tell us why you need to re-initialize a view, since I don't think it's possible like that, or at least not good practice to force re-render in SwiftUI – thisIsTheFoxe Nov 15 '20 at 08:40
  • 1
    @thisIsTheFoxe actually I was more trying to understand how the life cycles between the struct and the view differ. And also to understand when and why properties from the ```SubView``` (like a ViewModel) were re initialized when the ContentView was re rendered (for example if I provide a custom Init() for a ```View```, then the property is probably re initialized, except if I use the ```@StateObject``` property wrapper). – alpennec Nov 15 '20 at 09:14

1 Answers1

1

Now, I'm not an expert and don't have any sources for this, but I could imagine it something like this:

When you change the items variable, SwiftUI has to find out which subviews are depending on that value so that they can be re-rendered with the correct (updated) values. One easy way to find out if an object has changed is via hashes. So, I think SwiftUI (internally) re-initializes the view, computes a hash over the struct and if it differs from the hash of the current view, it gets re-rendered. Otherwise, the change (of items) didn't effect the view so it doesn't need to be re-rendered.

EDIT: for clarity, a computed property (like body) isn't initialzed when the rest of the object is. It is computed on demand. That is why you can also use other variables in them.

thisIsTheFoxe
  • 1,584
  • 1
  • 9
  • 30