我希望我的代码在后台 运行 consecutively/synchronously (DispatchQueue)
I want my code to run consecutively/synchronously in the background (DispatchQueue)
我希望 grabAllFollowingPosts()
到 运行 只有在 loadFollowing()
完成 运行ning 之后。这些都是网络调用,所以我想在后台 运行 它们。关于为什么我的代码不起作用的任何想法?
DispatchQueue.global(qos: .userInteractive).sync {
self.loadFollowing()
self.grabAllFollowingPosts()
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
这3个函数的作用是:
- 抓取当前用户关注的每个用户
- 对于每个用户,获取他们的帖子
因此,loadUsers()
必须在 grabAllFollowingPosts()
之前 运行
var followingUsers = [String]()
//Function 1: load the poeple you are following into the followingUsers array
func loadFollowing () {
guard let userID = Auth.auth().currentUser?.uid else { return }
let firestoreRef = Firestore.firestore().collection("Following").document(userID).collection("UserFollowing")
firestoreRef.addSnapshotListener { (snapshot, error) in
if error != nil {
//error retrieving documents
print (error!.localizedDescription)
} else {
// document retrival successful
guard let snapshot = snapshot else { return }
for document in snapshot.documents {
let data = document.data()
let userid = data["UserID"] as? String ?? "anonymous"
self.followingUsers.append(userid)
}
}
}
}
//Function 2: for all of the users in the followingUsers array - grab their documents from Firestore
func grabAllFollowingPosts () {
for users in followingUsers {
loadPosts(theUsers: users)
}
}
//Function 3: loads the posts
func loadPosts (theUsers: String) {
let firestoreRef = Firestore.firestore().collection("Posts").whereField("UserID", isEqualTo: theUsers).whereField("Date", isGreaterThanOrEqualTo: Date()).limit(to: 8)
//TODO: add infinate scroll
firestoreRef.addSnapshotListener { (snapshot, error) in
if error != nil {
//error retrieving documents
print (error!.localizedDescription)
} else {
// document retrival successful
guard let snapshot = snapshot else { return }
for document in snapshot.documents {
let data = document.data()
let ageRestriction = data["AgeRestriction"] as? String ?? "Age E"
let category = data["Category"] as? String ?? "Error - No Category"
let date = data["Date"] as? Date ?? Date()
let documentId = data["DocumentID"] as? String ?? "Error - No Document-ID"
let description = data["Description"] as? String ?? "Error - No Description"
let location = data["Location"] as? String ?? "Error - No Location"
let title = data["Title"] as? String ?? "Error - No Title"
let userId = data["UserID"] as? String ?? "Error - No User-ID"
let username = data["Username"] as? String ?? "Anonymous"
let color = data["Color"] as? String ?? "Sale"
let newPost = Post(documentIDText: documentId, usernameText: username, titleText: title, locationText: location, dateText: date, descriptionText: description, ageText: ageRestriction, category: category, uid: userId, color: color)
self.posts.append(newPost)
}
if self.posts.isEmpty {self.goFollowPeopleImage.isHidden = false}
}
}
}
有两种基本模式:
在处理 RESTful 网络请求时,我们为所有网络例程提供一个完成处理程序闭包,我们在网络请求完成时调用它。这样,调用者就可以调用前一步的完成处理程序中的每个后续步骤。
这个主题有很多变体(异步 Operation
子类,futures/promises,等等),但想法是一样的,即将一系列异步任务以这样的方式链接在一起调用者可以知道请求何时完成并可以触发 UI 更新。
另一方面,在处理 Firestore 时,我们可以添加 observers/listeners 来更新我们的 UI 随着更新的到来。addSnapshotListener
闭包重复当底层数据库更新时调用。在这种情况下,没有“好的,我们完成了,更新 UI”的时间点(因此我们通常不会使用完成处理程序方法),而是我们只是不断更新 UI 随着文档的到来。
但是当您的示例使用 addSnapshotListener
时,它也使用了 limit(to:)
,这增加了麻烦。这有点像第一种情况(例如,如果您限制为 8,并且您检索了 8,则不会再次调用侦听器)。但它也有点像第二种情况(例如,如果限制为 8 而你目前只有 7 个 posts,它将检索前七个并调用该闭包;但如果有另一条记录进来,它将调用再次关闭,这次也是第 8th 个文档。
尝试同时处理 limited/paginated 响应和监听实时更新可能会变得复杂。我可能会建议,如果你想让 Firestore 像 RESTful 服务一样工作,我可能会建议使用 getDocuments
而不是 addSnapshotListener
,从而消除这种复杂性。然后您可以使用其他人推荐的完成处理程序方法。它使它的行为有点像 RESTful 方法(但是,话又说回来,你失去了实时更新功能)。
如果您想知道实时的第二种情况是什么样子,这里有一个简化的示例(我的 post 只有“文本”和“日期”属性,但希望它能说明过程):
func addPostsListener() {
db.collection("posts").addSnapshotListener { [weak self] snapshot, error in
guard let self = self else { return }
guard let snapshot = snapshot, error == nil else {
print(error ?? "Unknown error")
return
}
for diff in snapshot.documentChanges {
let document = diff.document
switch diff.type {
case .added: self.add(document)
case .modified: self.modify(document)
case .removed: self.remove(document)
}
}
}
}
func add(_ document: QueryDocumentSnapshot) {
guard let post = post(for: document) else { return }
let indexPath = IndexPath(item: self.posts.count, section: 0)
posts.append(post)
tableView.insertRows(at: [indexPath], with: .automatic)
}
func modify(_ document: QueryDocumentSnapshot) {
guard let row = row(for: document) else { return }
guard let post = post(for: document) else { return }
posts[row] = post
tableView.reloadRows(at: [IndexPath(row: row, section: 0)], with: .automatic)
}
func remove(_ document: QueryDocumentSnapshot) {
guard let row = row(for: document) else { return }
posts.remove(at: row)
tableView.deleteRows(at: [IndexPath(row: row, section: 0)], with: .automatic)
}
func row(for document: QueryDocumentSnapshot) -> Int? {
posts.firstIndex {
[=10=].id == document.documentID
}
}
func post(for document: QueryDocumentSnapshot) -> Post? {
let data = document.data()
guard
let text = data["text"] as? String,
let timestamp = data["date"] as? Timestamp
else {
return nil
}
return Post(id: document.documentID, text: text, date: timestamp.dateValue())
}
但这种方法有效,因为我没有限制回复。如果您确实使用 limit(to:)
或 limit(toLast:)
,那么当您达到该限制时,您将停止获取实时更新。
我希望 grabAllFollowingPosts()
到 运行 只有在 loadFollowing()
完成 运行ning 之后。这些都是网络调用,所以我想在后台 运行 它们。关于为什么我的代码不起作用的任何想法?
DispatchQueue.global(qos: .userInteractive).sync {
self.loadFollowing()
self.grabAllFollowingPosts()
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
这3个函数的作用是:
- 抓取当前用户关注的每个用户
- 对于每个用户,获取他们的帖子
因此,loadUsers()
必须在 grabAllFollowingPosts()
var followingUsers = [String]()
//Function 1: load the poeple you are following into the followingUsers array
func loadFollowing () {
guard let userID = Auth.auth().currentUser?.uid else { return }
let firestoreRef = Firestore.firestore().collection("Following").document(userID).collection("UserFollowing")
firestoreRef.addSnapshotListener { (snapshot, error) in
if error != nil {
//error retrieving documents
print (error!.localizedDescription)
} else {
// document retrival successful
guard let snapshot = snapshot else { return }
for document in snapshot.documents {
let data = document.data()
let userid = data["UserID"] as? String ?? "anonymous"
self.followingUsers.append(userid)
}
}
}
}
//Function 2: for all of the users in the followingUsers array - grab their documents from Firestore
func grabAllFollowingPosts () {
for users in followingUsers {
loadPosts(theUsers: users)
}
}
//Function 3: loads the posts
func loadPosts (theUsers: String) {
let firestoreRef = Firestore.firestore().collection("Posts").whereField("UserID", isEqualTo: theUsers).whereField("Date", isGreaterThanOrEqualTo: Date()).limit(to: 8)
//TODO: add infinate scroll
firestoreRef.addSnapshotListener { (snapshot, error) in
if error != nil {
//error retrieving documents
print (error!.localizedDescription)
} else {
// document retrival successful
guard let snapshot = snapshot else { return }
for document in snapshot.documents {
let data = document.data()
let ageRestriction = data["AgeRestriction"] as? String ?? "Age E"
let category = data["Category"] as? String ?? "Error - No Category"
let date = data["Date"] as? Date ?? Date()
let documentId = data["DocumentID"] as? String ?? "Error - No Document-ID"
let description = data["Description"] as? String ?? "Error - No Description"
let location = data["Location"] as? String ?? "Error - No Location"
let title = data["Title"] as? String ?? "Error - No Title"
let userId = data["UserID"] as? String ?? "Error - No User-ID"
let username = data["Username"] as? String ?? "Anonymous"
let color = data["Color"] as? String ?? "Sale"
let newPost = Post(documentIDText: documentId, usernameText: username, titleText: title, locationText: location, dateText: date, descriptionText: description, ageText: ageRestriction, category: category, uid: userId, color: color)
self.posts.append(newPost)
}
if self.posts.isEmpty {self.goFollowPeopleImage.isHidden = false}
}
}
}
有两种基本模式:
在处理 RESTful 网络请求时,我们为所有网络例程提供一个完成处理程序闭包,我们在网络请求完成时调用它。这样,调用者就可以调用前一步的完成处理程序中的每个后续步骤。
这个主题有很多变体(异步
Operation
子类,futures/promises,等等),但想法是一样的,即将一系列异步任务以这样的方式链接在一起调用者可以知道请求何时完成并可以触发 UI 更新。另一方面,在处理 Firestore 时,我们可以添加 observers/listeners 来更新我们的 UI 随着更新的到来。
addSnapshotListener
闭包重复当底层数据库更新时调用。在这种情况下,没有“好的,我们完成了,更新 UI”的时间点(因此我们通常不会使用完成处理程序方法),而是我们只是不断更新 UI 随着文档的到来。
但是当您的示例使用 addSnapshotListener
时,它也使用了 limit(to:)
,这增加了麻烦。这有点像第一种情况(例如,如果您限制为 8,并且您检索了 8,则不会再次调用侦听器)。但它也有点像第二种情况(例如,如果限制为 8 而你目前只有 7 个 posts,它将检索前七个并调用该闭包;但如果有另一条记录进来,它将调用再次关闭,这次也是第 8th 个文档。
尝试同时处理 limited/paginated 响应和监听实时更新可能会变得复杂。我可能会建议,如果你想让 Firestore 像 RESTful 服务一样工作,我可能会建议使用 getDocuments
而不是 addSnapshotListener
,从而消除这种复杂性。然后您可以使用其他人推荐的完成处理程序方法。它使它的行为有点像 RESTful 方法(但是,话又说回来,你失去了实时更新功能)。
如果您想知道实时的第二种情况是什么样子,这里有一个简化的示例(我的 post 只有“文本”和“日期”属性,但希望它能说明过程):
func addPostsListener() {
db.collection("posts").addSnapshotListener { [weak self] snapshot, error in
guard let self = self else { return }
guard let snapshot = snapshot, error == nil else {
print(error ?? "Unknown error")
return
}
for diff in snapshot.documentChanges {
let document = diff.document
switch diff.type {
case .added: self.add(document)
case .modified: self.modify(document)
case .removed: self.remove(document)
}
}
}
}
func add(_ document: QueryDocumentSnapshot) {
guard let post = post(for: document) else { return }
let indexPath = IndexPath(item: self.posts.count, section: 0)
posts.append(post)
tableView.insertRows(at: [indexPath], with: .automatic)
}
func modify(_ document: QueryDocumentSnapshot) {
guard let row = row(for: document) else { return }
guard let post = post(for: document) else { return }
posts[row] = post
tableView.reloadRows(at: [IndexPath(row: row, section: 0)], with: .automatic)
}
func remove(_ document: QueryDocumentSnapshot) {
guard let row = row(for: document) else { return }
posts.remove(at: row)
tableView.deleteRows(at: [IndexPath(row: row, section: 0)], with: .automatic)
}
func row(for document: QueryDocumentSnapshot) -> Int? {
posts.firstIndex {
[=10=].id == document.documentID
}
}
func post(for document: QueryDocumentSnapshot) -> Post? {
let data = document.data()
guard
let text = data["text"] as? String,
let timestamp = data["date"] as? Timestamp
else {
return nil
}
return Post(id: document.documentID, text: text, date: timestamp.dateValue())
}
但这种方法有效,因为我没有限制回复。如果您确实使用 limit(to:)
或 limit(toLast:)
,那么当您达到该限制时,您将停止获取实时更新。