如何使用调度组在类初始化时异步等待 Firebase 回调?



我正在通过Firebase DataSnapshot实例化一个User类。在调用初始值设定项init(snapshot: DataSnapshot)时,它应该通过getFirebasePictureURLgetFirebaseNameString方法的@escaping完成处理程序(使用Firebase的observeSingleEvent方法(从两个数据库引用(即pictureRefnameRef(中异步检索值。引用pictureRefnameRef都是单个父节点的子节点。但是,在实例化类时,它从不初始化类属性的名称和图片User因为init是同步执行的。

import Firebase
class User {
var uid: String
var fullName: String? = ""
var pictureURL: URL? = URL(string: "initial")
//DataSnapshot Initializer
init(snapshot: DataSnapshot) {
self.uid = snapshot.key
getFirebasePictureURL(userId: uid) { (url) in
self.getFirebaseNameString(userId: self.uid) { (fullName) in
self.fullName = fullName
self.profilePictureURL = url
}
}
func getFirebasePictureURL(userId: String, completion: @escaping (_ url: URL) -> Void) {
let currentUserId = userId
//Firebase database picture reference
let pictureRef = Database.database().reference(withPath: "pictureChildPath")
pictureRef.observeSingleEvent(of: .value, with: { snapshot in
//Picture url string
let pictureString = snapshot.value as! String
//Completion handler (escaping)
completion(URL(string: pictureString)!)
})
}

func getFirebaseNameString(userId: String, completion: @escaping (_ fullName: String) -> Void) {
let currentUserId = userId
//Firebase database name reference
let nameRef = Database.database().reference(withPath: "nameChildPath")
nameRef.observeSingleEvent(of: .value, with: { snapshot in
let fullName = snapshot.value as? String
//Completion handler (escaping)
completion(fullName!)
})
}
}

在上一篇文章中有人建议我向init方法添加一个@escaping完成处理程序:

init(snapshot: DataSnapshot, completionHandler: @escaping (User) -> Void) {
self.uid = snapshot.key
getFirebasePictureURL(userId: uid) { (url) in
self.getFirebaseNameString(userId: self.uid) { (fullName) in
self.fullName = fullName
self.profilePictureURL = url
completionHandler(self)
}
}
}

但是,这将要求如果我通过此类之外的方法中的User(snapshot: snapshot)初始化此类,我将该方法的主体封装在Userinit 方法的完成处理程序中,这不适用于我当前的项目。

有没有办法使用调度组来暂停主线程上的执行,直到fullNamepictureURL填充值?还是有其他方法可以做到这一点?

有没有办法使用调度组来暂停主线程上的执行,直到fullNamepictureURL填充值?还是有其他方法可以做到这一点?

好吧,你永远不想"暂停"执行。但是,可以使用调度组在两个异步方法完成以及现在可以创建新实例时通知应用:

func createUser(for userId: String, completion: @escaping (User) -> Void) {
var pictureUrl: URL?
var fullName: String?
let group = DispatchGroup()
group.enter()
getFirebaseNameString(userId: userId) { name in
fullName = name
group.leave()
}
group.enter()
getFirebasePictureURL(userId: userId) { url in
pictureUrl = url
group.leave()
}
group.notify(queue: .main) {
guard
let pictureUrl = pictureUrl, 
let fullName = fullName 
else { return }
completion(User(uid: userId, fullName: fullName, pictureURL: pictureUrl))
}
}

然后:

let userId = ...
createUser(for: userId) { user in 
// use `User` instance here, e.g. creating your new node
}

现在简化User

class User {
let uid: String
let fullName: String
let pictureURL: URL
init(uid: String, fullName: String, pictureURL: URL) {
self.uid = uid
self.fullName = fullName
self.pictureURL = pictureURL
}
}

但我建议不要尝试将异步代码埋在init方法中。相反,我会翻转它并在两个异步方法完成后创建您的实例。

最新更新