Данные Firebase загружаются медленнее, чем UITableView - PullRequest
0 голосов
/ 05 января 2019

Я могу успешно извлекать данные из моей базы данных Firebase, однако она загружается недостаточно быстро, чтобы распознаваться некоторыми функциями, используемыми UITableView.

При вызове TableView numberOfRowsInSection я получаю сообщение об ошибке «Неожиданно найдено nil при развертывании необязательного значения», потому что я возвращаю количество элементов в массиве, заполненном данными Firebase; к моменту вызова этой функции в массиве еще нет данных.

Если я распечатываю данные в массиве где-либо еще, он показывает.

Я попытался поместить код для извлечения данных из Firebase в viewDidAppear (), а также viewWillAppear ().

//
//  PostsViewController.swift
//  cheerup-ios
//
//  Created by Diamonique Danner on 12/26/18.
//  Copyright © 2018 Danner Opp., LLC. All rights reserved.
//

import UIKit
import Firebase

var postDictionary : NSDictionary!
var currentPost : String!
var postData : [Any]!

@IBOutlet var postsTable: UITableView!

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    let dbRef = Database.database().reference()
    dbRef.observeSingleEvent(of: .value, with: {
        (snapshot) in
        let value = snapshot.value as? NSDictionary
        let posts = value!["posts"] as? NSDictionary
        self.postDictionary = posts!

        for post in posts! {
            self.postData.append(post)
        }
    }) { (error) in
        print(error.localizedDescription)
    }

   // print(self.postData)
}

override func viewDidLoad() {
    super.viewDidLoad()
    postsTable.dataSource = self
    postsTable.delegate = self  
}

@IBAction func logOut(_ sender: Any) {
    let firebase = Auth.auth()
    do {
        try firebase.signOut()
    } catch let signOutError as NSError {
        print(signOutError)
    }
    let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
    let loginViewController = storyboard.instantiateViewController(withIdentifier: "LoginViewController")
    self.present(loginViewController, animated: true, completion: nil)
}

func numberOfSections(in tableView: UITableView) -> Int {
 return 1
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

print(self.postData)
    return self.postData.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

let cell = self.postsTable.dequeueReusableCell(withIdentifier: 
"cell") as! CustomTableViewCell
//  cell.postText.text = String(indexPath.row)
let data = postData[indexPath.row]

cell.postText.text = data as? String
return cell
}

/*
// MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    // Get the new view controller using segue.destination.
    // Pass the selected object to the new view controller.
}
*/

}

Я бы хотел, чтобы данные Firebase загружались вовремя, чтобы массив был заполнен указанными данными, которые затем могут быть использованы для заполнения моего UITableView.

Ответы [ 2 ]

0 голосов
/ 06 января 2019

Здесь есть несколько ответов, которые содержат полезную информацию. Настоящий корень проблемы заключается в следующем:

var postData : [Any]!

Таким образом, когда доступ к массиву postData осуществляется до того, как в массиве есть данные, он становится равным nil и вылетает, потому что вы пытаетесь получить свойство .count из nil - это не работает.

return self.postData.count

Сделай это

var postData = [Post]()

Это создаст экземпляр массива postData со счетчиком 0.

Также обратите внимание, что с тех пор, когда вызывается func tableView (_ tableView: UITableView, numberOfRowsInSection, он вернет 0, потому что массив существует (не ноль) с нулевыми элементами

Вы можете свободно использовать viewDidLoad или viewDidAppear, как и в вашем вопросе, для заполнения вашего tableView до тех пор, пока ваш источник данных заполнен, вы вызываете tableView.reloadData, как отмечено в других ответах.

    for post in posts! {
        self.postData.append(post)
    }
    self.tableView.reloadData()

Обратите внимание, что в вашем вопросе одни и те же данные по существу хранятся в нескольких классовых переменных, что, вероятно, не нужно, поэтому вы можете удалить currentPost и postDictionary. Также будьте осторожны при работе со словарями; помните, что они неупорядочены, поэтому, если вы прочитаете некоторые посты и поместите их в словарь, они потеряют свой порядок.

Я собрал это вместе, чтобы вы пошли в правильном направлении:

class PostClass {
    var postKey = ""
    var post = ""
    init(withKey: String, andPost: String) {
        self.postKey = withKey
        self.post = andPost
    }
}

var postsArray = [PostClass]()

override func viewDidAppear(_ animated: Bool) {
   super.viewDidAppear(animated)

    let postsRef = self.ref.child("posts")
    postsRef.observeSingleEvent(of: .value, with: { snapshot in
        let allPosts = snapshot.children.allObjects as! [DataSnapshot]
        for postSnap in allPosts {
            let postKey = postSnap.key
            let post = postSnap.childSnapshot(forPath: "post").value as! String
            let aPost = PostClass(withKey: postKey, andPost: post)
            self.postsArray.append(aPost)
        }
        self.myTableView.reloadData()
        // a test to print the post objects
        for post in self.postsArray {
           let key = post.postKey
           let postText = post.post
           print(key, postText)
        }
    })
0 голосов
/ 06 января 2019

По той причине, что сетевые запросы выполняются асинхронно, вы НИКОГДА не должны предполагать, что у вас есть данные при представлении табличного представления. Фактически, ваше табличное представление будет загружаться намного быстрее, чем большинство сетевых запросов (отсюда и сбои).

Я предлагаю вам всегда избегать принудительного развертывания переменных (т. Е. Использовать "!"). На самом деле, поскольку вы относительно новичок в iOS, я бы не стал использовать "!" как будто это черная чума.

измените ваши переменные на:

var postDictionary : NSDictionary?
var currentPost: String?
var postData: [Any] = []

Выполнение этого заставит вас безопасно развернуть ваши переменные. (то есть проверьте, не равен ли он нулю)

Затем обязательно перезагрузите представление таблицы, когда получите результаты.

Больше вещей, которые вы должны сделать

Я бы сделал еще один шаг: я бы не использовал Any и NSDictionary, как и чуму

Конечно, вы можете продолжать использовать NSDictionary и Any переменные, но вам придется бесконечно продолжать их разыгрывать. И, в конце концов, вы даже не будете знать, что происходит в вашем коде, если будете продолжать это делать.

изменить ваши сообщения на это:

var posts: [Post] = []

И используйте один из следующих способов для преобразования ваших сырых словарей в объекты Post: 1. Кодируемый 2. ObjectMapper 3. MapCodableKit (это мой фреймворк, который я лично предпочитаю)

А затем сделайте это (на примере MapCodableKit):

dbRef.observeSingleEvent(of: .value, with: { (snapshot) in
  guard let value = snapshot.value as? [String: Any?] else { return }
  guard let json = value["posts"] as? [String: Any?] else { return }
  self.posts = Posts(json: json)
  self.tableView.reloadData()
})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...