Search code examples
iosobjective-cswiftswift4

High CPU Usage when parsing large json data with Date Strings Swift


I am working on a chat application. I get conversations list containing data related to conversations. Json response looks like below

[
  {
    "id": "1021",
    "title": "Test Group",
    "memberCount": 250,
    "isGroup": true,
    "ownerId": "18646",
    "createdAt": "2017-08-24T12:05:06.000Z",
    "type": "private",
    "lastReadAt": {
      "18646": "2020-01-29T06:48:53.700Z",
      "18664": "2020-01-29T07:12:41.613Z",
      "18714": "2020-01-29T07:12:41.613Z",
      // 247 More Records
    },
    "unreadMessageCount": 0,
    "mute": false,
    "isActive": true,
    "isAdmin": true,
    "isDeleted": false,
    "updatedAt": "2020-01-29T07:12:41.124Z",
    "lastMessage": {
      "id": "18a16164-657c-420a-887d-5408f1479619",
      "conversationId": "1021",
      "type": "normal",
      "createdAt": "2020-01-29T07:12:41.124Z",
      "updatedAt": "2020-01-29T07:12:41.124Z",
      "body": "hi",
      "ownerId": "18646",
      "attachments": [],
      "owner": {
          "id": "18646",
          "displayName": "sydney millers",
          "profileImageUrl": "https://sample-file-url/media/fbef1bb47eb85fb2979b135ed04e0eb3.png"
      },
      "mentionedUsers": []
    }
  },
  {
    "id": "1021",
    "title": "Test Group 2",
    "memberCount": 250,
    "isGroup": true,
    "ownerId": "18646",
    "createdAt": "2017-08-24T12:05:06.000Z",
    "type": "private",
    "lastReadAt": {
      "18646": "2020-01-29T06:48:53.700Z",
      "18664": "2020-01-29T07:12:41.613Z",
      "18714": "2020-01-29T07:12:41.613Z",
      // 247 More Records
    },
    "unreadMessageCount": 0,
    "mute": false,
    "isActive": true,
    "isAdmin": true,
    "isDeleted": false,
    "updatedAt": "2020-01-29T07:12:41.124Z",
    "lastMessage": {
      "id": "18a16164-657c-420a-887d-5408f1479619",
      "conversationId": "1021",
      "type": "normal",
      "createdAt": "2020-01-29T07:12:41.124Z",
      "updatedAt": "2020-01-29T07:12:41.124Z",
      "body": "hi",
      "ownerId": "18646",
      "attachments": [],
      "owner": {
          "id": "18646",
          "displayName": "sydney millers",
          "profileImageUrl": "https://sample-file-url/media/fbef1bb47eb85fb2979b135ed04e0eb3.png"
      },
      "mentionedUsers": []
    }
  }
]

lastReadAt is basically containing info when the group member last read the conversation. Now to get last message status whether it is read by all or not and put it in table cell, I have to find oldest date and compare it with message date. But to compare dates, I have to convert them from String to Date. Now suppose there are 10 conversation containing 250 members each. So there is a lot of task, so my cpu usage goes to 100%. Any better solution for this. For swift I am using below function

func getMessageStatus(itemMessageDate: Date) -> BaseMessageStatus {
        let dateTransformer = ISODateTransform() // Custom Date Transformer Same as of ios default but with a fixed date format.
        if let lastReadInfoDic = self.conversation?.lastReadDictionary {
            var lastReadData = [String:Date]()
            lastReadInfoDic.forEach({(id,date) in
                if let memberReadDate = dateTransformer.transformFromJSON(date) {
                    lastReadData.updateValue(memberReadDate, forKey: id)
                }
            })
            let sortedData = lastReadData.sorted(by: {$0.value < $1.value})
            if let oldestReader = sortedData.first {
                let oldestReadDate = oldestReader.value
                if itemMessageDate <= oldestReadDate {
                    return .seen
                } else {
                    return .sent
                }
            }
        }
        return .sent
    }

Solution

  • If I'm reading this correctly, you want the minimum date in self.conversation?.lastReadDictionary. It looks like lastReadDictionary is [String: String].

    If so, then use compactMapValues(_:) to turn the [String: String] into a [String: Date]. Next, use min(_:) to find the earliest date.

    func getMessageStatus(itemMessageDate: Date) -> BaseMessageStatus {
        let dateTransformer = ISODateTransform()
    
        let oldestReader = conversation?.lastReadDictionary
            .compactMapValues { dateTransformer.transformFromJSON($0) }
            .min { $0.value < $1.value } // NOTE: Comparing Date objects.
    
        guard let oldestDate = oldestReader?.value else {
            return .sent
        }
    
        if itemMessageDate <= oldestDate {
            return .seen
        } else {
            return .sent
        }
    }
    

    Do all the time strings use Z timezone? If so then you can use string compare to find the earliest ISO time string. Once you have the earliest string, then convert that one ISO time string to a Date.

    func getMessageStatus(itemMessageDate: Date) -> BaseMessageStatus {
        let dateTransformer = ISODateTransform()
    
        let oldestElement = conversation?.lastReadDictionary
            .min { $0.value < $1.value } // NOTE: Comparing String objects.
    
        guard let string = oldestElement?.value, let oldestDate = dateTransformer.transformFromJSON(string) else {
            return .sent
        }
    
        if itemMessageDate <= oldestDate {
            return .seen
        } else {
            return .sent
        }
    }