我正在通过Firebase DataSnapshot实例化一个User
类。在调用初始值设定项init(snapshot: DataSnapshot)
时,它应该通过getFirebasePictureURL
和getFirebaseNameString
方法的@escaping完成处理程序(使用Firebase的observeSingleEvent
方法(从两个数据库引用(即pictureRef
和nameRef
(中异步检索值。引用pictureRef
和nameRef
都是单个父节点的子节点。但是,在实例化类时,它从不初始化类属性的名称和图片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)
初始化此类,我将该方法的主体封装在User
init 方法的完成处理程序中,这不适用于我当前的项目。
有没有办法使用调度组来暂停主线程上的执行,直到fullName
和pictureURL
填充值?还是有其他方法可以做到这一点?
有没有办法使用调度组来暂停主线程上的执行,直到
fullName
和pictureURL
填充值?还是有其他方法可以做到这一点?
好吧,你永远不想"暂停"执行。但是,可以使用调度组在两个异步方法完成以及现在可以创建新实例时通知应用:
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
方法中。相反,我会翻转它并在两个异步方法完成后创建您的实例。