DispatchQueue.main.asyncAfter с включением / выключением - PullRequest
1 голос
/ 09 марта 2019

Я использовал приведенную ниже структуру, чтобы предупредить пользователя о медленном сетевом соединении.

Когда функция собирается позвонить на сервер, она создает ResponseTimer. Это устанавливает отложенное уведомление, которое срабатывает только если responseTimer var isOn = true. Когда моя функция get ответит от сервера, установите responseTimer.isOn = false.

Вот структура:

struct ResponseTimer {

var isOn: Bool

init() {
    self.isOn = true
    self.setDelayedAlert()
}

func setDelayedAlert() {
    let timer = DispatchTime.now() + 8
    DispatchQueue.main.asyncAfter(deadline: timer) {
        if self.isOn {
            NotificationCenter.default.post(name: NSNotification.Name(rawValue: toastErrorNotificationKey), object: self, userInfo: ["toastErrorCase" : ToastErrorCase.poorConnection])
        }
    }
}

А вот как бы я это использовал

func getSomethingFromFirebase() {

    var responseTimer = ResponseTimer()

    ref.observeSingleEvent(of: .value, with: { snapshot in
        responseTimer.isOn = false

        //do other stuff
    })
}

Даже в тех случаях, когда ответ возвращается до истечения 8-секундной задержки, уведомление все равно срабатывает. Что я здесь не так делаю ??? Есть ли лучший образец для использования для чего-то вроде этого?

Спасибо за помощь!

Ответы [ 2 ]

2 голосов
/ 09 марта 2019

A Timer - лучший способ сделать это (зачем держать эту отправленную задачу там, когда вы получили ответ, и вы знаете, что он вам больше не нужен). Но я не вижу ничего выше, что могло бы вызвать поведение, которое вы описываете. Я бы посоветовал вам добавить некоторые логичные записи и / или «часы» на isOn, чтобы вы могли видеть, где они устанавливаются / сбрасываются.

Но реализация Timer может выглядеть так:

class ResponseTimer {
    private weak var timer: Timer?

    func schedule() {
        timer = Timer.scheduledTimer(withTimeInterval: 8, repeats: false) { _ in // if you reference `self` in this block, make sure to include `[weak self]` capture list, too
            NotificationCenter.default.post(...)
        }
    }

    func invalidate() {
        timer?.invalidate()
    }

    // you might want to make sure you `invalidate` this when it’s deallocated just in 
    // case you accidentally had path of execution that failed to call `invalidate`.

    deinit {
        invalidate()
    }
}

И тогда вы можете сделать:

func getSomethingFromFirebase() {

    var responseTimer = ResponseTimer()
    responseTimer.schedule()

    ref.observeSingleEvent(of: .value) { snapshot in
        responseTimer.invalidate()

        //do other stuff
    }
}
2 голосов
/ 09 марта 2019

Лучше использовать DispatchSourceTimer, который можно отменить

var timer : DispatchSourceTimer?

func startTimer()
{
    if timer == nil {
        timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global())
        timer!.schedule(deadline: .now() + .seconds(8))
        timer!.setEventHandler {
            DispatchQueue.main.async {
                NotificationCenter.default.post(name: NSNotification.Name(rawValue: toastErrorNotificationKey), object: self, userInfo: ["toastErrorCase" : ToastErrorCase.poorConnection])
            }
            self.timer = nil
        }
        timer!.resume()
    } 
}

func getSomethingFromFirebase() {

    startTimer()

    ref.observeSingleEvent(of: .value, with: { snapshot in
         self.timer?.cancel()
         self.timer = nil
        //do other stuff
    })
}
...