Firebase dispatch_async keeps crashing -swift 2 iOS9 - ios

I'm using a tabBar Controller. TabOne is the rootVC. In TabOne I have a viewController that is sending info to Firebase via a sendButton and programmatically presenting TabTwo.
In TabTwo I have tableView controller that gets the info and displays it. Oddly twice I ran the code and everything was fine, I pressed the sendButton, TabTwo gets presented, it's tableview correctly loads the cells with the correct info. The problem is now for some strange reason it only successfully ran those 2 times and it has been crashing on dispatch_async ever since.
Now I just tried to run the simulator and instead of pressing the sendButton in TabOne I pressed the tab for TabTwo and the app crashes on dispatch_async. I didn't attempt to send anything to Firebase, I just clicked the tab for TabTwo and dispatch_async crashed with code:
Thread1: EXC_BAD_INSTRUCTION (code=EXC_1338_INVOP, subcode=0x0)
Any idea why it seems to crash when I switch tabs?
Any idea why everything ran fine twice then all of a sudden crash?
Here is the code:
ModelObject
class CookieModel: NSObject{
var name: String?
}
tab1
class TabOneController: UIViewController{
var dbRef: FIRDatabaseReference!
let userID: String? = (FIRAuth.auth()?.currentUser?.uid)!
let cookieModel = [CookieModel]()
override func viewDidLoad() {
super.viewDidLoad()
self.dbRef = FIRDatabase.database().reference()
}
#IBAction func sendButton(sender: UIButton){
self.sendToFireBase()
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let tabBarController = mainStoryboard.instantiateInitialViewController() as! UITabBarController
tabBarController.selectedIndex = 1
self.presentViewController(tabBarController , animated: true, completion: nil)
}
//Code to send data to FirebaseDatabase
func sendToFireBase(){
let uniquePath = NSUUID().UUIDString
let usersIDRef = self.dbRef.child("users").child(self.userID!)
let cookiePath = usersIDRef.child("cookieData").child(self.uniquePath)
let cookie0 = CookieModel()
cookie0.name = "oatmeal"
self.cookieModel.append(cookie0)
let cookie1 = CookieModel()
cookie1.name = "chocolate"
self.cookieModel.append(cookie1)
let cookie2 = CookieModel()
cookie2.name = "coconut"
self.cookieModel.append(cookie2)
var cookieDict = [String:AnyObject]()
cookieDict.updateValue(cookie0.name!, forKey: "cookie0")
cookieDict.updateValue(cookie1.name!, forKey: "cookie1")
cookieDict.updateValue(cookie2.name!, forKey: "cookie2")
cookiePath.updateChildValues(cookieDict, withCompletionBlock: {
(error, user) in
if error != nil{
print((error?.localizedDescription))
return
}
}
}
tab2
class TabTwoController: UIViewController, UITableViewDataSource, UITableViewDelegate{
#IBOutlet weak var tableView: UITableView!
var dbRef: FIRDatabaseReference!
let userID: String? = (FIRAuth.auth()?.currentUser?.uid)!
var cookieModel = [CookieModel]()
override func viewDidLoad() {
super.viewDidLoad()
self.dbRef = FIRDatabase.database().reference()
self.tableView.delegate = self
self.observeFBCookieData()
}
//Code to retrieve data from FirebaseDatabase
func observeFBCookieData(){
let usersIDRef = self.dbRef.child("users").child(self.userID!)
let cookiePath = usersIDRef.child("cookieData")
cookiePath.observeEventType(.ChildAdded, withBlock: {
(snapshot) in
if let dict = snapshot.value as? [String:AnyObject]{
let cookie0 = dict["cookie0"] as? String
let cookie1 = dict["cookie1"] as? String
let cookie2 = dict["cookie2"] as? String
let cookie0 = CookieModel()
cookie0.name = cookie0!
self.cookieModel.append(cookie0)
let cookie1 = CookieModel()
cookie1.name = cookie1!
self.cookieModel.append(cookie1)
let cookie2 = CookieModel()
cookie2.name = cookie2!
self.cookieModel.append(cookie2)
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
}
}, withCancelBlock: nil)
}
//MARK: -TableViewDatasource Methods
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.cookieModel.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! MyCookieCell
cell.titleLabel.text = self.cookieModel[IndexPath.row].name!
return cell
}
}

