我能够从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。
答案 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] = []
您当然可以继续使用Any
和NSDictionary
变量,但是您将不得不无休止地继续转换它们。最终,如果您继续这样做,您甚至都不知道代码中正在发生什么。
将您的信息更改为此:
NSDictionary
并使用以下方法之一将原始字典转换为Post对象: 1. Codable 2. ObjectMapper 3. MapCodableKit(这是我个人更喜欢的框架)
然后执行此操作(使用Any
示例):
var posts: [Post] = []