I am trying to make simple firebase realtime chat app in swift. I have node in my database where number of unread messages is stored/
I have custom tableview cell which has label for displaying number of unread messages
class UserCell: UITableViewCell {
var message: Message? {
didSet {
setNumberOfUnreadMessages()
}
fileprivate func setNumberOfUnreadMessages() {
if let partnerId = message?.chatPartnerId(), let selfId = message?.selfId() {
let unreadMessagesRef = Database.database().reference().child("unread-messages").child(selfId).child(partnerId).child("numberOfUnreadMessages")
unreadMessagesRef.observe(.value, with: { (snapshot) in
if let count = snapshot.value as? Int {
self.unreadMessagesCountLabel.isHidden = false
self.unreadMessagesCountLabel.text = String(count)
} else {
self.unreadMessagesCountLabel.isHidden = true
}
print(snapshot)
}, withCancel: nil)
}
}
My tableView
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as? UserCell
let message = messages[indexPath.row]
cell?.message = message
return cell!
}
func observeUserMessages() {
guard let uid = Auth.auth().currentUser?.uid else { return }
// getting reference to current user's node
let ref = Database.database().reference().child("user-messages").child(uid)
ref.observe(.childAdded, with: { (snapshot) in
let userId = snapshot.key
// getting reference to partners node in user's node
let userMessagesRef = Database.database().reference().child("user-messages").child(uid).child(userId)
userMessagesRef.observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String:AnyObject] {
let message = Message(dictionary: dictionary)
if let chatPartnerId = message.chatPartnerId() {
self.messagesDictionary[chatPartnerId] = message
}
self.attemptReloadOfTableView()
}
}, withCancel: nil)
}, withCancel: nil)
ref.observe(.childRemoved, with: { (snapshot) in
print(snapshot.key)
self.messagesDictionary.removeValue(forKey: snapshot.key)
self.attemptReloadOfTableView()
}, withCancel: nil)
}
// Big thank to Brian Woong)))))
var timer: Timer?
private func attemptReloadOfTableView() {
self.timer?.invalidate()
self.timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(self.handleReloadTableView), userInfo: nil, repeats: false)
}
#objc func handleReloadTableView() {
self.messages = Array(self.messagesDictionary.values)
self.messages.sort(by: { (message1, message2) -> Bool in
return (message1.timeStamp?.intValue)! > (message2.timeStamp?.intValue)!
})
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
When I start my app every tableView.cell shows correct number of unread messages from database. When I start to send messages, tableView reloads and begins to show the number of unread messages from sender not only on his cell, but on ALL cells. Moreover in console I see that it gets more and more snapshots from database with every upcoming message.
What happens? How to fix this strange bug?
let unreadMessagesRefHandle = unreadMessagesRef.observe(.value, with: { (snapshot) in
if let count = snapshot.value as? Int {
self.unreadMessagesCountLabel.isHidden = false
self.unreadMessagesCountLabel.text = String(count)
} else {
self.unreadMessagesCountLabel.isHidden = true
}
print(snapshot)
}, withCancel: nil)
unreadMessagesRef.removeObserver(withHandle: unreadMessagesRefHandle)
Related
I'm trying to do small apps containing firebase codes to learn more about it. so here I made a todo list app, I was able to add the tasks to the firebase and I was able to delete it, the problem I have is updating the status of the task (isComplete: Bool) I've no idea how to write firebase code to update data. almost all the tutorial I read was about the data uploaded to real-time database and I'm using the cloud so I couldn't figure it out. here I wrote this code so when the task is done and I select the cell the circle turn into checkmark.circle It's work but of course the database isn't updated..
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as! TodoCell
if cell.isComplete == false{
cell.doneButton.image = UIImage(systemName: "checkmark.circle")
cell.isComplete = true
} else {
cell.doneButton.image = UIImage(systemName: "circle")
cell.isComplete = false
}
}
}
Adding tasks to firebase codes
public func postTask(task:String, isComplete: Bool,
completion: #escaping (Result<Bool, Error>) -> ()) {
guard let user = Auth.auth().currentUser else {
return
}
let documentRef = db.collection(DatabaseService.itemsCollection).document()
db.collection(DatabaseService.usersCollection).document(user.uid).
collection(DatabaseService.tasksCollection).
document(documentRef.documentID).setData(["task" : task,
"isComplete": isComplete,
"taskId": documentRef.documentID])
{ (error) in
if let error = error {
completion(.failure(error))
} else {
completion(.success(true))
}
}
}
SnapshotListener
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
guard let user = Auth.auth().currentUser else {
return
}
listener = Firestore.firestore().collection(DatabaseService.usersCollection)
.document(user.uid).collection(DatabaseService.tasksCollection)
.addSnapshotListener({ [weak self] (snapshot, error) in
if let error = error {
DispatchQueue.main.async {
self?.showAlert(title: "Try Again", message:
error.localizedDescription)
}
} else if let snapshot = snapshot {
let task = snapshot.documents.map { TasksList($0.data()) }
self?.todoItems = task
}
})
}
based on #bkbkchoy answer I wrote these codes:
func updateTask(task: TasksList,
isComplete: Bool,
completion: #escaping (Result<Bool, Error>) -> ()) {
guard let user = Auth.auth().currentUser else { return }
db.collection(DatabaseService.usersCollection).document(user.uid)
.collection(DatabaseService.tasksCollection).document(task.taskId)
.updateData(["isComplete": isComplete]) { (error) in
if let error = error {
completion(.failure(error))
} else {
completion(.success(true))
}
}
}
}
and under didSelectRow
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as! TodoCell
let isComplete = false
if task2.isComplete == false{
cell.doneButton.image = UIImage(systemName: "checkmark.circle")
cell.doneButton.tintColor = .systemBlue
cell.isComplete = true
} else {
cell.doneButton.image = UIImage(systemName: "circle")
cell.doneButton.tintColor = .systemGray
cell.isComplete = false
}
updateStatus(isComplete: isComplete)
}
private func updateStatus(isComplete: Bool) {
databaseService.updateTask(task: task2, isComplete: isComplete)
{ [weak self] (result) in
switch result {
case .failure(let error):
DispatchQueue.main.async {
self?.showAlert(title: "Try again", message: error.localizedDescription)
}
case .success:
break
}
}
}
}
but I got an error :
No document to update: project/todo-list/database/(default)/documents/users/jYZmghQeXodeF2/tasks/1
struct TasksList {
let task: String
let taskId: String
let isComplete: Bool
}
extension TasksList {
init(_ dictionary: [String: Any]) {
self.task = dictionary["task"] as? String ?? ""
self.taskId = dictionary["taskId"] as? String ?? ""
self.isComplete = dictionary["isComplete"] as? Bool ?? false
}
}
There are a couple of ways to update documents in Cloud Firestore:
Rewrite a specific property using setData:
db.collection(DatabaseService.usersCollection)
.document(user.uid)
.collection(DatabaseService.tasksCollection)
.document(task.taskId)
.setData(["isComplete": isComplete], merge: true)
Note: if you use setData, you must include merge: true to overwrite a single property on an existing document or else the whole document will be overwritten.
Use updateData
db.collection(DatabaseService.usersCollection)
.document(user.uid)
.collection(DatabaseService.tasksCollection)
.document(task.taskId)
.updateData(["isComplete": isComplete]) { err in
if let err = err {
print("error updating document: \(err)")
} else {
print("doc successfully updated")
}
}
Firestore has some great documentation online. If you want to learn more about updating/adding data here's a good place to start.
Your approach cannot work.
The cell is just the view, it shows the UI elements and their values, the data source is the model TasksList (why not simply Task).
Cells are reused and you will lose the isCompleted information in the cell when the user scrolls. You have to update the model and reload the view
First of all declare the model as Task and isComplete as variable. According to the naming guidelines task should be name or title and taskId should be just id
struct Task {
let task: String
let taskId: String
var isComplete: Bool
}
In cellForRow set the UI elements in the cell according to the model
let task = todoItems[indexPath.row]
let imageName = task.isComplete ? "checkmark.circle" : "circle"
cell.doneButton.image = UIImage(systemName: imageName)
cell.doneButton.tintColor = task.isComplete ? .systemBlue : .systemGray
In didSelect toggle isComplete in the model, reload the row and save the task
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
todoItems[indexPath.row].isComplete.toggle()
tableView.reloadRows(at: [indexPath], with: .none)
let task = todoItems[indexPath.row]
updateTask(task: task, isComplete: task.isComplete) { result in print(result) }
}
As the whole task is handed over, the second parameter isComplete is not needed in updateTask.
I have added chatting/messaging to my application. I can go to a users profile, select message and message them.
I also added a tab that show all the users I messaged which is in a table view and you can click a user and see messages between you and them.
The issue happens when I open this tab to see the users Ive messaged when I've messaged more than 10 users. The simulator crashes and I get this error:
"Thread 1: Exception: "Invalid Query. 'in' filters support a maximum of 10 elements in the value array."
here is my code:
import UIKit
import FirebaseFirestore
class MessagingVC: UIViewController {
#IBOutlet weak var tableView: UITableView! { didSet {
tableView.tableFooterView = UIView()
tableView.contentInset.top = 10
}}
#IBOutlet weak var noDataLabel: UILabel!
private let db = Firestore.firestore()
var users = [AppUser]()
var user: AppUser {
return UserManager.currentUser!
}
private var lastMessageDict: [String: Date]?
private var unreadMessageDict: [String: Bool]?
var chatListener: ListenerRegistration?
override func viewDidLoad() {
super.viewDidLoad()
getChats()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
noDataLabel.isHidden = true
// remove any blocked users when entering the screen.
users.removeAll() { self.user.blockedUsers?.contains($0.uid) == true }
// filter hidden users
users = users.filter { $0.isHidden ?? false == false }
tableView.reloadData()
// getChats()
}
// Get all users id that the current user has been chating with.
func getChats() {
let chatCollection = db.collection("chat")
chatListener = chatCollection.whereField("participants", arrayContains: user.uid).addSnapshotListener { [unowned self] (querySnapshot, error) in
guard error == nil else {
print(error!.localizedDescription)
return
}
var userChatIds = [String]()
self.lastMessageDict = [String: Date]()
self.unreadMessageDict = [String: Bool]()
for chat in querySnapshot?.documents ?? [] {
let chatData = chat.data()
if let participants = chatData["participants"] as? [String] {
for particiant in participants {
if particiant != self.user.uid,
!(self.user.blockedUsers?.contains(particiant) == true),
!userChatIds.contains(particiant) {
userChatIds.append(particiant)
if let lastMessageDate = chatData["last message"] as? Timestamp {
self.lastMessageDict![particiant] = lastMessageDate.dateValue()
}
if let unreadMessageDict = chatData["unread message"] as? [String: Bool],
let unreadMesage = unreadMessageDict[self.user.uid] {
self.unreadMessageDict![particiant] = unreadMesage
}
}
}
}
}
if !userChatIds.isEmpty {
self.getChatsInfo(chatIds: userChatIds)
} else {
self.tableView.reloadData()
self.noDataLabel.isHidden = self.users.count > 0
}
}
}
func getChatsInfo(chatIds: [String]) {
getUsersForChat(chatIds) { (users, error) in
guard error == nil else {
print(error?.localizedDescription ?? "")
return
}
for user in users {
if let index = self.users.firstIndex(of: user) {
self.users[index] = user
} else {
self.users.append(user)
}
}
// self.users = users
self.users.sort { (first, second) -> Bool in
let firstDate = self.lastMessageDict?[first.uid]
let secondDate = self.lastMessageDict?[second.uid]
if firstDate == nil { return false }
else if secondDate == nil { return true }
else {
return firstDate! > secondDate!
}
}
self.users = self.users.filter { $0.isHidden ?? false == false }
self.tableView.reloadData()
self.noDataLabel.isHidden = self.users.count > 0
}
}
func getUsersForChat(_ ids: [String], completion:#escaping (_ users: [AppUser], _ error: Error?)->()) {
var allUsers = [AppUser]()
let allids = self.users.map { $0.uid }
let ids = ids.filter { !allids.contains($0) }
if ids.count == 0 {
completion(allUsers, nil)
return
}
var error: Error?
let userCollection = db.collection("users")
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
userCollection.whereField("uid", in: ids).getDocuments {querySnapshot,err in
error = err
var users = [AppUser]()
for document in querySnapshot?.documents ?? [] {
if let restaurant = Restaurant(snapshot: document) {
users.append(restaurant)
}
}
allUsers.append(contentsOf: users)
dispatchGroup.leave()
}
dispatchGroup.enter()
let userCollection2 = db.collection("users2")
userCollection2.whereField("uid", in: ids).getDocuments {querySnapshot,err in
error = err
var users = [AppUser]()
for document in querySnapshot?.documents ?? [] {
if let user = AppUser(snapshot: document) {
users.append(user)
}
}
allUsers.append(contentsOf: users)
dispatchGroup.leave()
}
dispatchGroup.notify(queue: DispatchQueue.main) {
completion(allUsers, error)
}
}
deinit {
chatListener?.remove()
}
}
extension MessagingVC: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MessageCell", for: indexPath) as! MessagingTableViewCell
let user = users[indexPath.row]
cell.nameLabel.text = user.firstName + " " + user.lastName
if let unreadMessage = unreadMessageDict?[user.uid],
unreadMessage == true {
cell.unreadMessageIndicatorLabel.isHidden = false
} else {
cell.unreadMessageIndicatorLabel.isHidden = true
}
cell.photoImageView.image = nil
user.getProfileImage { (image) in
DispatchQueue.main.async {
if let cell = tableView.cellForRow(at: indexPath) as? MessagingTableViewCell {
cell.photoImageView.image = image
}
}
}
if let rest = user as? Restaurant {
cell.categoryImageView.image = UIImage(named: rest.Categories1)
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let user = users[indexPath.row]
// unreadMessageDict?[user.uid] = false
performSegue(withIdentifier: "messagingToChatSegue", sender: user)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "messagingToChatSegue" {
let chatVC = segue.destination as! ChatViewController
let otherUser = sender as! AppUser
chatVC.currentUser = user
chatVC.otherUser = otherUser
chatVC.channelId = ClickedResturantVC.generateIdForMessages(user1Id: user.uid, user2Id: otherUser.uid)
}
}
}
Here is a picture of the error I am getting
Here is a picture of my Firestore database
The error message is telling you that Firestore doesn't support more than 10 items in the ids array that you pass to this query filter:
userCollection.whereField("uid", in: ids)
According to the documentation for this sort of query:
Note the following limitations for in and array-contains-any:
in and array-contains-any support up to 10 comparison values.
If you need more than 10, you will need to batch them into multiple queries.
** I want my tableview to reload after it sees a change in firestore database I thought that using tableview reload would make it reload but no it doesn't it only loads the new data after I restart the app I want the new data to reload right after function load daily motivation has a change in it **
import UIKit
import Firebase
//MARK: MAINVIEW MOTIVATION
class motivationviewcontroller : UIViewController,UITableViewDataSource,UITableViewDelegate{
var motivationThoughts = [MotivatioNDataModel]()
var tableview : UITableView!
override func viewDidLoad() {
print("madicc")
print("the user logged in is \( Auth.auth().currentUser?.email)")
tableview = UITableView(frame: view.bounds, style: .plain)
tableview.backgroundColor = UIColor.white
view.addSubview(tableview)
var layoutGuide : UILayoutGuide!
layoutGuide = view.safeAreaLayoutGuide
let cellNib = UINib(nibName: "dailyMotivationTableViewCell", bundle: nil)
tableview.register(cellNib, forCellReuseIdentifier: "DailyThoughtCELL")
tableview.leadingAnchor.constraint(equalTo: layoutGuide.leadingAnchor).isActive = true
tableview.topAnchor.constraint(equalTo: layoutGuide.topAnchor).isActive = true
tableview.trailingAnchor.constraint(equalTo: layoutGuide.trailingAnchor).isActive = true
tableview.bottomAnchor.constraint(equalTo: layoutGuide.bottomAnchor).isActive = true
tableview.dataSource = self
tableview.delegate = self
loaddailymotivation()
self.tableview.reloadData()
}
override func viewDidAppear(_ animated: Bool) {
//loaddailymotivation()
self.tableview.reloadData()
}
//======================================================================
//MARK: LOADS THE DATA INTO THE TABLEVIEW
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
motivationThoughts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "DailyThoughtCELL", for: indexPath) as? dailyMotivationTableViewCell
cell!.generateCellsforDailymotivation(_MotivationdataMODEL: motivationThoughts[indexPath.row])
return cell!
}
//MARK: FUNCTION THAT HANDLES GETTING THE DATA FROM FIREBASE
func loaddailymotivation() {
FirebaseReferece(.MotivationDAILY).getDocuments { (snapshot, error) in
if let error = error {
print("error getting MOTIVATIONDAILY DATA \(error.localizedDescription)")
}
else {
guard let snapshot = snapshot else { return }
for allDocument in snapshot.documents {
let data = allDocument.data()
print("\(allDocument.documentID) => \(allDocument.data())")
print("we have\(snapshot.documents.count) documents in this array")
let dailymotivationTitle = data["Motivation title"] as! String //calls the data thats heald inside of motivation title in firebase
let dailyMotivationScripture = data["daily motivation scripture"] as! String //calls the data thats heald inside of Motivation script in firebase
let dailyMotivationNumberOfLikes = data["Number of likes in daily motivation post"]as! Int
let newthought = MotivatioNDataModel(RealmotivationTitle: dailymotivationTitle, RealmotivationScrip: dailyMotivationScripture, RealmotivationNumberOfLikes: dailyMotivationNumberOfLikes )
self.motivationThoughts.append(newthought)
}
}
}
}
Problem is you are fetching the data but not reloading your tableView after that, Change your loaddailymotivation() with the below one
func loaddailymotivation() {
FirebaseReferece(.MotivationDAILY)
.addSnapshotListener { querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documentChanges.forEach { diff in
if (diff.type == .added) {
let data = diff.document.data()
let dailymotivationTitle = data["Motivation title"] as! String //calls the data thats heald inside of motivation title in firebase
let dailyMotivationScripture = data["daily motivation scripture"] as! String //calls the data thats heald inside of Motivation script in firebase
let dailyMotivationNumberOfLikes = data["Number of likes in daily motivation post"]as! Int
let newthought = MotivatioNDataModel(RealmotivationTitle: dailymotivationTitle, RealmotivationScrip: dailyMotivationScripture, RealmotivationNumberOfLikes: dailyMotivationNumberOfLikes )
self.motivationThoughts.append(newthought)
}
if (diff.type == .modified) {
print("Modified data: \(diff.document.data())")
// here you will receive if any change happens in your data add it to your array as you want
}
DispatchQueue.main.async {
self.tableview.reloadData()
}
}
}
}
here i have added listeners to your firestore data so if any new data adds up or any data changes into the database you will receive it in the App & will reflect that changes realtime.
Do one thing follow my comment in the code.
You can try add "addSnapshotListener" to Your "FUNCTION THAT HANDLES GETTING THE DATA FROM FIREBASE".
Let's try add it like this:
func loaddailymotivation() {
FirebaseReferece(.MotivationDAILY).getDocuments.addSnapshotListener { (snapshot, error) in
if let error = error {
print("error getting MOTIVATIONDAILY DATA \(error.localizedDescription)")
}
else {
guard let snapshot = snapshot else { return }
for allDocument in snapshot.documents {
let data = allDocument.data()
print("\(allDocument.documentID) => \(allDocument.data())")
print("we have\(snapshot.documents.count) documents in this array")
let dailymotivationTitle = data["Motivation title"] as! String //calls the data thats heald inside of motivation title in firebase
let dailyMotivationScripture = data["daily motivation scripture"] as! String //calls the data thats heald inside of Motivation script in firebase
let dailyMotivationNumberOfLikes = data["Number of likes in daily motivation post"]as! Int
let newthought = MotivatioNDataModel(RealmotivationTitle: dailymotivationTitle, RealmotivationScrip: dailyMotivationScripture, RealmotivationNumberOfLikes: dailyMotivationNumberOfLikes )
self.motivationThoughts.append(newthought)
}
}
}
I get this error:
This is my code:
I am using refresh in the tableView section of the project. What could be causing this error during the refresh?
But in which phase it falls to the fault I could not solve that part
var kategoriId = ""
var refresher = UIRefreshControl()
var arrayKonularData = [konularData]()
let singleton = konularClass.sharedGlobal
override func viewDidLoad() {
super.viewDidLoad()
refresher.attributedTitle = NSAttributedString(string: "Yükleniyor")
refresher.addTarget(self, action: #selector(KonuDetayViewController.refresh), for: UIControlEvents.valueChanged)
self.tableview.addSubview(refresher)
KonulariGetir(sirala: "order by tarih desc")
navigationController?.delegate = self
tableview.layer.cornerRadius = 10
}
func refresh()
{
DispatchQueue.main.async {
if self.segmentControl.selectedSegmentIndex == 0
{
self.arrayKonularData.removeAll()
self.KonulariGetir(sirala: "order by tarih desc")
}
if self.segmentControl.selectedSegmentIndex == 1
{
self.arrayKonularData.removeAll()
self.KonulariGetir(sirala: "order by indirimpuani desc")
}
}
DispatchQueue.main.async {
self.refresher.endRefreshing()
}
}
I am taking data from web service in this section
func KonulariGetir(sirala:String)
{
var request = URLRequest(url: URL(string:"http://212.xxx.xxx.xxx:7001/IndirimiKovala/KonuGetir")!)
request.httpMethod = "POST"
let postString = "filtre="+sirala
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if error != nil
{
print("error")
}
if let urlContent = data
{
do
{
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
if let gelenDizi = jsonResult as? NSArray
{
for i in 0..<gelenDizi.count
{
if let baslik = (gelenDizi[i] as? NSDictionary)?["baslik"] as? String
{
self.singleton.baslik = baslik
}
if let indirimPuani = (gelenDizi[i] as? NSDictionary)?["indirimpuani"] as? Int
{
self.singleton.indirimPuani = String(indirimPuani)
}
if let konuId = (gelenDizi[i] as? NSDictionary)?["id"] as? Int
{
self.singleton.konuId = String(konuId)
}
if let haberVeren = (gelenDizi[i] as? NSDictionary)?["uye"] as? String
{
self.singleton.haberVerenUye = haberVeren
}
if let gelenTarih = (gelenDizi[i] as? NSDictionary)?["tarih"] as? String
{
self.singleton.tarih = gelenTarih
}
if let gelenAktif = (gelenDizi[i] as? NSDictionary)?["aktif"] as? Int
{
self.singleton.aktif = gelenAktif
}
self.arrayKonularData.append(konularData.init(baslik: self.singleton.baslik, indirimPuani: self.singleton.indirimPuani, konuId: self.singleton.konuId,haberVeren:self.singleton.haberVerenUye , tarih:self.singleton.tarih,aktif:self.singleton.aktif))
}
}
DispatchQueue.main.async {
self.tableview.reloadData()
}
}
catch
{
print("server hatası")
}
}
}
task.resume()
}
I guess the problem comes from the part of code where you try to populate tableview. So the possible solution can be in tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) delegate methode check if arrayKonularData array is not empty like this
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: orderCell, for: indexPath)
if !arrayKonularData.isEmpty {
.....// Your code
}
return cell
}
Another solution (which I thing will be the right solution in your case) add completion function of
func KonulariGetir(sirala:String)
and reload tableview in the completion method
I have a tableview that populates an array. I am able to add to firebase and the tableview reloads to show the newly added object. If I have 5 items in firebase then I would have 5 on the tableview. I am able to remove items from firebase through code and reload the tableview and it works great.
My issue is when I am on the last item on firebase and tableview and I delete that last item, the firebase removes it just fine, but the tableview keeps the last item but grays it out.
The app doesn't crash it just stays there until I add something back in.
Obviously if I tap on that grayed out cell my app crashes because I am tapping an index out of range.
Is there some code that I need to add to prevent this grayed out cell and just have an empty tableview?
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return serviceArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "serviceCell", for: indexPath as IndexPath) as! ServiceTableViewCell
let row = indexPath.row
// cell.serviceLogoImage.image = UIImage.init(named: serviceArray[row].serviceUrl!)
cell.serviceNameLabel.text = serviceArray[row].serviceName
if serviceArray[row].serviceStatus == true {
cell.serviceStatusView.backgroundColor = .green
} else {
cell.serviceStatusView.backgroundColor = .red
}
return cell
}
I must be missing a small conditional because other than this the tableview works perfectly with firebase...
Edit 1
I have added the code used to populate the tableview
var serviceArray: [ServiceClass] = []
func pullCardData() {
serviceArray.removeAll()
let cardRef = ref.child("cards")
cardRef.observeSingleEvent(of: .value, with: { snapshot in
for cards in snapshot.children {
let allCardIDs = (cards as AnyObject).key as String
if allCardIDs == self.cardID {
let thisCardLocation = cardRef.child(self.cardID)
thisCardLocation.observeSingleEvent(of: .value, with: { snapshot in
let thisCardDetails = snapshot as FIRDataSnapshot
let cardDict = thisCardDetails.value as! [String: AnyObject]
self.selectedCard.cardID = thisCardDetails.key
self.selectedCard.nickname = cardDict["nickname"] as! String
self.selectedCard.type = cardDict["type"] as! String
self.cardNickNameLabel.text = cardDict["nickname"] as? String ?? ""
let thisCardServices = self.ref.child("cards").child(self.cardID).child("services")
let serviceRefLoc = self.ref.child("services")
thisCardServices.observeSingleEvent(of: .value, with: {serviceSnap in
if serviceSnap.hasChildren() {
for serviceChild in serviceSnap.children {
let serviceID = (serviceChild as AnyObject).key as String
serviceRefLoc.observeSingleEvent(of: .value, with: {allServiceSnap in
if allServiceSnap.hasChildren() {
for all in allServiceSnap.children {
let allServs = (all as AnyObject).key as String
let thisServiceLocationInServiceNode = self.ref.child("services").child(serviceID)
if serviceID == allServs {
thisServiceLocationInServiceNode.observeSingleEvent(of: .value, with: {thisSnap in
let serv = thisSnap as FIRDataSnapshot
let serviceDict = serv.value as! [String: AnyObject]
let aService = ServiceClass()
self.serviceCurrent = serviceDict["serviceStatus"] as? Bool
self.serviceName = serviceDict["serviceName"] as? String
self.serviceURL = serviceDict["serviceURL"] as? String
self.serviceFixedBool = serviceDict["serviceFixed"] as? Bool
self.serviceFixedAmount = serviceDict["serviceAmount"] as? String
aService.serviceUrl = serviceDict["serviceURL"] as! String
aService.serviceName = serviceDict["serviceName"] as! String
aService.serviceStatus = serviceDict["serviceStatus"] as? Bool
aService.serviceID = serviceID
self.serviceArray.append(aService)
self.tableView.reloadData()
})
}
}
}
})
}
}
})
})
}
}
})
}
Edit 2
I had the idea to check if the firebase node even exits (it shouldn't since I just deleted it. SO
func checkIfDataExits() {
ref.observeSingleEvent(of: .value, with: { snapshot in
if snapshot.hasChild("services") {
self.pullCardData()
} else {
self.tableView.endUpdates()
print("no childen")
}
})
}
As expected since I don't have the firebase node there anymore it prints "no children" but it still shows that last tableview cell....so
Well, my last edit was one line of code off from being what I needed.
Instead of
self.tableView.endUpdates()
I replaced it with
self.tableView.reloadData()
So (without retying that long method) I simply wrote another method
func checkIfDataExits() {
serviceArray.removeAll()
ref.observeSingleEvent(of: .value, with: { snapshot in
if snapshot.hasChild("services") {
self.pullCardData()
} else {
self.tableView.reloadData()
}
})
}
And THIS method decides wether or not to even run that long one
resolve:
serviceArray.removeAll()
Code:
ref.observeSingleEvent(of: .value, with: { snapshot in
if snapshot.hasChild("services") {
self.pullCardData()
} else {
//code here
serviceArray.removeAll()
self.tableView.reloadData()
}
})