Swift Firebase loading records with long delay

匿名 (未验证) 提交于 2019-12-03 01:26:01

问题:

I'm building a Swift app using Firebase, and I'm new to both so be gentle. Currently, when I open the app, it syncs the entire database again and causes a 2 or 3 second lag where the user stares at an empty tableview. How can I speed this up?

Any thoughts?

My code:

My loadContacts function

func loadContact(snap : FIRDataSnapshot) -> Contact { let key = snap.key let contact = (snap.value) as? NSDictionary  let c1 = Contact(     id: (contact?["id"] as? String)!,     firebasekey: key,     first_name: (contact?["First Name"] as? String)!,     middle_name: (contact?["Middle Name"] as? String)!,     last_name: (contact?["Last Name"] as? String)!,     suffix: (contact?["Suffix"] as? String)!,     company: (contact?["Company"] as? String)!,     phone_labe1: (contact?["Phone Label 1"] as? String)!,     phone1: (contact?["Phone 1"] as? String)!,     phone_label2: (contact?["Phone Label 2"] as? String)!,     phone2: (contact?["Phone 2"] as? String)!,     email_label1: (contact?["Email Label 1"] as? String)!,     email1: (contact?["Email 1"] as? String)!,     email_label2: (contact?["Email Label 2"] as? String)!,     email2: (contact?["Email 2"] as?  String)!,     social: (contact?["Social Security Number"] as? String)!,     dob: (contact?["Date of Birth"] as? String)!,     street: (contact?["Street"] as? String)!,     city: (contact?["City"] as? String)!,     zip: (contact?["ZIP and Postal Code"] as? String)!,     state: (contact?["State and Province"] as? String)!,     reg_number: (contact?["Reg Num"] as? String)!,     stable_reg_number: (contact?["Stable Reg Num"] as? String)!,     emergency_contact: (contact?["Emergency Contact"] as? String)!,     emergency_phone: (contact?["Emergency Phone"] as? String)!,     drivers_license: (contact?["Driver's License Num"] as? String)!,     insurance_carrier: (contact?["Insurance Carrier"] as? String)!,     details: (contact?["Details"] as? String)!,     insurance_exp: (contact?["Insurance Expiration Date"] as? String)!,     insurance_group: (contact?["Insurance Group Num"] as? String)!,     insurance_member: (contact?["Insurnace Member Num"] as? String)!, // spelled wrong in database     job_title: (contact?["Job Title"] as? String)!,     date_modified: (contact?["Modified"] as? String)!,     keywords: [],     notes: [] )  return c1; } 

And in my contact table view

import UIKit import Firebase  class ContactTableViewController: UITableViewController, UISearchBarDelegate, UISearchDisplayDelegate { // MARK: Properties var contactSearchResults : [Contact] = []  // FIRDatabase.database().persistenceEnabled = true let contactRef = FIRDatabase.database().reference().child("contacts")  override func viewDidLoad() {      contactRef.queryOrdered(byChild: "Last Name").observe(.childAdded) { (snap: FIRDataSnapshot) in         contacts.append(loadContact(snap: snap))         self.tableView.reloadData()     }      contactRef.queryOrdered(byChild: "Last Name").observe(.childChanged) { (snap: FIRDataSnapshot) in         // this code here is wrong, but it doesn't matter for demonstration purposes         contacts.append(loadContact(snap: snap))         self.tableView.reloadData()     }      // Uncomment the following line to preserve selection between presentations     // self.clearsSelectionOnViewWillAppear = false      // Uncomment the following line to display an Edit button in the navigation bar for this view controller.     // self.navigationItem.rightBarButtonItem = self.editButtonItem() } 

My database has structured like

Contacts (my problem area) has about 4000 records in it with 33 individual children values each.

回答1:

There are number of issues with the code in the question that will affect performance.

1) The child added event fires for every child initially and then for any children added afterwards. If you have 1000 contacts, that means on startup, you are refreshing the table view 1000 times. You would be better off loading all of the contacts by .value and iterating over them, adding to an array, and then refreshing the tableView when done

2) Going along with #1, if you just want to order them by last name, observe the node by .value, iterate over the snapshot to populate the array and then sort, then reload the tableView. That will be significantly faster.

3) The childChanged event: there's no reason to query by last name as when a child changes, it notifies you of just that child and again, you can sort in code if needed

4) Firebase queries are very 'heavy' by comparison to observe events. In this case you really are not querying for any in particular so that should be eliminated. Just use observe events to load a node's worth of data and use a query when you are looking for a subset of that data.

*note that a lot of this depends on how many contacts you have. For a few thousand these suggestions work fine.

So there's a really cool design pattern that makes populating an initial dataset really clean. I think one of the Firebasers wrote it as part of an example app.

We start with defining an class level variable called initialLoad and set it to true. Then we use a childAdded observe to load in all the contacts and populate our tableView dataSource array.

var initialLoad = true  contactsRef.observeEventType(.ChildAdded, withBlock: { snapshot in      self.handleChildAdded(withSnap: snapshot) }) 

and the function to handle childAdded events and will initially load each child one at a time, and watch for children added after that.

func handleChildAdded(withSnap: snapshot: FDataSnapshot! ) {     let myContact = Contact()     myContact.initWithSnap(snapshot)     myDataSourceArray.append(myContact)      //upon first load, don't reload the tableView until all children are loaded     if ( self.initialLoad == false ) {              self.contactsTableView.reloadData()     }     } 

Now the tricky bit

//this .Value event will fire AFTER the child added events to reload the tableView  //  the first time and to set subsequent childAdded events to load after each child is //    added in the future contactsRef.observeSingleEventOfType(.Value, withBlock: { snapshot in            print("inital data loaded so reload tableView!")      self.itemsTableView.reloadData()      self.initialLoad = false }) 

The the 10k foot view:

The key here is that .value events fire AFTER .childAdded events so we are leveraging that to set the initialLoad variable to false AFTER all of the child added events complete.

The above code is Swift 2/3, Firebase 2 but it gives you the concept of how to do your initial load.



易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!