skip to Main Content

I would like to pass a timer from ContentView to SecondView, but I don’t know how to manage it because I never used it before.

Can someone figure this out for me?

ContentView

struct ContentView: View {
    @State private var timer = Timer.publish(every: 1, tolerance: 0.5, on: .main, in: .common).autoconnect()
    @State private var timeRemaining = 10
    
    
    var body: some View {
        NavigationView {
            VStack {
                Text("(timeRemaining)")
                    .onReceive(timer) { _ in
                        if timeRemaining > 0 {
                            timeRemaining -= 1
                        }
                    }
                
                NavigationLink {
                    SecondView(timer: ???) // <-- What should i pass here?
                } label: {
                    Text("Change View")
                }
            }
        }
    }
}

SecondView

struct SecondView: View {
    @Binding var timer: ??? // <-- What type?
    @State private var timeRemaining = 5
    
    var body: some View {
        Text("Hello")
            .onReceive(timer) { _ in
                if timeRemaining > 0 {
                    timeRemaining -= 1
                }
            }
    }
}

struct SecondView_Previews: PreviewProvider {
    static var previews: some View {
        SecondView(timer: ???) // <-- Same thing here in SecondView preview
    }
}

3

Answers


  1. With this timer declaration you are in the Combine world. Combine is the reactive framework from Apple.

    First you would need to import it:

    import Combine
    

    I have commented the code but Combine is a far field and it probably would be best to read the documentation about it, read some tutorials and try some things out.

    documentation

    struct ContentView: View {
        // The typ here is Publishers.Autoconnect<Timer.TimerPublisher>
        // But we can erase it and the result will be a Publisher that emits a date and never throws an error: AnyPublisher<Date,Never>
        @State private var timer = Timer.publish(every: 1, tolerance: 0.5, on: .main, in: .common)
            .autoconnect()
            .eraseToAnyPublisher()
        @State private var timeRemaining = 10
        
        
        var body: some View {
            NavigationView {
                VStack {
                    Text("(timeRemaining)")
                        .onReceive(timer) { _ in
                            if timeRemaining > 0 {
                                timeRemaining -= 1
                            }
                        }
                    
                    NavigationLink {
                        // pass the publisher on
                        SecondView(timer: timer)
                    } label: {
                        Text("Change View")
                    }
                }
            }
        }
    }
    
    struct SecondView: View {
        //You don´t need binding here as this view never manipulates this publisher
        var timer: AnyPublisher<Date,Never>
        @State private var timeRemaining = 5
        
        var body: some View {
            Text("Hello")
                .onReceive(timer) { _ in
                    if timeRemaining > 0 {
                        timeRemaining -= 1
                        print(timeRemaining)
                    }
                }
        }
    }
    
    struct SecondView_Previews: PreviewProvider {
        // Creating a static private var should work here !not tested!
        @State static private var timer = Timer.publish(every: 1, tolerance: 0.5, on: .main, in: .common)
            .autoconnect()
            .eraseToAnyPublisher()
        static var previews: some View {
            SecondView(timer: timer)
        }
    }
    
    Login or Signup to reply.
  2. You could simply inject the timer publisher, as suggested above, but there may be an even simpler solution:

    FirstView is already updating with every tick of the timer. You could simply pass a timeRemaning binding to your second view and then it too would just update with every tick of the timer (because timeRemaining changes on each tick). You can then observe and react to changes of timeRemaining using .onChange(of:):

    struct SecondView: View {
        @Binding var timeRemaining: TimeInterval
    
        var body: some View {
            Text("Hello")
               .onChange(of: timeRemaining) {
                   if $0 < 0 { 
                       timeRemaining = -1
                   }
               }
        }
    }
                   
    
    Login or Signup to reply.
  3. You don’t need to pass a binding, Since you are not mutating timer of contentview from the second view. You can just pass the reference to the timer publisher and then subscribe to it using .onReceive().

    import Combine // here
    
    struct ContentView: View {
        let timer = Timer.publish(every: 1, tolerance: 0.5, on: .main, in: .common).autoconnect().eraseToAnyPublisher() //<= Here
        @State private var timeRemaining = 10
        
        
        var body: some View {
            NavigationView {
                VStack {
                    Text("(timeRemaining)")
                        .onReceive(timer) { _ in
                            if timeRemaining > 0 {
                                timeRemaining -= 1
                            }
                        }
                    
                    NavigationLink {
                        SecondView(timer: timer)
                    } label: {
                        Text("Change View")
                    }
                }
            }
        }
    }
    
    struct SecondView: View {
        let timer: AnyPublisher<Date, Never> // Here
        @State private var timeRemaining = 5
        
        var body: some View {
            VStack {
                Text("Hello")
                    .onReceive(timer) { _ in
                        if timeRemaining > 0 {
                            timeRemaining -= 1
                        }
                    }
                Text("time remaining (timeRemaining)")
            }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search