Try setting the table view's data source in your viewDidLoad after setting its delegate.
Self.tableView.datasource = self

Related

Swift - passing data from VC to detail VC with Firestore

I'm totally new to Swift well I have a View Controller, where a uitableview of data is being fetched from the Firestore and I want to send this data from View Controller to detail View Controller. I mean, when a cell in View Controller is clicked, detail View Controller shows such as name, description from Firestore.. is there anyone to help me?
here's HospitalViewController.Swift :
class HospitalViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
struct HospitalData {
var Name: String = ""
var Image: String = ""
var Region: String = ""
func getDic() -> [String:String] {
let dic = [
"Name": self.Name,
"Image": self.Image,
"Region": self.Region
]
return dic
}
}
var hospitalArray: Array<HospitalData> = []
#IBOutlet weak var hospitalTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.isNavigationBarHidden = true
hospitalTableView.delegate = self
hospitalTableView.dataSource = self
}
#IBAction func onBtnRead(_ sender: UIButton) {
getValueFromList()
}
func getValueFromList() {
hospitalArray.removeAll()
let db = Firestore.firestore()
db.collection("Hospital").getDocuments() {
(querySnapshot, err) in
if let error = err {
print("fail", error)
}else{
print("success")
for document in querySnapshot!.documents {
print("\(document.documentID) => \(document.data())")
let dataDic = document.data() as NSDictionary
let Name = dataDic["Name"] as? String ?? ""
print("Name:", Name)
let Image = dataDic["Image"] as? String ?? ""
print("Image:", Image)
let Region = dataDic["Region"] as? String ?? ""
print("Region:", Region)
var hospital = HospitalData()
hospital.Name = Name
hospital.Image = Image
hospital.Region = Region
self.hospitalArray.append(hospital)
}
self.hospitalTableView.reloadData()
}
}
}
func setValueIntoList() {
var hospital = HospitalData()
hospital.Name = "SUN Hospital"
hospital.Image = "hospital.png"
hospital.Region = "Seoul"
let dic = hospital.getDic()
let db = Firestore.firestore()
var ref: DocumentReference? = nil
ref = db.collection("Hospital").addDocument(data: dic) {
err in
if let error = err {
print("fail", error)
}else{
print("success", ref!.documentID)
}
}
}
// mark: datasource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.hospitalArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = hospitalTableView.dequeueReusableCell(withIdentifier: "hosptialTableViewCell", for: indexPath) as! HospitalTableViewCell
let hospitalStruct = self.hospitalArray[indexPath.row]
cell.labelName.text = hospitalStruct.Name
cell.labelRegion.text = hospitalStruct.Region
cell.hospitalImageView.image = UIImage(named: "hospital.png")
return cell
}
To navigate from one viewcontroller to others you can use the basic push navigation as bellow
let viewController = UIStoryboard.init(name: "YOUR_STORYBOARD", bundle: Bundle.main).instantiateViewController(withIdentifier: "YOUR_VC_IDENTIFIER") as? YOUR_VC
self.navigationController?.pushViewController(viewController, animated: true)
Now to pass data from one view to the viewcontroller you would like to move you have to declare a variable on the viewcontroller you would like to move.
i.e
You need to have a variable in your detail vc.
hospital_detail_vc.swift
var hospitalData: HospitalData?
Now you need to pass the data/variable as below through the navigation in your TableView's method didSelectRowAt as below.
let viewController = UIStoryboard.init(name: "YOUR_STORYBOARD", bundle: Bundle.main).instantiateViewController(withIdentifier: "YOUR_VC_IDENTIFIER") as? YOUR_VC
viewController.hospitalData = self.hospitalArray[indexPath.row]
self.navigationController?.pushViewController(viewController, animated: true)
Happy coding :)

How to fix a UITableView class which repeat twice elements read on Firebase database and in the load the table is empty?

