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 :)
Related
Hey i got "Thread 1: Fatal error: Index out of range" (in this line selectedId = idArray[indexPath.row] ) error every time i clicked any cell at table view. How i can solve this problem. I think i have problem with my arrays but i cant figure that out. I was do same thing at my last app but i couldnt get any error.
import UIKit
import CoreData
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var nameArray = [String]()
var idArray = [UUID?]()
var selectedName = ""
var selectedId : UUID?
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
navigationController?.navigationBar.topItem?.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.add, target: self, action: #selector(addNewPatient))
getData()
}
override func viewWillAppear(_ animated: Bool) {
NotificationCenter.default.addObserver(self, selector: #selector(getData) , name: NSNotification.Name(rawValue: "newData"), object: nil)
}
#objc func getData() {
nameArray.removeAll(keepingCapacity: false)
idArray.removeAll(keepingCapacity: false)
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "ToName")
fetchRequest.returnsObjectsAsFaults = false
do {
let results = try context.fetch(fetchRequest)
if results.count > 0 {
for result in results as! [NSManagedObject] {
if let name = result.value(forKey: "name") as? String {
self.nameArray.append(name)
}
if let id = result.value(forKey: "id") as? UUID {
self.idArray.append(id)
}
}
}
} catch {
print("error")
}
tableView.reloadData()
}
#objc func addNewPatient() {
selectedName = ""
performSegue(withIdentifier: "toNameVC", sender: nil)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return nameArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
var content = cell.defaultContentConfiguration()
content.text = nameArray[indexPath.row]
// content.secondaryText = "secondary test"
cell.contentConfiguration = content
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toNameVC" {
let destinationVC = segue.destination as! ToNameViewController
destinationVC.choosenName = selectedName
destinationVC.choosenId = selectedId
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedName = nameArray[indexPath.row]
selectedId = idArray[indexPath.row]
performSegue(withIdentifier: "toNameVC", sender: nil)
}
}
This is my toNameViewController.swift page
ToNameViewController.swift
Patient Record App
import UIKit
import CoreData
class ToNameViewController: UIViewController {
#IBOutlet weak var nameLabelText: UITextField!
#IBOutlet weak var tcLabelText: UITextField!
#IBOutlet weak var birthDateLabelText: UITextField!
var choosenName = ""
var choosenId : UUID?
override func viewDidLoad() {
super.viewDidLoad()
if choosenName != "" {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "ToName")
let idString = choosenId?.uuidString
fetchRequest.predicate = NSPredicate(format: "id = %#", idString!)
fetchRequest.returnsObjectsAsFaults = false
do {
let results = try context.fetch(fetchRequest)
if results.count > 0 {
for result in results as! [NSManagedObject] {
if let name = result.value(forKey: "name") as? String {
nameLabelText.text = name
}
if let tc = result.value(forKey: "tc") as? Int {
tcLabelText.text = String(tc)
}
if let birth = result.value(forKey: "birth") as? Int {
birthDateLabelText.text = String(birth)
}
}
}
} catch {
print("error")
}
}else {
}
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(hiddenKeyboard))
view?.addGestureRecognizer(gestureRecognizer)
}
#objc func hiddenKeyboard() {
view?.endEditing(true)
}
#IBAction func saveButton(_ sender: Any) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let newPatient = NSEntityDescription.insertNewObject(forEntityName: "ToName", into: context)
newPatient.setValue(nameLabelText.text, forKey: "name")
if let tc = Int(tcLabelText.text!) {
newPatient.setValue(tc, forKey: "tc")
}
if let birth = Int(birthDateLabelText.text!) {
newPatient.setValue(birth, forKey: "birth")
}
do {
try context.save()
print("saved")
} catch {
print("error")
}
NotificationCenter.default.post(name: NSNotification.Name("newData"), object: nil)
self.navigationController?.popViewController(animated: true)
}
}
This is a common mistake: Multiple arrays for the data source is extremely error-prone if both arrays are populated with optionals.
Declare a custom struct
struct Item {
let name: String
let id: UUID?
}
Declare the data source
var items = [Item]()
Populate the array (valueForKey syntax is outdated)
let fetchRequest = NSFetchRequest<ToName>(entityName: "ToName")
fetchRequest.returnsObjectsAsFaults = false
do {
let results = try context.fetch(fetchRequest)
for result in results {
if let name = result.name {
self.items.append(Item(name: name, id: result.id))
}
}
tableView.reloadData()
} catch {
print(error)
}
In numberOfRowsInSection
return items.count
In cellForRowAt
content.text = items[indexPath.row].name
And in didSelectRowAt
let item = items[indexPath.row]
selectedName = item.name
selectedId = item.id
You can even pass the Item instance to the second view controller rather than the two selected... properties.
But why not even
var toNames = [ToName]()
This avoids any out of range crash
Inside your getData function u append values to idArray. But result.value(forKey: "id") is optional and values get appended unless it is nil. So there may be a difference between the count between nameArray and idArray. So if the result.value(forKey: "id") is nil append a default value to idArray.
#objc func getData() {
nameArray.removeAll(keepingCapacity: false)
idArray.removeAll(keepingCapacity: false)
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "ToName")
fetchRequest.returnsObjectsAsFaults = false
do {
let results = try context.fetch(fetchRequest)
if results.count > 0 {
for result in results as! [NSManagedObject] {
if let name = result.value(forKey: "name") as? String {
self.nameArray.append(name)
//change here like this
if let id = result.value(forKey: "id") as? UUID {
self.idArray.append(id)
}else{
self.idArray.append("your_default_value")//default value or nil
}
}
}
} catch {
print("error")
}
tableView.reloadData()
}
Add an extension to the Array:
extension Array {
func object(at index: Int) -> Element? {
if index < count {
return self[index]
} else {
return nil
}
}
}
Now use this method whenever you access the element from the array.
I am trying to display users for a messaging app in a TableView however when I scroll it updates the cells and therefore pulls the data again thus lagging. I am using a prototype cell in a storyboard.
import UIKit
import FirebaseAuth
import FirebaseDatabase
class TableViewCell: UITableViewCell {
#IBOutlet weak var profilePicture: UIImageView!
#IBOutlet weak var statusImage: UIImageView!
#IBOutlet weak var nameLabel: UILabel!
override func prepareForReuse() -> Void {
profilePicture.image = nil
statusImage.backgroundColor = UIColor.systemGreen
nameLabel.text = nil
}
}
class MainController: UITableViewController {
#IBOutlet weak var tableViews: UITableView!
var users = [String]()
var decryptedUsers = [String]()
#IBAction func signOut(_ sender: Any) {
do {
let userid = Auth.auth().currentUser?.uid
let dbReference = Database.database().reference().child("Users").child(userid!).child("status")
dbReference.observeSingleEvent(of: .value, andPreviousSiblingKeyWith: { snapshot,error in
if (snapshot.value as? String == "online") {
dbReference.setValue("offline")
}
})
try Auth.auth().signOut()
self.performSegue(withIdentifier: "backToSignIn", sender: nil)
} catch {
let alert = UIAlertController(title: "Error", message: "There was an error signing out. Please ensure that you are connected to the internet.", preferredStyle: UIAlertController.Style.alert)
let okButton = UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil)
alert.addAction(okButton)
self.present(alert, animated: true, completion: nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
setOnline()
fetchUsers()
}
static var userslist = [String]()
func fetchUsers() {
let userid = (Auth.auth().currentUser?.uid)!
let dbRef = Database.database().reference().child("Chatlist").child(userid)
dbRef.observe(.value) { (snapshot, error) in
self.users.removeAll()
for yeet in snapshot.children {
let yeetsnap = yeet as! DataSnapshot
self.users.append(yeetsnap.childSnapshot(forPath: "id").value as! String)
print(self.users)
self.tableViews.reloadData()
}
}
}
func setOnline() {
let userid = Auth.auth().currentUser?.uid
let dbReference = Database.database().reference().child("Users").child(userid!).child("status")
dbReference.observeSingleEvent(of: .value, andPreviousSiblingKeyWith: { snapshot,error in
if (snapshot.value as? String == "offline") {
dbReference.setValue("online")
}
})
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell: TableViewCell?
cell = nil
if (cell == nil) {
cell = (tableView.dequeueReusableCell(withIdentifier: "CellProto") as? TableViewCell)!
DispatchQueue.main.async {
cell!.profilePicture.layer.masksToBounds = true
cell!.profilePicture.layer.cornerRadius = (cell!.profilePicture.bounds.width) / 2
cell!.statusImage.layer.masksToBounds = true
cell!.statusImage.layer.cornerRadius = (cell!.statusImage.bounds.width) / 2
let dbRef = Database.database().reference().child("Users").child(self.users[indexPath.row])
dbRef.observeSingleEvent(of: .value) { (snapshot) in
cell!.nameLabel?.text = snapshot.childSnapshot(forPath: "username").value as? String
let urlstring = snapshot.childSnapshot(forPath: "imageURL").value as? String
if (urlstring != "default") {
let url = URL(string: urlstring!)
let data = try? Data(contentsOf: url!)
cell!.profilePicture.image = UIImage(data: data!)
}
let statusstring = snapshot.childSnapshot(forPath: "status").value as? String
if (statusstring == "online") {
cell!.statusImage.backgroundColor = UIColor.systemGreen
} else if (statusstring == "offline") {
cell!.statusImage.backgroundColor = UIColor.systemGray
} else if (statusstring == "appearoffline") {
cell!.statusImage.backgroundColor = UIColor.systemGray
} else if (statusstring == "dnd") {
cell!.statusImage.backgroundColor = UIColor.systemRed
}
}
}
}
print("test")
return cell!
}
}
Do not load the data every time within the cellForRow. This reloads the data every time you scroll, causing the table to lag.
A better approach would be to load the data once by putting the database retrieving code in here:
func viewWillAppear(_ animated: Bool){
super.viewWillAppear(animated)
// retrieve the data here by calling a separate function
}
Whatever you do, it is not a good coding approach to load data within the cellForRow method.
You can also retrieve the data by calling the function whenever the user swipes down from the top of the table to reload the table.
One more option would be to only load a number of rows at once (10 max for example), then when the user scroll to the bottom of the 10 rows and keeps scrolling it will then retrieve the next 10 datapoints for the next 10 rows in the table.
I am having issues displaying the make and model of the selected cell in another view controller connected by a segue. The Cells display the Vin Number and I want the make and model info for that specific vin number passed to another ViewController. I have attempted to set up passing the values but can not get it to work. Can someone take a look and see if they can spot where I went wrong?
Here is what my firebase database looks like:
Vehicles: {
5UXKR0C34H0X82785: {
VehicleInfo: {
make:"toyota",
model:"corolla",
VinNumber: "5UXKR0C34H0X82785"
}
}
}
Here is my Vehicle model class
import Foundation
import FirebaseDatabase
struct VehicleModel {
var Make: String?
var Model: String?
var VinNumber: String?
init(Make: String?, Model: String?, VinNumber: String?){
self.Make = Make
self.Model = Model
self.VinNumber = VinNumber
}
init(snapshot: DataSnapshot) {
let snapshotValue = snapshot.value as! [String: AnyObject]
VinNumber = snapshotValue["VinNumber"] as? String
Make = snapshotValue["Make"] as? String
Model = snapshotValue["Model"] as? String
}
}
Here is my view controller code
import UIKit
import Firebase
import FirebaseDatabase
class InventoryTableViewController: UITableViewController{
var ref: DatabaseReference!
var refHandle: UInt!
var userList = [VehicleModel]()
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference()
tableView.delegate = self
tableView.dataSource = self
tableView?.register(UITableViewCell.self, forCellReuseIdentifier:
"cellId")
fetchUsers()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
return userList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
// Set cell contents
let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for:
indexPath) as UITableViewCell
let eachvehicle = userList[indexPath.row]
cell.textLabel!.text = "\(String(describing: eachvehicle.VinNumber))"
return cell
}
func fetchUsers(){
refHandle = ref.child("Vehicles").observe(.childAdded, with: {
(snapshot) in
if let dictionary = snapshot.childSnapshot(forPath:
"VehicleInfo").value as? [String: AnyObject] {
print(dictionary)
let VinNumber = dictionary["VinNumber"]
let Make = dictionary["Make"]
let Model = dictionary["Model"]
self.userList.insert(VehicleModel(Make: Make as? String, Model:
Model as? String, VinNumber: VinNumber as? String), at: 0)
self.tableView.reloadData()
}
})
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath:
IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let destination = storyboard.instantiateViewController(withIdentifier:
"AdditionalInfoViewController") as! AdditionalInfoViewController
navigationController?.pushViewController(destination, animated: true)
performSegue(withIdentifier: "toAdditionalInfo", sender: self)
let row = indexPath.row
print("working so far ")
let indexPath = tableView.indexPathForSelectedRow!
let currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell
makeToPass = currentCell.Model
modelToPass = currentCell.Make
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toMapView" {
var viewController = segue.destination as!
AdditionalInfoViewController
viewController.makeToPass = makeToPass
viewController.modelToPass = modelToPass
}
}
structure for variables in my AdditionalInfoView Controller
var passedValue: String?
var latitudePassedValue: String?
var longitudePassedValue: String?
The problem seems to be in the parsing of the data from the database. The structure has make and model all lower case:
// ...
make:"toyota",
model:"corolla",
// ...
But in the parsing method you're addressing it with the first letter in uppercase:
// ...
Make = snapshotValue["Make"] as? String // "Make"
Model = snapshotValue["Model"] as? String // "Model"
// ...
I am having issues getting my data from firebase to show on my TableView. I only want the vin number to display on the table view. Right now i am either getting cells that display "nil", or nothing in the cells.
My goal is to have each cell display the Vin Number.
Can someone take a look and let me know where i have an issue?
Thanks!!!
Alex
here is what my firebase database looks like
child --Vehicles
child--5UXKR0C34H0X82785
child-- VehicleInfo
then under the "vehicle Info" child it displays these three fields
make:"toyota"
model:"corolla"
VinNumber: "5UXKR0C34H0X82785"
Here is my Vehicle model class
import Foundation
import FirebaseDatabase
struct VehicleModel {
var Make: String?
var Model: String?
var VinNumber: String?
init(Make: String?, Model: String?, VinNumber: String?){
self.Make = Make
self.Model = Model
self.VinNumber = VinNumber
}
init(snapshot: DataSnapshot) {
let snapshotValue = snapshot.value as! [String: AnyObject]
VinNumber = snapshotValue["VinNumber"] as? String
Make = snapshotValue["Make"] as? String
Model = snapshotValue["Model"] as? String
}
}
Here is my view controller code
import UIKit
import Firebase
import FirebaseDatabase
class InventoryTableViewController: UITableViewController{
var ref: DatabaseReference!
var refHandle: UInt!
var userList = [VehicleModel]()
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference()
tableView.delegate = self
tableView.dataSource = self
tableView?.register(UITableViewCell.self, forCellReuseIdentifier:
"cellId")
fetchUsers()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
return userList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
// Set cell contents
let cell = tableView.dequeueReusableCell(withIdentifier: "cellId", for:
indexPath) as UITableViewCell
let eachvehicle = userList[indexPath.row]
cell.textLabel!.text = "\(String(describing: eachvehicle.VinNumber))"
return cell
}
func fetchUsers(){
refHandle = ref.child("Vehicles").observe(.childAdded, with: {
(snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
print(dictionary)
let VinNumber = dictionary["VinNumber"]
let Make = dictionary["Make"]
let Model = dictionary["Model"]
self.userList.insert(VehicleModel(Make: Make as? String, Model:
Model as? String, VinNumber: VinNumber as? String), at: 0)
self.tableView.reloadData()
}
})
}
}
Also, I am having issues displaying the make and model of the selected cell in another view controller connected by a segue. I have attempted to set up passing the values but can not get it to work.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath:
IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let destination = storyboard.instantiateViewController(withIdentifier:
"AdditionalInfoViewController") as! AdditionalInfoViewController
navigationController?.pushViewController(destination, animated: true)
performSegue(withIdentifier: "toAdditionalInfo", sender: self)
let row = indexPath.row
print("working so far ")
let indexPath = tableView.indexPathForSelectedRow!
let currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell
makeToPass = currentCell.Model
modelToPass = currentCell.Make
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toMapView" {
var viewController = segue.destination as! AdditionalInfoViewController
viewController.makeToPass = makeValueToPass
viewController.modelToPass = modelValueToPass
}
}
Correct me if I'm wrong, but this is your data structure, right?
Vehicles: {
5UXKR0C34H0X82785: {
VehicleInfo: {
make:"toyota",
model:"corolla",
VinNumber: "5UXKR0C34H0X82785"
}
}
}
Which means in order to access the data under VehicleInfo, you need to specify that location. There are a few ways you can do this, but one of them would be using childSnapshot(forPath:)
func fetchUsers(){
refHandle = ref.child("Vehicles").observe(.childAdded, with: {
(snapshot) in
if let dictionary = snapshot.childSnapshot(forPath: "VehicleInfo").value as? [String: AnyObject] {
print(dictionary)
let VinNumber = dictionary["VinNumber"]
let Make = dictionary["Make"]
let Model = dictionary["Model"]
self.userList.insert(VehicleModel(Make: Make as? String, Model:
Model as? String, VinNumber: VinNumber as? String), at: 0)
self.tableView.reloadData()
}
})
}
}
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