Firebase数据加载比UITableView慢

时间:2019-01-05 19:09:33

标签: swift firebase uitableview

我能够从Firebase数据库成功检索数据,但是它的加载速度不够快,无法被UITableView使用的某些功能识别。

在TableView上调用numberOfRowsInSection时,出现一个错误,提示“在展开一个可选值时意外发现nil”,因为我正在返回用Firebase数据填充的数组中的项目数;到该函数被调用时,数组中还没有数据。

如果我在其他任何地方打印数组中的数据,则会显示出来。

我已经尝试在viewDidAppear()和viewWillAppear()中放置用于从Firebase检索数据的代码。

//
//  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 :(得分:1)

这里有几个包含良好信息的答案。问题的真正根源是:

var postData : [Any]!

因此,在数组有数据之前访问postData数组时,它为nil并且崩溃,因为您正试图从nil获取.count属性-这是行不通的。

return self.postData.count

这样做

var postData = [Post]()

这将以0计数实例化postData数组。

还要注意,从那时起,当调用func tableView(_ tableView:UITableView,numberOfRowsInSection时,它将返回0,因为存在的数组(不为nil)具有零个元素

只要有问题,您就可以随意使用viewDidLoad或viewDidAppear来填充tableView,只要填充了dataSource,就可以调用tableView.reloadData,如其他答案中所述。

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

请注意,在您的问题中,相同的数据实际上存储在多个类var中,这可能是不必要的,因此您可以删除currentPost和postDictionary。另外,请谨慎使用Dictionary;请记住,它们是无序的,因此,如果您阅读一些帖子并将其放在“词典”中,它们将失去顺序。

我综合考虑了这一点,以使您朝正确的方向前进:

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)
        }
    })

答案 1 :(得分:0)

由于网络请求是异步完成的,因此,在呈现表视图时,绝对不要假设您拥有数据。实际上,您的表视图将比大多数网络请求加载速度更快(因此崩溃)。

我的建议是,您应始终避免强行解包变量(即使用“!”)。实际上,由于您是iOS的新手,所以我会避免使用“!”就像是黑死病。

将变量更改为此:

version: '2'
services:
  invoiceninja:
    image: invoiceninja/invoiceninja:latest
    dns:
      - 1.1.1.1
      - 1.0.0.1
    labels:
      io.rancher.container.pull_image: always
      {{- if (.Values.HOST_LABEL)}}
      io.rancher.scheduler.affinity:host_label: ${HOST_LABEL}
      {{- end}}
    links:
      - mysql
    restart: on-failure
    volumes:
      - /etc/localtime:/etc/localtime:ro # Syncronize time of container with the host system
      - /etc/timezone:/etc/timezone:ro # Syncronize timezone of container with the host system
      - /RancherCattle/${DATA_DIR}/Configuration/.env:/var/www/app/.env
      - /RancherCattle/${DATA_DIR}/Logo:/var/www/app/public/logo
      - /RancherCattle/${DATA_DIR}/Storage:/var/www/app/storage
  cron:
    image: invoiceninja/invoiceninja:latest
    dns:
      - 1.1.1.1
      - 1.0.0.1
    entrypoint: |
      bash -c 'bash -s <<EOF
      trap "break;exit" SIGHUP SIGINT SIGTERM
      sleep 300s
      while /bin/true; do
        ./artisan ninja:send-invoices
        ./artisan ninja:send-reminders
        sleep 1d
      done
      EOF'
    labels:
      io.rancher.container.pull_image: always
      {{- if .Values.HOST_LABEL}}
      io.rancher.scheduler.affinity:host_label: ${HOST_LABEL}
      {{- end}}
    links:
      - mysql
    restart: on-failure
    volumes_from:
      - invoiceninja
  mysql:
    image: mysql:5
    dns:
      - 1.1.1.1
      - 1.0.0.1
    environment:
      MYSQL_DATABASE: ninja_db
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASS}
      MYSQL_USER: ninja_user
      MYSQL_PASSWORD: ${DB_USER_PASS}
    labels:
      io.rancher.container.pull_image: always
      {{- if .Values.HOST_LABEL}}
      io.rancher.scheduler.affinity:host_label: ${HOST_LABEL}
      {{- end}}
      traefik.enable: false
    networks:
      - db-admin
    restart: on-failure
    volumes:
      - /etc/localtime:/etc/localtime:ro # Syncronize time of container with the host system
      - /etc/timezone:/etc/timezone:ro # Syncronize timezone of container with the host system
      - /RancherCattle/${DATA_DIR}/Database:/var/lib/mysql
  nginx:
    image: nginx
    dns:
      - 1.1.1.1
      - 1.0.0.1
    labels:
      io.rancher.container.pull_image: always
      {{- if .Values.HOST_LABEL}}
      io.rancher.scheduler.affinity:host_label: ${HOST_LABEL}
      {{- end}}
      {{- if .Values.TRAEFIK_HOST}}
      traefik.enable: true
      traefik.frontend.rule: Host:${TRAEFIK_HOST}
      traefik.frontend.entryPoints: http,https
      traefik.frontend.headers.forceSTSHeader: true
      traefik.frontend.headers.SSLRedirect: true
      traefik.frontend.headers.STSPreload: true
      traefik.frontend.headers.STSSeconds: 15552000
      traefik.port: "80"
      {{- else}}
      traefik.enable: false
      {{- end}}
      io.rancher.sidekicks: invoiceninja,cron
    links:
      - invoiceninja
    networks:
      - public-proxy
    ports:
      - "${WEB_PORT}:80"
    restart: on-failure
    volumes:
      - /RancherCattle/${DATA_DIR}/Configuration/nginx.conf:/etc/nginx/nginx.conf:ro
    volumes_from:
      - invoiceninja

networks:
  db-admin:
    external: true
  public-proxy:
    external: true

这样做将迫使您安全地解包变量。 (即检查是否为零)

然后确保在获得结果时重新加载表格视图。

您应该做的更多事情

我将更进一步:我将避免像瘟疫一样使用user www-data; events { worker_connections 768; } http { upstream backend { server invoiceninja:9000; } include /etc/nginx/mime.types; default_type application/octet-stream; gzip on; gzip_disable "msie6"; server { listen 80 default; server_name _; root /var/www/app/public; index index.php; charset utf-8; location / { try_files $uri $uri/ /index.php?$query_string; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } sendfile off; location ~ \.php$ { fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass backend; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_intercept_errors off; fastcgi_buffer_size 16k; fastcgi_buffers 4 16k; fastcgi_param HTTPS 1; } location ~ /\.ht { deny all; } } } var postDictionary : NSDictionary? var currentPost: String? var postData: [Any] = []

您当然可以继续使用AnyNSDictionary变量,但是您将不得不无休止地继续转换它们。最终,如果您继续这样做,您甚至都不知道代码中正在发生什么。

将您的信息更改为此:

NSDictionary

并使用以下方法之一将原始字典转换为Post对象: 1. Codable 2. ObjectMapper 3. MapCodableKit(这是我个人更喜欢的框架)

然后执行此操作(使用Any示例):

var posts: [Post] = []