我想 grabAllFollowingPosts()
后才运行 loadFollowing()
已完成运行。这些都是网络调用,所以我想在后台运行它们。有什么办法可以解决为什么我的代码不工作?
DispatchQueue.global(qos: .userInteractive).sync {
self.loadFollowing()
self.grabAllFollowingPosts()
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
这三个函数的作用是
因此: 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
子类、futurespromises等),但想法是一样的,即把一系列异步任务链在一起,这样调用者可以知道什么时候请求都完成了,并可以触发UI更新。
另一方面,在处理Firestore时,我们可以添加observerslisteners来在更新到来时更新我们的UI。这个 addSnapshotListener
闭包被反复调用,因为底层数据库被更新。在这种情况下,并没有一个 "好了,我们完成了,更新UI "的时间点(所以我们一般不会使用完成处理程序的方法),而只是在文档进来的时候不断地更新UI。
但是,虽然你的例子使用的是 addSnapshotListener
它还在使用 limit(to:)
,这就增加了一个皱纹。它有点像第一种情况(比如,如果限制到8个,你检索了8个,监听器就不会再被调用)。但它也有点像第二种情况(例如,如果限制为8条,而你目前只有7条帖子,它会检索前7条,并调用该关闭;但如果又有一条记录进来,它会再次调用该关闭,这次是8条钍 文档)。)
试图处理有限的分页响应和监听实时更新可能会变得复杂。我建议,如果你想让Firestore像一个RESTful服务一样,我建议使用 getDocuments
而不是 addSnapshotListener
,消除了这种复杂性。然后你可以使用别人推荐的完成处理程序方法。这使得它的行为有点像RESTful方法(但是,你又失去了实时更新功能)。
如果你想知道实时的、第二种方案可能是什么样子的,这里是一个简化的例子(我的帖子只有 "文本 "和 "日期 "属性,但希望它能说明这个过程)。
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 {
$0.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:)
,那么当你达到这个极限时,你就会停止获得实时更新。