Lew*_*ack 12 ios firebase completionhandler swift firebase-realtime-database
我有一个简单的功能从Firebase加载数据.
func loadFromFireBase() -> Array<Song>? {
var songArray:Array<Song> = []
ref.observe(.value, with: { snapshot in
//Load songArray
})
if songArray.isEmpty {
return nil
}
return songArray
}
Run Code Online (Sandbox Code Playgroud)
目前nil,即使有要加载的数据,此函数也会始终返回.它这样做是因为它不会在函数返回之前执行完成块,它会加载数组.我正在寻找一种方法,只有在调用完成块后才能返回该函数,但我无法在完成块中返回.
Dun*_*n C 13
(关于这个问题的变化经常出现在SO上.我永远找不到一个好的,全面的答案,所以下面是尝试提供这样的答案)
你不能这样做.Firebase是异步的.它的函数采用完成处理程序并立即返回.您需要重写loadFromFirebase函数以获取完成处理程序.
我在Github上有一个名为Async_demo(link)的示例项目,它是一个工作(Swift 3)应用程序,说明了这种技术.
关键部分是函数downloadFileAtURL,它接受完成处理程序并执行异步下载:
typealias DataClosure = (Data?, Error?) -> Void
/**
This class is a trivial example of a class that handles async processing. It offers a single function, `downloadFileAtURL()`
*/
class DownloadManager: NSObject {
static var downloadManager = DownloadManager()
private lazy var session: URLSession = {
return URLSession.shared
}()
/**
This function demonstrates handling an async task.
- Parameter url The url to download
- Parameter completion: A completion handler to execute once the download is finished
*/
func downloadFileAtURL(_ url: URL, completion: @escaping DataClosure) {
//We create a URLRequest that does not allow caching so you can see the download take place
let request = URLRequest(url: url,
cachePolicy: .reloadIgnoringLocalAndRemoteCacheData,
timeoutInterval: 30.0)
let dataTask = URLSession.shared.dataTask(with: request) {
//------------------------------------------
//This is the completion handler, which runs LATER,
//after downloadFileAtURL has returned.
data, response, error in
//Perform the completion handler on the main thread
DispatchQueue.main.async() {
//Call the copmletion handler that was passed to us
completion(data, error)
}
//------------------------------------------
}
dataTask.resume()
//When we get here the data task will NOT have completed yet!
}
}
Run Code Online (Sandbox Code Playgroud)
上面的代码使用Apple的URLSession类异步下载远程服务器中的数据.在创建a时dataTask,传入一个在数据任务完成(或失败)时调用的完成处理程序.但要注意:在后台线程上调用完成处理程序.
这很好,因为如果您需要执行耗时的处理(如解析大型JSON或XML结构),您可以在完成处理程序中执行此操作,而不会导致应用程序的UI冻结.但是,因此,如果不将这些UI调用发送到主线程,则无法在数据任务完成处理程序中执行UI调用.上面的代码使用调用来调用主线程上的整个完成处理程序DispatchQueue.main.async() {}.
回到OP的代码:
我发现一个带闭包作为参数的函数很难读,因此我通常将闭包定义为一个类型.
从@ Raghav7890的答案重新编写代码以使用类型:
typealias SongArrayClosure = (Array<Song>?) -> Void
func loadFromFireBase(completionHandler: @escaping SongArrayClosure) {
ref.observe(.value, with: { snapshot in
var songArray:Array<Song> = []
//Put code here to load songArray from the FireBase returned data
if songArray.isEmpty {
completionHandler(nil)
}else {
completionHandler(songArray)
}
})
}
Run Code Online (Sandbox Code Playgroud)
我很长一段时间没有使用Firebase(然后只修改了其他人的Firebase项目),所以我不记得它是否在主线程或后台线程上调用它的完成处理程序.如果它在后台线程上调用完成处理程序,那么您可能希望在对主线程的GCD调用中将调用包装到完成处理程序中.
根据这个SO问题的答案,听起来像Firebase在后台线程上进行网络调用,但在主线程上调用它的侦听器.
在这种情况下,您可以忽略Firebase下面的代码,但对于那些阅读此线程以获取其他种类异步代码的帮助的人,以下是如何重写代码以调用主线程上的完成处理程序:
typealias SongArrayClosure = (Array<Song>?) -> Void
func loadFromFireBase(completionHandler:@escaping SongArrayClosure) {
ref.observe(.value, with: { snapshot in
var songArray:Array<Song> = []
//Put code here to load songArray from the FireBase returned data
//Pass songArray to the completion handler on the main thread.
DispatchQueue.main.async() {
if songArray.isEmpty {
completionHandler(nil)
}else {
completionHandler(songArray)
}
}
})
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
9088 次 |
| 最近记录: |