Reputation: 379
I am trying to loop through two Firebase database references, firstRef
and secondRef
, observe the respective values with the observeSingleEvent
Firebase async method, and append the values to two arrays, firstArray
and secondArray
. Once this has been completed, I would like to notify the dispatch group and execute a completion handler. However, when the following method is called the two arrays are always unexpectedly found nil. I'm not too sure why this is or whether this problem arises because I have nested asynchronous functions. I know force unwrapping is also syntactically frowned upon, but, in this case, I expect the two arrays to always be populated with data retrieved from the database.
func loop(inputArray: [inputType], doneLoop: @escaping (_ firstArray: [firstType],_ secondArray: [secondType]) -> Void) {
var firstArray: [firstType]?
var nameArray: [secondType]?
let dispatchGroup = DispatchGroup()
for element in inputArray {
let firstRef = Database.database().reference(withPath: "firstPath")
//Enter dispatch group
dispatchGroup.enter()
firstRef.observeSingleEvent(of: .value, with: { snapshot in
//Storing first snapshot value
let firstSnapshotValue = snapshot.value as! firstType
let secondRef = Database.database().reference(withPath: "secondPath")
secondRef.observeSingleEvent(of: .value, with: { snapshot in
//Storing second snapshot value
let secondSnapshotValue = snapshot.value as? String
//Append retrieved values to arrays
firstArray?.append(firstSnapshotValue)
secondArray?.append(secondSnapshotValue)
})
})
//Leave dispatch group
dispatchGroup.leave()
}
//Notify on main queue and execute completion handler
dispatchGroup.notify(queue: .main) {
doneLoop(firstArray!, secondArray!)
}
}
Do I perhaps have to create a new function with an @escaping completion handler? The only problem is that since it is looping through the array, the completion will only be executed once and so only one value would have be populated.
Any help would be much appreciated!
Upvotes: 0
Views: 581
Reputation: 3618
You should make both firstArray
and nameArray
:
var firstArray = [firstType]()
var nameArray = [secondType]()
And leave()
the dispatchGroup
inside the innermost callback. And consider wrapping it in a defer{}
, protecting against any multiple paths inside the callback in the future.
func loop(inputArray: [inputType], doneLoop: @escaping (_ firstArray: [firstType],_ secondArray: [secondType]) -> Void) {
var firstArray = [firstType]()
var nameArray = [secondType]()
let dispatchGroup = DispatchGroup()
for element in inputArray {
let firstRef = Database.database().reference(withPath: "firstPath")
//Enter dispatch group
dispatchGroup.enter()
firstRef.observeSingleEvent(of: .value, with: { snapshot in
//Storing first snapshot value
let firstSnapshotValue = snapshot.value as! firstType
let secondRef = Database.database().reference(withPath: "secondPath")
secondRef.observeSingleEvent(of: .value, with: { snapshot in
//Leave dispatch group
defer { dispatchGroup.leave() }
//Storing second snapshot value
let secondSnapshotValue = snapshot.value as? String
//Append retrieved values to arrays
firstArray.append(firstSnapshotValue)
secondArray.append(secondSnapshotValue)
})
})
}
//Notify on main queue and execute completion handler
dispatchGroup.notify(queue: .main) {
doneLoop(firstArray, secondArray)
}
}
Also, if the values from secondRef
are independent, consider unnesting the inner observeSingleEvent
call (and adding additional enter()
and leave()
calls).
As is, your code will potentially/eventually lose writes to both firstArray
and secondArray
. The faster Firebase's observeSingleEvent
executes its with
block, the sooner the data will be lost. To avoid this, all writes (append
s) should be done from a serial queue:
let queue = DispatchQueue(label: "tld.company.app.queue")
queue.async() {
// Write
}
queue.sync() {
// Read
}
Or, even better, keep reads concurrent with the .barrier
flag:
let queue = DispatchQueue(label: "tld.company.app.queue", attributes: .concurrent)
queue.async(flags: .barrier) {
// Write
}
queue.sync() {
// Read
}
Read more: Create thread safe array in Swift
Upvotes: 2