Я хочу, чтобы мой код выполнялся последовательно / синхронно в фоновом режиме (DispatchQueue) - PullRequest
0 голосов
/ 03 мая 2020

Я хочу, чтобы grabAllFollowingPosts() запускался только после завершения loadFollowing(). Это оба сетевых вызова, поэтому я хочу запускать их в фоновом режиме. Любые идеи о том, почему мой код не работает?

DispatchQueue.global(qos: .userInteractive).sync {
    self.loadFollowing()
    self.grabAllFollowingPosts()
    DispatchQueue.main.async {
        self.tableView.reloadData()
    }
}

Что эти 3 функции делают:

  1. захватить каждого пользователя, за которым текущий пользователь следит
  2. для каждого из этих пользователей, захватите их сообщения

Следовательно, 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}

        }
    }
}

1 Ответ

0 голосов
/ 03 мая 2020

Существует два базовых c шаблона:

  1. При работе с сетевыми запросами RESTful мы даем всем нашим сетевым подпрограммам закрытие обработчика завершения, которое мы вызываем при запросе сети сделано. Таким образом, вызывающая сторона может вызывать каждый последующий шаг в обработчике завершения предыдущего шага.

    Существует много вариантов этой темы (асинхронные Operation подклассы, фьючерсы / обещания и т. Д. c), но Идея та же, а именно - объединение серии асинхронных задач таким образом, чтобы вызывающий мог знать, когда все запросы были выполнены, и запускать обновление пользовательского интерфейса.

  2. На с другой стороны, при работе с Firestore мы можем добавлять наблюдателей / слушателей для обновления нашего пользовательского интерфейса по мере поступления обновлений. Закрытие addSnapshotListener неоднократно вызывается при обновлении базовой базы данных. В этом сценарии нет момента «хорошо, мы закончили, обновите пользовательский интерфейс» (поэтому мы обычно не будем использовать подход обработчика завершения), но мы просто будем постоянно обновлять пользовательский интерфейс по мере поступления документов .

Но, хотя ваш пример использует addSnapshotListener, он также использует limit(to:), который добавляет складку. Это немного похоже на первый сценарий (например, если вы ограничены 8, и вы получили 8, слушатель больше не будет вызываться). Но это также немного похоже на второй сценарий (например, если ограничение до 8, и в настоящее время у вас есть только 7 сообщений, он извлечет первые семь и вызовет это закрытие; но если появится другая запись, он снова вызовет закрытие, это время с документом 8 th ).

Попытка обрабатывать как ограниченные / разбитые на страницы ответы, так и прослушивание обновлений в реальном времени может быть затруднено. Я мог бы предложить, чтобы, если вы хотите, чтобы 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:), вы перестанете получать обновления в реальном времени, когда достигнете этого предела.

...