I'm trying to read from firebase realtime database a series of instructions.
I have an unknown number of information on my Vehicles node, so I just use nextObject method to get the number of times that the function need to iterate.
The problem is that at the opening, my table is empty. When I click on the searchbar then my cells contents did appear. How can I solve these problem?
Here my UITableView file:
import UIKit
import FirebaseDatabase
import Alamofire
class Vehicles: UITableViewController,
UISearchResultsUpdating, UISearchBarDelegate {
//variables
var model: NSMutableArray = []
var numberOfVehicles: NSMutableArray = []
var price: NSMutableArray = []
var imagePathString: NSMutableArray = []
var detailpage: NSMutableArray = []
var populator: NSMutableArray = []
var searching = false
var matches = [Int]()
let searchController = UISearchController(searchResultsController: nil)
#IBOutlet weak var InfoTableView: UITableView!
var InfoList: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
loadData()
//this should reload but, it didn't.
self.InfoTableView.reloadData()
//then the searchbar that is good and don't have any problem.
searchingField()
}
func loadData() {
//read data from database
let rootRef = Database.database().reference()
let conditionalRef = rootRef.child("Vehicles")
conditionalRef.observe(.value) {(snap: DataSnapshot) in
// Get all the children from snapshot you got back from Firebase
let snapshotChildren = snap.children
// Loop over all children in Firebase
while let child = snapshotChildren.nextObject() as? DataSnapshot {
// Get code node key and save it to they array
self.populator.add(child.key)
if self.populator.contains("\(child.key)") {
let userRef = rootRef.child("Vehicles").child("\(child.key)")
userRef.observeSingleEvent(of: .value, with: { snapshot in
let userDict = snapshot.value as! [String: Any]
let model1 = userDict["Model"] as! String
self.model.add(model1)
let detail1 = userDict["Detail"] as! String
self.detailpage.add(detail1)
let numberOfVehicles1 = userDict["numberOfVehicles"] as! String
self.numberOfVehicles.add(numberOfVehicles1)
let Price1 = userDict["Price"] as! String
self.price.add(Price1)
let imageURL1 = userDict["imageURL"] as! String
self.imagePathString.add(imageURL1)
}) //end second observeSingleEvent
}
else {
let alert = UIAlertController(title: "Error", message: "No one vehicle found", preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "ok", style: UIAlertAction.Style.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
} //end searching object in Vehicles node
} //end first observeSingleEvent
}//end func
func searchingField() {
//setup searchbar
tableView.estimatedRowHeight = 50
navigationController?.navigationBar.prefersLargeTitles = true
searchController.searchBar.delegate = self
searchController.searchResultsUpdater = self
searchController.searchBar.backgroundColor = UIColor.white
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Search"
navigationItem.searchController = searchController
definesPresentationContext = true
let attributes = [
NSAttributedString.Key.foregroundColor : UIColor.black,
NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 17)
]
UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).setTitleTextAttributes(attributes, for: .normal)
UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).title = "Dismiss"
UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self])
}
// MARK: Search Controller
func updateSearchResults(for searchController: UISearchController) {
var regArray = self.model as NSArray as! [String]
if let searchText = searchController.searchBar.text,
!searchText.isEmpty {
matches.removeAll()
for index in 0..<model.count {
if regArray[index].lowercased().contains(
searchText.lowercased()) {
matches.append(index)
}
}
searching = true
} else {
searching = false
}
tableView.reloadData()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searching = false
tableView.reloadData()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return searching ? matches.count : model.count
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searching ? matches.count : model.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "TableCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as! Vehicles_cell
let row = indexPath.row
var regArray = self.model as NSArray as! [String]
cell.Label.text = searching ? regArray[matches[row]] : model[row] as! String
cell.Subtitle?.text = "N. Vehicles: \(self.numberOfVehicles[indexPath.row]) - Price: \(self.price[indexPath.row])$"
Alamofire.request("\(self.imagePathString[indexPath.row])").response { response in
guard let image = UIImage(data:response.data!) else {
// Handle error
return
}
let imageData = image.jpegData(compressionQuality: 1.0)
cell.Image.contentMode = .scaleAspectFit
cell.Image.image = UIImage(data : imageData!)
}
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowrentDetails" {
let myIndexPath = self.tableView.indexPathForSelectedRow!
//save detail1 in UserDefault
let SVDetail = self.detailpage[myIndexPath.row]
let SVDetaildefaults = UserDefaults.standard
SVDetaildefaults.set(SVDetail, forKey: "sv_detail")
SVDetaildefaults.synchronize()
_ = segue.destination
as! Vehicles_Detail
}
}
//IMPOSTA LE DIMENSIONI DELLE CELLE
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
switch indexPath.row {
default:
return 100
}
}
}
I expect the table show on the opening all the data from database, while actually and not repeat unless I click on the searchbar. And the table shouldn't be repeated twice.
Edit (solution to duplicates)
This is so embarrassing. The answer to this problem is simple
In numberOfSections function, I used address.count instead to use 1 Section. So, what I saw were not duplicate cells, but new block sections of model.count
Your tableview isn't reloading data once it is fetched during the Firebase observation, but is in updateSearchResults(). Does adding self.InfoTableView.reloadData() inside your loadData() in between the //end searching object in Vehicles node and //end first observeSingleEvent closing brackets fix the issue?
Edit: The reason your reload of tableview data doesn't fix the issue within viewDidLoad() is because it gets called before the loadData() function starts to iterate through your Firebase data objects. By doing it at the end of the Firebase observation, you're ensuring that you've loaded all of your data from Firebase prior to calling the reload.

