如何在 class 初始化时使用调度组异步等待 Firebase 回调?
How to use dispatch group to asynchronously await Firebase callback upon class initialization?
我正在通过 Firebase DataSnapshot 实例化 User
class。在调用初始化程序 init(snapshot: DataSnapshot)
时,它应该通过 getFirebasePictureURL
和 getFirebaseNameString
方法的 @escaping 从两个数据库引用异步检索值,即 pictureRef
和 nameRef
完成处理程序(使用 Firebase 的 observeSingleEvent
方法)。引用 pictureRef
和 nameRef
都是单个父节点的子节点。但是,在实例化class时,它从不初始化名称和图片User
class属性,因为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!)
})
}
}
有人在 中建议我将 @escaping 完成处理程序添加到 init
方法:
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)
}
}
}
但是,如果我在此 class 之外的方法中通过 User(snapshot: snapshot)
初始化此 class,则需要将该方法的主体封装在完成处理程序中User
init 方法,它不适用于我当前的项目。
有没有办法使用调度组来暂停主线程上的执行,直到 fullName
和 pictureURL
填充了值?还是有其他方法可以做到这一点?
Is there a way to employ dispatch groups to pause execution on the main thread until the fullName
and pictureURL
are populated with values? Or is there an alternative way of doing this?
好吧,你永远不想“暂停”执行。但是,您可以使用调度组在两个异步方法完成时以及现在可以创建新实例时通知您的应用程序:
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
方法中。相反,我会翻转它并在完成两个异步方法后创建您的实例。
我正在通过 Firebase DataSnapshot 实例化 User
class。在调用初始化程序 init(snapshot: DataSnapshot)
时,它应该通过 getFirebasePictureURL
和 getFirebaseNameString
方法的 @escaping 从两个数据库引用异步检索值,即 pictureRef
和 nameRef
完成处理程序(使用 Firebase 的 observeSingleEvent
方法)。引用 pictureRef
和 nameRef
都是单个父节点的子节点。但是,在实例化class时,它从不初始化名称和图片User
class属性,因为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
方法:
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)
}
}
}
但是,如果我在此 class 之外的方法中通过 User(snapshot: snapshot)
初始化此 class,则需要将该方法的主体封装在完成处理程序中User
init 方法,它不适用于我当前的项目。
有没有办法使用调度组来暂停主线程上的执行,直到 fullName
和 pictureURL
填充了值?还是有其他方法可以做到这一点?
Is there a way to employ dispatch groups to pause execution on the main thread until the
fullName
andpictureURL
are populated with values? Or is there an alternative way of doing this?
好吧,你永远不想“暂停”执行。但是,您可以使用调度组在两个异步方法完成时以及现在可以创建新实例时通知您的应用程序:
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
方法中。相反,我会翻转它并在完成两个异步方法后创建您的实例。