skip to Main Content

I am not sure if I use it properly, but the case is the following:

I have a View:

struct TimerView: View {
    @State private var numberOfVideos: Int
    @State private var time = ""
    private let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()

    init(numberOfVideos: Int) {
        _numberOfVideos = State(initialValue: numberOfVideos)
    }

    var body: some View {
        Text(time)
            .onReceive(timer) { _ in
                // it is called every second
                time = "00:00"
            }
        ReportView(numberOfVideos: $numberOfVideos)
    }
}

struct ReportView: View {
    @Binding private var numberOfVideos: Int
    init(numberOfVideos: Binding<Int>) {
        // ❌ this is called every second, I don't want it.
        self._numberOfVideos = numberOfVideos
    }

    var body: some View {
        Text(String(numberOfVideos))
    }
}

Usage:

TimerView(numberOfVideos: 5)

Instantiating ReportView every second this is not what I need. How can I fix this?

2

Answers


  1. The problem is that you are storing numberOfVideos as Binding on ReportView.

    You should only use Binding to inject mutable state into subviews, which you want to redraw whenever that mutable state changes either from inside or outside the view.

    Since you are never mutating numberOfVideos from inside ReportView, you should store it as an immutable property. If TimerView would update numberOfVideos, that would redraw the whole TimerView and hence create a new ReportView with the updated number as well.

    This ensures that ReportView isn’t recreated every time time is updated on TimerView.

    struct ReportView: View {
      private let numberOfVideos: Int
    
      init(numberOfVideos: Int) {
        self.numberOfVideos = numberOfVideos
      }
    
      var body: some View {
        Text(String(numberOfVideos))
      }
    }
    
    Login or Signup to reply.
  2. Jut move timer logic in separate view like this:

    struct TimerView: View {
        @State private var numberOfVideos: Int
        @State private var time = ""
    
        init(numberOfVideos: Int) {
            _numberOfVideos = State(initialValue: numberOfVideos)
        }
    
        var body: some View {
            TimeText(text: $time)
            ReportView(numberOfVideos: $numberOfVideos)
                // .background(.debug)
        }
    }
    
    struct ReportView: View {
        @Binding private var numberOfVideos: Int
        init(numberOfVideos: Binding<Int>) {
            // ❌ this is called every second, I dont want it.
            self._numberOfVideos = numberOfVideos
        }
    
        var body: some View {
            Text(String(numberOfVideos))
        }
    }
    
    struct TimeText: View {
        @Binding var text: String
        private let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
        
        var body: some View {
            Text(text)
                .onReceive(timer) { t in
                    // it is called every second
                    text = t.debugDescription
                }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search