I use GCD's DispatchWorkItem
to keep track of my data that's being sent to firebase.
The first thing I do is declare 2 class properties of type DispatchWorkItem
and then when I'm ready to send the data to firebase I initialize them with values.
The first property is named errorTask
. When initialized it cancels
the firebaseTask
and sets it to nil
then prints "errorTask fired". It has a DispatchAsync Timer
that will call it in 0.0000000001 seconds if the errorTask isn't cancelled before then.
The second property is named firebaseTask
. When initialized it contains a function that sends the data to firebase. If the firebase callback is successful then errorTask
is cancelled and set to nil
and then a print statement "firebase callback was reached" prints. I also check to see if the firebaseTask was cancelled.
The problem is the code inside the errorTask
always runs before the firebaseTask
callback is reached. The errorTask
code cancels the firebaseTask
and sets it to nil but for some reason the firebaseTask
still runs. I can't figure out why?
The print statements support the fact that the errorTask runs first because
"errorTask fired"
always gets printed before "firebase callback was reached"
.
How come the firebaseTask isn't getting cancelled and set to nil even though the errorTask makes those things happen?
Inside my actual app what happens is if a user is sending some data to Firebase an activity indicator appears. Once the firebase callback is reached then the activity indicator is dismissed and an alert is shown to the user saying it was successful. However if the activity indicator doesn't have a timer on it and the callback is never reached then it will spin forever. The DispatchAsyc after
has a timer set for 15 secs and if the callback isn't reached an error label would show. 9 out of 10 times it always works .
- send data to FB
- show activity indicator
- callback reached so cancel errorTask, set it to nil, and dismiss activity indicator
- show success alert.
But every once in while
- it would take longer then 15 secs
firebaseTask
is cancelled and set to nil, and the activity indicator would get dismissed- the error label would show
- the success alert would still appear
The errorTask
code block dismisses the actiInd, shows the errorLabel, and cancels the firebaseTask
and sets it to nil. Once the firebaseTask is cancelled and set to nil I assumed everything inside of it would stop also because the callback was never reached. This may be the cause of my confusion. It seems as if even though the firebaseTask
is cancelled and set to nil, someRef?.updateChildValues(...
is somehow still running and I need to cancel that also.
My code:
var errorTask:DispatchWorkItem?
var firebaseTask:DispatchWorkItem?
@IBAction func buttonPush(_ sender: UIButton) {
// 1. initialize the errorTask to cancel the firebaseTask and set it to nil
errorTask = DispatchWorkItem{ [weak self] in
self?.firebaseTask?.cancel()
self?.firebaseTask = nil
print("errorTask fired")
// present alert that there is a problem
}
// 2. if the errorTask isn't cancelled in 0.0000000001 seconds then run the code inside of it
DispatchQueue.main.asyncAfter(deadline: .now() + 0.0000000001, execute: self.errorTask!)
// 3. initialize the firebaseTask with the function to send the data to firebase
firebaseTask = DispatchWorkItem{ [weak self] in
// 4. Check to see the if firebaseTask was cancelled and if it wasn't then run the code
if self?.firebaseTask?.isCancelled != true{
self?.sendDataToFirebase()
}
// I also tried it WITHOUT using "if firebaseTask?.isCancelled... but the same thing happens
}
// 5. immediately perform the firebaseTask
firebaseTask?.perform()
}
func sendDataToFirebase(){
let someRef = Database.database().reference().child("someRef")
someRef?.updateChildValues(myDict(), withCompletionBlock: {
(error, ref) in
// 6. if the callback to firebase is successful then cancel the errorTask and set it to nil
self.errorTask?.cancel()
self.errorTask? = nil
print("firebase callback was reached")
})
}