skip to Main Content

I am trying to cancel a delayed execution of a function running on the main queue, in a tap gesture, I found a way to create a cancellable DispatchWorkItem, but the issue I have is that it’s getting created every time while tapping, and then when I cancel the execution, I actually cancel the new delayed execution and not the first one.

Here is a simpler example with a Timer instead of a DispatchQueue.main.asyncAfter:

.onTapGesture {
    isDeleting.toggle()
    let timer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) { timer in
        completeTask()
    }
        
    if !isDeleting {
        timer.invalidate()
    }
 }

completeTask:

  private func completeTask() {
    tasksViewModel.deleteTask(task: task) // task is declared above this func at the top level of the struct and so is tasksViewModel, etc.
    guard let userID = userViewModel.id?.uuidString else { return }
    Task {
        //do some async stuff
    }
}

As you can see if I click it once the timer fires, but if I click it again, another timer fires and straight away invalidates, but the first timer is still running.
So I have to find a way to create only one instance of that timer.
I tried putting it in the top level of the struct and not inside the var body but the issue now is that I can’t use completeTask() because it uses variables that are declared at the same scope.
Also, can’t use a lazy initialization because it is an immutable struct.

My goal is to eventually let the user cancel a timed task and reactivate it at will on tapping a button/view. Also, the timed task should use variables that are declared at the top level of the struct.

2

Answers


  1. that is my solution mb can help you
    var timer: Timer?

    private func produceWorkItem(withDelay: Double = 3) {
            scrollItem?.cancel()
            timer?.invalidate()
            scrollItem = DispatchWorkItem.init { [weak self] in
                self?.timer = Timer.scheduledTimer(withTimeInterval: withDelay, repeats: false) { [weak self] _ in
                    self?.goToNextPage(animated: true, completion: { [weak self] _ in self?.produceWorkItem() })
                    guard let currentVC = self?.viewControllers?.first,
                          let index = self?.pages.firstIndex(of: currentVC) else {
                        return
                    }
                    self?.pageControl.currentPage = index
                }
            }
            
            scrollItem?.perform()
        }
    

    for stop use scrollItem?.cancel()
    for start call func

    Login or Signup to reply.
  2. First of all you need to create a strong reference of timer on local context like so:

    var timer: Timer?
    

    and then, set the timer value on onTapGesture closure:

    .onTapGesture {
        isDeleting.toggle()
        self.timer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) { timer in
            completeTask()
        }
            
        if !isDeleting {
            timer.invalidate()
        }
     }
    

    and after that you can invalidate this Timer whenever you need by accessing the local variable timer like this:

    func doSomething() {
        timer?.invalidate()
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search