When I study the SwiftUI Apprentice book of Kodeco, I come across the below code snippet.
struct CountdownView: View {
  let date: Date
  @Binding var timeRemaining: Int
  let size: Double
  var body: some View {
    Text("\(timeRemaining)")  // 5
      .font(.system(size: size, design: .rounded))
      .padding()
      .onChange(of: date) { _ in  // 6
        timeRemaining -= 1
      }
  }
}
struct TimerView: View {
  @State private var timeRemaining: Int = 3  // 1
  @Binding var timerDone: Bool  // 2
  let size: Double
  var body: some View {
    TimelineView(  // 3
      .animation(
        minimumInterval: 1.0,
        paused: timeRemaining <= 0)) { context in
          CountdownView(  // 4
            date: context.date,
            timeRemaining: $timeRemaining,
            size: size)
        }
        .onChange(of: timeRemaining) { _ in
          if timeRemaining < 1 {
            timerDone = true  // 7
          }
        }
  }
}
struct TimerView_Previews: PreviewProvider {
  static var previews: some View {
    TimerView(timerDone: .constant(false), size: 90)
  }
}
When I examine the code snippet, I firstly expected to be recreated CountdownView. But there would be no change on date property in that case. Then, I added onAppear modifier to CountdownView to detect the recreating of the view. As a result, it is created only once. I come up with a question to help me to understand the view rending mechanism of SwiftUI, on which I still work that although date property is a constant and does not use @Binding how is it possible to observe the change of a data that is declared as a constant in SwiftUI?
 
                        
While
onAppearcan indeed be used to detect the view being recreated. "Recreated" here does not mean "CountdownView.initgot called". You can handwrite your ownCountdownView.initto detect this.onAppeargets called when the actual thing on the screen gets removed and recreated. This happens when, e.g. the identity of the view changes (learn more about identity in Demystifying SwiftUI). Note that all you are doing inbodyis to provide a description of the view you are trying to create, and SwiftUI creates new views (this callsonAppear), or updates existing views (this doesn't callonAppear), all based on your descriptions. This is unlike in UIKit, where creating aUILabelwith its initialiser directly creates something that can be shown on the screen.In your code, you are calling
CountdownView.initin a closure passed toTimelineView. SwiftUI runs this closure at appropriate times, and when it does so, it compares theView(i.e. the description of what views you want) it returns, with theViewit returned last time.SwiftUI sees that you used
onChange(of: date)both in the previous description, and in this description, so it compares thedates from both descriptions, and finds that they are not equal. Therefore, it calls the closure you passed toonChange.Of course, how it actually works is much more complicated, but this should be just enough to understand why you can
onChangeof aletconstant. SwiftUI is comparing the previous version of your view'sbodywith the new version'sbody.