TableView not displaying data from Firebase Database

I am building an app which uses Firebase's database service. I am trying to load the data into a table view but I am unable to do so. I can't seem to figure out what's going wrong. The code is also not giving me any errors. I've checked the database permissions on Firebase and they seem to be good. Here's my code:
import UIKit
import Firebase
struct postStruct {
let word : String!
let wordType : String!
}
class sentenceBuilderViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var wordSearchBar: UISearchBar!
#IBOutlet weak var wordsTableView: UITableView!
var posts = [postStruct]()
override func viewDidLoad() {
wordsTableView.reloadData()
getWordsFromDatabase()
super.viewDidLoad()
wordsTableView.delegate = self
wordsTableView.dataSource = self
}
func getWordsFromDatabase() {
let databaseRef = Database.database().reference()
databaseRef.child("wordList").queryOrderedByKey().observeSingleEvent(of: .childAdded, with: {
snapshot in
let word = (snapshot.value as? NSDictionary)!["word"] as? String
let wordType = (snapshot.value as? NSDictionary
)!["wordType"] as? String
self.posts.insert(postStruct(word: word, wordType: wordType), at: 0)
})
wordsTableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = wordsTableView.dequeueReusableCell(withIdentifier: "Cell")
let wordLabel = cell?.viewWithTag(1) as! UILabel
wordLabel.text = posts[indexPath.row].word
let wordTypeLabel = cell?.viewWithTag(2) as! UILabel
wordTypeLabel.text = posts[indexPath.row].wordType
return cell!
}
}
Any help and inputs would be appreciated! Thanks!
The problem is that you are just observing a single event here:
databaseRef.child("wordList").queryOrderedByKey().observeSingleEvent(of: .childAdded, with: {
snapshot in
What this does is that it justs goes through your database and once it finds any child, it displays that one without going further. What you need to do is change it to observe like this:
func getAllWordsFromDatabase() {
let databaseRef = Database.database().reference()
databaseRef.child("wordList").queryOrderedByKey().observe(.childAdded, with: {
snapshot in
let word = (snapshot.value as? NSDictionary)!["word"] as? String
let wordType = (snapshot.value as? NSDictionary)!["wordType"] as? String
self.posts.append(postStruct(word: word, wordType: wordType))
DispatchQueue.main.async {
self.wordsTableView.reloadData()
}
})
}
Try implementing this and it should work.
Move the "getWordsFromDatabase()" line in "viewDidLoad" function to AFTER you assign the delegate and data source, like this:
override func viewDidLoad() {
super.viewDidLoad()
wordsTableView.delegate = self
wordsTableView.dataSource = self
getWordsFromDatabase()
}
Also you can try to add a "reloadData()" method in the databaseRef block on the main queue, like this:
let databaseRef = Database.database().reference()
databaseRef.child("wordList").queryOrderedByKey().observeSingleEvent(of: .childAdded, with: {
snapshot in
let word = (snapshot.value as? NSDictionary)!["word"] as? String
let wordType = (snapshot.value as? NSDictionary
)!["wordType"] as? String
self.posts.insert(postStruct(word: word, wordType: wordType), at: 0)
DispatchQueue.main.async {
wordsTableView.reloadData()
}
})

Error while receiving image array from the Child's folder Firebase Swift

I am currently trying to receive an array of images with title from my Child's folder to the another offertableview which is connected by button from the detailViewController, but unfortunately I keep getting an error. Below I attached images of my firebase data structure and my mainstoryboard screenshot.
For the first table view I have a list of the restaurants and upon selecting a cell it transfers to the detail view controller which lists all the details of the restaurant (for that I've created a model of my restaurant) in that detailVC I have a button connected to the offerstableview which lists all the offers of that particular restaurant.
When I click to the button it transfers to the offers table view which results to the application shut down due to the error.
my offers tableview code:
var ref: DatabaseReference!
var offerImageArray = [String]()
var titleArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
fetchBars()
}
func fetchBars(){
ref.child("Paris").observeSingleEvent(of: .value, with: { (snapshot) in
for child in snapshot.children {
let snap = child as! DataSnapshot
let imageSnap = snap.childSnapshot(forPath: "offers")
let dict = imageSnap.value as! [String: Any]
let imageUrl = dict["offer_image"] as? String
let titleUrl = dict["offer_title"] as? String
self.offerImageArray = [imageUrl! as String]
self.titleArray = [titleUrl! as String]
}
})
self.tableView.reloadData()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return offerImageArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "OfferCell", for: indexPath) as! OffersTableViewCell
cell.offerImageView.sd_setImage(with: URL(string: self.offerImageArray[indexPath.row]))
cell.titleLabel.text = titleArray[indexPath.row]
return cell
}
Xcode error:
2017-08-16 10:26:33.652 Applic[1174]
[Firebase/Analytics][I-ACS003007] Successfully created Firebase
Analytics App Delegate Proxy automatically. To disable the proxy, set
the flag FirebaseAppDelegateProxyEnabled to NO in the Info.plist
2017-08-16 10:26:33.826 Applic[1174]
[Firebase/Analytics][I-ACS032003] iAd framework is not linked. Search
Ad Attribution Reporter is disabled. 2017-08-16 10:26:33.828
Applic[1174] [Firebase/Analytics][I-ACS023012] Firebase
Analytics enabled fatal error: unexpectedly found nil while unwrapping
an Optional value
import UIKit
import Firebase
import FirebaseAuth
import FirebaseDatabase
import FirebaseStorage
import SDWebImage
class OffersTableVC: UITableViewController {
var ref: DatabaseReference!
var offerImageArray = [String]()
var titleArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
fetchBars()
}
func fetchBars(){
Database.database().reference().child("paris").observeSingleEvent(of: .value, with: { (snapshot) in
print("Main Snapshot is \(snapshot)")
for child in snapshot.children {
let snap = child as! DataSnapshot
let imageSnap = snap.childSnapshot(forPath: "offers")
if let snapDict = imageSnap.value as? [String:AnyObject] {
let dictKeys = [String](snapDict.keys)
print(dictKeys)
let dictValues = [AnyObject](snapDict.values)
print(dictValues)
for each in dictValues{
let imageUrl = each["offer_image"] as? String
print(imageUrl!)
self.offerImageArray.append(imageUrl!)
}
self.tableView.dataSource = self
self.tableView.delegate = self
self.tableView.reloadData()
}
// let dict = imageSnap.value as! [String: Any]
// let imageUrl = dict["offer_image"] as? String
// let titleUrl = dict["offer_title"] as? String
// self.offerImageArray = [imageUrl! as String]
// self.titleArray = [titleUrl! as String]
}
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return offerImageArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "OfferCell", for: indexPath) as! OffersTableViewCell
cell.offerImageView.sd_setImage(with: URL(string: self.offerImageArray[indexPath.row]))
// cell.titleLabel.text = titleArray[indexPath.row]
return cell
}
}
1) in Appdelegate.swift add selectedBarname as follow
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var selectedBarName = String()
2) MainTableVc Add following code
After declaration of Class
let appDelegate = UIApplication.shared.delegate as! AppDelegate
in prepareForSegue
if segue.identifier == "DetailView", let bar = selectedBar{
appDelegate.selectedBarName = bar.barName
3) OfferTableVc
now just call this function and Done but do not call your fetchBars now just getOffers
func getOffers() {
let databaseRef = Database.database().reference().child("aktau")
databaseRef.queryOrdered(byChild: "bar_name").queryEqual(toValue: self.appDelegate.selectedBarName).observe(.value, with: { snapshot in
if ( snapshot.value is NSNull ) {
print("not found)")
} else {
print(snapshot.value!)
for child in snapshot.children {
let snap = child as! DataSnapshot
let imageSnap = snap.childSnapshot(forPath: "offers")
if let snapDict = imageSnap.value as? [String:AnyObject] {
let dictValues = [AnyObject](snapDict.values)
for each in dictValues{
let imageUrl = each["offer_image"] as? String
print(imageUrl!)
self.offerImageArray.append(imageUrl!)
}
self.tableView.dataSource = self
self.tableView.delegate = self
self.tableView.reloadData()
}
}
}
})
}

