Search code examples
swiftnotificationstableviewdatasourcedelay

Notification is delayed?


Today I experienced that notifications are delayed.


If I add a new item to a tableView's dataSource array, and I use notifications to update that tableView (insert row) (because it's in another ViewController),

then if I do it 2 times at the same time, my app crashes: because notifications are arriving late.


More details:

I have a TabController:

Chat.shared.receiveMessage { message in
                    
    print("1: received msg, added to array")
    ChatStore.shared.messages.append(message)

    print("2: sending noti")
    NotificationCenter.default.post(name: .updateChatMessages, object: nil)

}

And a ChatMessageViewController that recieves the .updateChatMessages notification

@objc func notification_updateChatMessages(){
        
    DispatchQueue.main.async {
            
        print("3: noti arrived")
        self.tableView.beginUpdates()
        self.tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
        self.tableView.endUpdates()
                
    }
}

The problem is, that Notifications are delayed somehow.

If 2 messages are received at the same thime, then the following happens (output):

1: received msg, added to array

2: sending noti

-

1: received msg, added to array

2: sending noti

-

3: noti arrived

3: noti arrived

The app crashes, because when the first notification arrives, then already both 2 messages were added to the dataSource..

Instead the output should look something like this:

1: received msg, added to array

2: sending noti

3: noti arrived

-

1: received msg, added to array

2: sending noti

3: noti arrived

Are the notifications delayed? How could I fix that?


UPDATE 1: added receiveMessage() to thread as requested

Chat.swift (part where it receives messages)

func receiveMessage(completion: @escaping (String?)->()){
    socket.on("message") {
        (data, socketAck) -> Void in
        guard let dataArray = data[0] as? [String: Any] else {
            return
        }
        guard let message = dataArray["message"] as? String else {
            return
        }
        return completion(message)
    }

}

Solution

  • The issue is most likely with using DispatchQueue.main.async. That will add a closure to be run in the next iteration of the main run loop. If two notifications are fired between runloop iterations then multiple blocks will be queued up and run causing the issue your seeing.

    Theres a couple options that could work

    • Have the code after 3: noti arrived look at the datastore a calculate how many rows it should insert.
    • Have the chat store notify observers in a thread safe manner so that the notification always matches the content of the store.
    • Maybe use DispatchQueue.main.sync instead. This is highly dependant on queue you're receiving socket messages on and could lead to worse issues.