Приложение зависает при получении объемных данных от Socket.iOS |Socket.io |RealmSwift - PullRequest
6 голосов
/ 02 апреля 2019

Я разрабатываю приложение для чата, где я мог бы получать несколько сообщений одновременно, что приводит к зависанию приложения. Ниже приведен мой сокет приемник:

func receiveNewDirectMessages() {
    self.socket?.on(EventListnerKeys.message.rawValue, callback: { (arrAckData, ack) in
        print_debug(arrAckData)
        guard let dictMsg = arrAckData.first as? JSONDictionary else { return }
        guard let data = dictMsg[ApiKey.data] as? JSONDictionary else { return }
        guard let chatData = data[ApiKey.data] as? JSONDictionary else { return }
        guard let messageId = chatData[ApiKey._id]  as? String , let chatId = chatData[ApiKey.chatId] as? String else { return }
        if MessageModel.getMessageModel(msgId: messageId) != nil { return }
        let isChatScreen = self.isChatScreen
        let localMsgId = "\(arc4random())\(Date().timeIntervalSince1970)"
        if let senderInfo = data[ApiKey.senderInfo] as? JSONDictionary, let userId = senderInfo[ApiKey.userId] as? String, userId != User.getUserId() {
            _ = AppUser.writeAppUserModelWith(userData: senderInfo)
        }
        let msgModel = MessageModel.saveMessageData(msgData: chatData, localMsgId: localMsgId, msgStatus: 2, seenByMe: false)
        let chatModel = ChatModel.saveInboxData(localChatId: msgModel.localChatId, inboxData: chatData)
        if isChatScreen {
            self.emitMessageStatus(msgId: messageId, chatId: chatId, socketService: .messageStatus, status: .delivered)
            self.emitMessageStatus(msgId: messageId, chatId: chatId, socketService: .messageStatus, status: .seen)
        } else {
            ChatModel.updateUnreadCount(localChatId: chatModel.localChatId, incrementBy: 1)
            self.emitMessageStatus(msgId: messageId, chatId: chatId, socketService: .messageStatus, status: .delivered)
        }
        TabController.shared.updateChatBadgeCount()
    })
}

Что происходит выше: 1. Я получаю все недоставленные сообщения ОДИН-О-ОДИН в этом слушателе сокета. 2. Извлечение данных сообщения 3. Сохранение полученной информации об отправителе в Realm DB 4. Сохранение модели сообщения в БД области 5. СОХРАНЕНИЕ / ОБНОВЛЕНИЕ Тема чата в БД областей 6. Передача подтверждения для полученного сообщения 7. Обновить счетчик чата на панели вкладок

Ниже мой отправитель для подтверждения доставки сообщения.

 func emitMessageStatus(msgId: String, chatId: String, socketService: SocketService, status: MessageStatusAction) {

    // Create Message data packet to be sent to socket server
    var msgDataPacket = [String: Any]()
    msgDataPacket[ApiKey.type] = socketService.type
    msgDataPacket[ApiKey.actionType] = socketService.listenerType
    msgDataPacket[ApiKey.data] = [
        ApiKey.messageId: msgId,
        ApiKey.chatId: chatId,
        ApiKey.userId: User.getUserId(),
        ApiKey.statusAction: status.rawValue
    ]
    // send the messsage  data packet to socket server & wait for the acknowledgement
    self.emit(with: EventListnerKeys.socketService.rawValue, msgDataPacket) { (arrAckData) in
        print_debug(arrAckData)
        guard let dictMsg = arrAckData.first as? JSONDictionary else { return }
        if let msgData = dictMsg[ApiKey.data] as? [String: Any] {
            // Update delivered Seen Status here
            if let msgId = msgData[ApiKey.messageId] as? String, let actionType = msgData[ApiKey.statusAction] as? String, let msgStatusAction = MessageStatusAction(rawValue: actionType) {
                switch msgStatusAction {
                case .delivered:
                    if let deliveredTo = msgData[ApiKey.deliveredTo] as? [[String: Any]] {
                        _ = MessageModel.updateMsgDelivery(msgId: msgId, deliveredTo: deliveredTo)
                    }
                case .seen:
                    if let seenBy = msgData[ApiKey.seenBy] as? [[String: Any]] {
                        _ = MessageModel.updateMsgSeen(msgId: msgId, seenBy: seenBy)
                    }
                case .pin:
                    MessageModel.clearPinnedMessages(chatId: chatId)
                    if let pinTime = msgData[ApiKey.pinTime] as? Double {
                        MessageModel.updatePinnedStatus(msgId: msgId, isPinned: true, pinTime: pinTime)
                    }
                case .unPin:
                    if let pinTime = msgData[ApiKey.pinTime] as? Double {
                        MessageModel.updatePinnedStatus(msgId: msgId, isPinned: false, pinTime: pinTime)
                    }
                case .delete:
                    MessageModel.deleteMessage(msgId: msgId)
                case .ackMsgStatus, .like, .unlike:
                    break
                }
            }
        }
    }
}

Что происходит выше:

  1. Инкапсуляция всей связанной информации для подтверждения события
  2. Обновление базы данных области после доставки подтверждения

Теперь я не могу игнорировать идеальную политику потоков. Что писать в фоновом потоке, а что писать в главном потоке. Однако я попытался сделать это, но это приводит к случайным сбоям или потерям пакетов.

Может кто-нибудь, пожалуйста, ведите меня вперед по этой теме. Буду очень признателен.

1 Ответ

2 голосов
/ 12 апреля 2019
  1. Попробуйте использовать фоновый поток для обработки данных / обработки без пользовательского интерфейса.
  2. Уменьшить количество обновлений интерфейса пользователя

    Вместо обработки сообщения 1 на 1, используя debounce-like. Вы можете хранить новые сообщения, а затем обновлять пользовательский интерфейс с новыми сообщениями. Таким образом, вместо обновления пользовательского интерфейса / сохранения данных в 100 дБ для 100 сообщений, вы можете сделать это 1 раз для 100 сообщений. Более подробно: с каждым новым сообщением добавляйте его в массив. Позвоните дебоунсеру. Debouncer будет задерживать вызов функции, и каждый раз, когда он вызывается, он будет задерживать предыдущий вызов до тех пор, пока не истечет время задержки. Так, например, через 200 мсек, если нет нового сообщения, будет вызываться функция обновления (функция обратного вызова, которая обрабатывает debounce). Затем обновите ui / db с новыми сохраненными сообщениями.

    Вы можете группировать сообщения по времени, например, по 1 часу. А затем обновить с задержкой между каждой временной группой. Вы можете сделать это, когда вызывается debouncer -> группировать сообщения по времени -> обновлять db / ui для каждой группы. Вы можете использовать setTimeout, например, группу обновлений 1, через 100 мс позже группу обновлений 2, чтобы пользовательский интерфейс не зависал

...