Swift - "attempt to insert row 0 into section 0, but there are only 0 rows in section 0 after the update"

Xcode 8.1, Swift 2.3, iOS 10.1, And I use Firebase
I registered notices using firebase. And I am trying show notices on uitableview. viewDidLoad() succesfully connection firebase and get value. But I can not list the incoming data.
First I was getting the error "cellForRowAtIndexPath doesn't work". After, i use forRow & inSection. But now I'm getting the error that I do not know what it means.
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to insert row 0 into section 0, but there are only 0 rows in section 0 after the update'
NoticeViewController.swift
import UIKit
import FirebaseDatabase
import FirebaseAuth
import FirebaseStorage
private let reuseIdentifier = "NoticeViewTable"
class NoticeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var aivLoading: UIActivityIndicatorView!
#IBOutlet weak var noticeTableView: UITableView!
var databaseRef = FIRDatabase.database().reference()
var usersDict = NSDictionary()
var noticesArray = [AnyObject]()
var loggedInUser : AnyObject?
#IBAction func didTapAddNotice(sender: AnyObject) {
let mainStorboard: UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let viewController: UIViewController = mainStorboard.instantiateViewControllerWithIdentifier("AddNoticeView")
self.presentViewController(viewController, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
self.loggedInUser = FIRAuth.auth()?.currentUser
self.aivLoading.startAnimating()
self.databaseRef.child("notice").observeEventType(.Value, withBlock: { (snapshot) in
self.usersDict = snapshot.value as! NSDictionary
self.noticesArray = [AnyObject]()
for (userId, details) in self.usersDict {
let noticeImg = details.objectForKey("noticeImage1") as! String
let profileImg = details.objectForKey("profileImage") as! String
let profileName = details.objectForKey("userName") as! String
let wage = details.objectForKey("wage") as! String
let noticeName = details.objectForKey("noticeName") as! String
if(self.loggedInUser?.uid != userId as? String){
details.setValue(userId, forKey: "uId")
self.noticesArray.append(details)
}
self.noticeTableView?.reloadData()
self.noticeTableView.insertRowsAtIndexPaths([NSIndexPath(forRow: 0, inSection: 0)], withRowAnimation: UITableViewRowAnimation.Automatic)
self.aivLoading.stopAnimating()
}
}) {(error) in
print(error.localizedDescription)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.noticesArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell: NoticeViewTableViewCell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier, forIndexPath: indexPath) as! NoticeViewTableViewCell
let profileImageURL = NSURL(string: self.noticesArray[indexPath.row]["profileImage"] as! String)
let profileImageData = NSData(contentsOfURL: profileImageURL!)
cell.profilePic.image = UIImage(data:profileImageData!)
let noticeImageURL = NSURL(string: self.noticesArray[indexPath.row]["noticeImage!"] as! String)
let noticeImageData = NSData(contentsOfURL: noticeImageURL!)
cell.noticeImage.image = UIImage(data:noticeImageData!)
//add a border and corner radius the images
cell.profilePic.layer.masksToBounds = true
cell.profilePic.layer.cornerRadius = cell.profilePic.frame.size.width/2.0
cell.profilePic.layer.borderWidth = 1.5
let profileName = (self.noticesArray[indexPath.row]["userName"] as? String)!
cell.userName.text = profileName
let noticeName = (self.noticesArray[indexPath.row]["noticeName"] as? String)!
cell.noticeName.text = noticeName
let wage = (self.noticesArray[indexPath.row]["wage"] as? String)!
cell.wage.text = wage
return cell
}
}
There are a lot of mistakes in your code. Any of them could cause the crash.
A row is inserted in the table view even if uid is not valid.
details is appended to the datasource array but inserted at index 0 in the table view
Do not call both reloadData() and insertRowsAtIndexPaths. Delete reloadData()
For other users stumbling upon this question, these errors also arise when trying to update a TableView or CollectionView with no dataSource assigned. Make sure the TableView's dataSource is connected (when using a storyboard or nib) or assigned programmatically.

Resources