Reputation: 1233
I have a function which downloads data from firebase:
func downloadData(path: String) -> Data? {
let data: Data?
let storage = Storage.storage()
storage.reference(forURL: path).getData(maxSize: 1024 * 1024) { (result, error) in
if let error = error {
print("\(error)")
} else {
data = result
print("finished")
}
return data // <-- this is called before closue ends
}
but it is not returning the value from storage closure. I tried using a dispatch group
func downloadData(path: String) -> Data? {
...
let group = DispatchGroup()
group.enter()
storage.reference(forURL: path).getData(maxSize: 1024 * 1024) { (result, error) in
...
...
group.leave()
}
group.notify(queue: .main) {
return data // <-- build time error! the function doesn't return a value
}
}
so I tried a different approach: instead of group.notify() I used:
group.wait()
but it didn't work also. the whole app was frozen and nothing happened
and I tried another one:
let semaphore = DispatchSemaphore(value: 0)
storage.reference(forURL: path).getData(maxSize: 1024 * 1024) { (result, error) in
...
...
semaphore.signal()
}
semaphore.wait()
return data // <-- nothing happens... it is not being executed
Any ideas on how to figure this out?
UPDATE
I tried using the completion option but it still didn't wait until it is downloded.
this is my outerFunction:
var soundData: Data?
var imageData: Data?
downloadData(path: soundPath) { sound in
soundData = sound
downloadData(path: imageData) { image in
imageData = image
}
}
doMoreStuff() // <-- called before completeion blocks executed
Upvotes: 1
Views: 183
Reputation: 16341
You should use completion block for data instead of returning the data, here's how:
func downloadData(path: String, completion: @escaping ((Data?) -> Void)?) {
...
let group = DispatchGroup()
group.enter()
var data: Data?
storage.reference(forURL: path).getData(maxSize: 1024 * 1024) { (result, error) in
...
...
data = dataReceived
group.leave()
}
group.notify(queue: .main) {
completion?(data)
}
}
Upvotes: 1
Reputation: 300
You can't really do that without some kind of completion closure, because you'd be blocking the thread which calls the function, probably the UI thread.
Another alternative would be to use Apple's new Combine framework and return a Future
import Combine
func downloadData(path: String) -> Future<Data, Error> {
return Future<Data, Error> { promise in
let storage = Storage.storage()
storage.reference(forURL: path).getData(maxSize: 1024 * 1024) { (result, error) in
if let error = error {
promise(.failure(error))
} else {
promise(.success(result))
}
}
}
}
var bag = Set<AnyCancellable>()
downloadData(path: "test")
.subscribe(on: DispatchQueue.main)
.sink(receiveCompletion: { (res) in
<#code#>
}) { (data) in
<#code#>
}
.store(in: &bag)
Upvotes: 1
Reputation: 3514
Use completion instead of returning ->
func downloadData(path: String, _ completion: @escaping (Data?) -> Void) {
let data: Data?
let storage = Storage.storage()
storage.reference(forURL: path).getData(maxSize: 1024 * 1024) { (result, error) in
if let error = error {
print("\(error)")
} else {
data = result
print("finished")
}
completion(data)
}
}
Usage of the downloadData(path:,_)
downloadData(path: "") { data in
// Your data is in here
print("Data is:", data)
}
Upvotes: 1