Search code examples
swiftuitableviewfirebase-realtime-databasestateobservers

How to reload UITableView without printing data twice?


I have a single view of SlackTextViewController which works as a UITableView. I'm switching between "states" using a public String allowing it to read 1 set of data in a certain state and another set of data in another state. The problem is when I switch back and forth to the original state it prints the data 2, 3, 4 times; as many as I go back and forth. I'm loading the data from a Firebase server and I think it may be a Firebase issue. Here is my code...

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)

    if chatState == "ALL"
    {
        self.globalChat()
    }else if chatState == "LOCAL"
    {
        self.localChat()
    }
}

override func viewDidDisappear(animated: Bool) {
    self.messageModels.removeAll()
    tableView.reloadData()
    ref.removeAllObservers()
}

func chatACTN()
{
    if chatState == "ALL"
    {
        self.viewWillAppear(true)

    }else if chatState == "LOCAL"
    {
        self.viewWillAppear(true)
    }
}

func globalChat()
{
    self.messageModels.removeAll()
    tableView.reloadData()

    let globalRef = ref.child("messages")
    globalRef.keepSynced(true)
    globalRef.queryLimitedToLast(100).observeEventType(.ChildAdded, withBlock: { (snapshot) -> Void in
        if snapshot.exists()
        {
            let names = snapshot.value!["name"] as! String
            let bodies = snapshot.value!["body"] as! String
            let avatars = snapshot.value!["photo"] as! String
            let time = snapshot.value!["time"] as! Int

            let messageModel = MessageModel(name: names, body: bodies, avatar: avatars, date: time)
            self.messageModels.append(messageModel)
            self.messageModels.sortInPlace{ $0.date > $1.date }
        }
        self.tableView.reloadData()
    })
}

func localChat()
{
    self.messageModels.removeAll()
    tableView.reloadData()

    print("LOCAL")
}

Solution

  • The problem is that each time you call globalChat() you're creating another new observer which results in having multiple observers adding the same items to self.messageModels. Thats why you're seeing the data as many times as you switch to the global state.

    Since you want to clear the chat and load the last 100 each time you switch to global, there's no point in keeping the observer active when you switch to "Local".

    Just remove the observer when you switch to Local, that should fix your problem.


    From firebase docs:

    - (void) removeAllObservers

    Removes all observers at the current reference, but does not remove any observers at child references.

    removeAllObservers must be called again for each child reference where a listener was established to remove the observers.

    So, ref.removeAllObservers() will not remove observers at ref.child("messages") level.


    Using ref.child("messages").removeAllObservers at the beginning of localChat function to remove the observer you created in globalChat would be ok if you're only dealing with this one observer at this level but if you have more on the same level or you think you might add more in the future the best and safest way would be to remove the specific observer you created. To do that you should use the handle that is returned from starting an observer. Modify your code like this:

    var globalChatHandle : FIRDatabaseHandle?
    
    func globalChat()
    {
        self.messageModels.removeAll()
        tableView.reloadData()
        
        ref.child("messages").keepSynced(true)
        globalChatHandle = ref.child("messages").queryLimitedToLast(100).observeEventType(.ChildAdded, withBlock: { (snapshot) -> Void in
            if snapshot.exists()
            {
                let names = snapshot.value!["name"] as! String
                let bodies = snapshot.value!["body"] as! String
                let avatars = snapshot.value!["photo"] as! String
                let time = snapshot.value!["time"] as! Int
                
                let messageModel = MessageModel(name: names, body: bodies, avatar: avatars, date: time)
                self.messageModels.append(messageModel)
                self.messageModels.sortInPlace{ $0.date > $1.date }
            }
            self.tableView.reloadData()
        })
    }
    
    func localChat()
    {
        if globalChatHandle != nil {
            ref.child("messages").removeObserverWithHandle(globalChatHandle)
        }
        
        self.messageModels.removeAll()
        tableView.reloadData()
        
        print("LOCAL")
    }
    

    And in viewDidDisappear method replace this

    ref.removeAllObservers()
    

    with

    ref.child("messages").removeAllObservers()