Возвращает значение внутри функции после получения значения из закрытия хранилища firebase Swift - PullRequest
1 голос
/ 29 мая 2020

У меня есть функция, которая загружает данные из 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
}

, но она не возвращает значение из закрытия хранилища. Я попытался использовать группу отправки

 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
    }
}

, поэтому я попробовал другой подход: вместо group.notify () я использовал:

group.wait()

, но это тоже не сработало. все приложение было заморожено, и ничего не произошло

, и я попробовал другое:

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

Есть идеи, как это выяснить?

ОБНОВЛЕНИЕ

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

это моя внешняя функция:

    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

Ответы [ 3 ]

1 голос
/ 29 мая 2020

Вы должны использовать блок завершения для данных вместо того, чтобы возвращать данные, вот как:

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)
    }
}
1 голос
/ 29 мая 2020

Вы не можете сделать это без какого-либо закрытия завершения, потому что вы заблокируете поток, который вызывает функцию, возможно, поток пользовательского интерфейса.

Другой альтернативой было бы использование нового Combine от Apple framework и вернуть 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)
1 голос
/ 29 мая 2020

Использовать завершение вместо возврата ->

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)
    }
}

Использование downloadData(path:,_)

downloadData(path: "") { data in
    // Your data is in here 
    print("Data is:", data)
}
...