ChatViewController / Socket - Frezee UI - Swift

Well I’m working on a ChatViewController, My UI is freeze when a receive a message via socket and try to reload the data (before reload, I add the message to an array and adapt the array to an array of dictionary to apply the section - separate the rows by date).

Example Code (Excerpt):

SocketManager: (Here, I have a listener to message that I receive as a Dictionary, and well I create a message object and send to the delegate)

socket!.on("message") { (data:[AnyObject], emitter:SocketAckEmitter) -> Void in
 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { () -> Void in
        if let messageDict = data.first as? NSDictionary{
            var accountId = 0
            if let accountIdTmp = messageDict.objectForKey("accountId") as? Int{
                accountId = accountIdTmp
            }

            var firstName = ""
            if let firstNameTmp = messageDict.objectForKey("firstName") as? String{
                firstName = firstNameTmp
            }

            var lastName = ""
            if let lastNameTmp = messageDict.objectForKey("lastName") as? String{
                lastName = lastNameTmp
            }

            var tableId = 0
            if let tableIdTmp = messageDict.objectForKey("tableId") as? Int{
                tableId = tableIdTmp
            }

            var body = ""
            if let bodyTmp = messageDict.objectForKey("body") as? String{
                body = bodyTmp
            }

            let account = Account(identifier: "\(accountId)", name: firstName, lastName: lastName)

            let message = Message(identifier: 0, created: NSDate(), tableId: tableId, body: body, accountId: accountId, account: account)

            self.chatDelegate?.recievedMessage(message)
        }

    })  
}

MessageAdapter: (Here, I only show the method adaptMessageToTime, based on an array of Messages convert to an array of dictionary)

Example

Input: [MessageObject1,MessageObject2,MessageObject3]

Ouput: [[“date”:“10/03/2016”,“messages”:[MessageObject1,MessageObject2]],[“date”:“Today”,“messages”:[MessageObject3]]]

func adaptMessageToTime()->[[String:AnyObject]]{

let formatter = NSDateFormatter()
formatter.dateFormat = "dd/MM/yyyy"
var containerMessages: [[String:AnyObject]] = []

for message in messages{
    let key = Util.isDateInTodayRange(message.created) ? "Today" : formatter.stringFromDate(message.created)
    var hasKey = false

    for var (index, containerMessage) in containerMessages.enumerate(){
        if containerMessage["date"] as? String == key{
            hasKey = true
            if var messageList = containerMessage["messages"] as? [Message]{
                messageList.append(message)
                containerMessage["messages"] = messageList
                containerMessages[index] = containerMessage
            }
        }
    }

    if !hasKey{
        containerMessages.append(["date":key,"messages":[message]])
    }
}

return containerMessages
}

ChatViewController (Extension ChatDelegate): (Here, I show the delegate that receive the message, and that’s where a think is degrading the performance and freeze the UI, But I’m calling from the main thread the part of update, also I put the adaptMessageToTime method in a BackgroundQueue because that part is a consuming task, ex. It can be 100000 messages and I’m recreating the array of dictionary).

extension ChatViewController : ChatDelegate{

func recievedMessage(message: Message) {
    if let tableMessage = self.tableMessage where tableMessage.identifier == message.tableId{
        let backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)
        dispatch_async(backgroundQueue, { () -> Void in
            self.messageAdapter?.addMessage(message)
            if let messsagesByTimeTmp = self.messageAdapter?.adaptMessageToTime(){
                self.messagesByTime = messsagesByTimeTmp
                dispatch_async(dispatch_get_main_queue(), { () -> Void in
                    self.tblMessages.reloadData()
                    self.scrollToBottom()
                })
            }
        })
    }
}

}

Does it always freeze when a message comes in?

If not, I would look at printing a diagnostic message from the various functions as they execute. Is it possible that you need to make the changes to the table’s underlying data atomic? My first suspicion is that the data may be being modified as the table updates after reloadData().

Hi @aeberbach, thank for you reply.

Yes, always when a message arrive is freeze. Because for example, I can call reloadData and I don’t get the freeze. Example, If User A is Typing, and User B send a message, when User A receive the message the app Freeze (the keyboard typing freeze by some seconds and also the scrolling of TableView), but that occur in the recievedMessage(message:Message), because if I comment out this part, I don’t have problem (but don’t have chat).

Is this still a problem? I am having trouble imagining what could be going on to cause the freeze. I see that you have an explicit dispatch_get_main_queue() there or I would be almost sure it was due to UIKit on background thread.

Hi @aeberbach
The problem still persist, I left the functionality of the chat for today (to advance another aspect of the project), but I can figure out what it’s the problem (cuz I think I’m running on the Main Thread the update of UI), I’m going to create a project from scratch to this feature.

Also, when a send multiples messages from another device, the method scrollToBottom (here I call scroll to indexPath in UITableView) is crashing the app, like if it’s calling but don’t have the last data yet! It’s weird.

My attack on the problem would probably be to simplify the action of receivedMessage. I can’t point to an actual problem but the calling of this delegate method from a background thread, which then dispatches a block on another background thread, which then dispatches reloadData/scrollToBottom on the main thread makes me uneasy.
(Also for completeness the code in scrollToBottom is not shown)