What is the best way to use DateFormatters in UITableViews cellForRowAt method

39 Views Asked by At

I have a table view which has few cells. I need to display the date in a human-readable format whose raw format is retrieved from the API call. I am using the below code to call the convertToDate function which takes care of taking raw date string of format "yyyy-MM-dd'T'HH:mm:ssX" and converting it to "MMMM dd, yyyy 'at' hh:mm a". However as we know Date formatters can be expensive. Can anyone please help me answer a few questions looking at the below code implementation?

Note : I am calling the function convertToDate from my cellForRowAt method inside tableView

  1. Is it good practice to create a singleton class so that only one Dateformatter instance is created ( so it is less expensive )

  2. I am also creating a DispatchQueue which is concurrent so that Dateformatter is thread-safe. Is that good practice?

  3. Will calling convertToDate with convertToDate which is concurrent cause any lag in performance when scroll table views?

  4. Are there any standard references to handle/create Dateformatter in general?

Below code converts: "2023-12-22T13:17:27Z" to "December 22, 2023 at 01:17 PM"

Code

import Foundation

class DateUtils {
    
    static let shared = DateUtils()
    let dateFormatter = DateFormatter()
    
    // Date formatter are not thread safe and adding this to a concurrent queue makes if safer.
    // For table views cells this may have minimal impact and needs to have performance tested in different cases where used.
    private let dateFormatterQueue = DispatchQueue(label: "com.example.dateformatter",
                                                   attributes: .concurrent)
    
    private init(){}
    
    func convertToDate(from date: String,
                       with format: String = "yyyy-MM-dd'T'HH:mm:ssX",
                       to: String = "MMMM dd, yyyy 'at' hh:mm a") -> String? {
        
        var formattedDate: String?
        
        dateFormatter.dateFormat = format
        guard let date = dateFormatter.date(from: date) else {
            return nil
        }
        dateFormatter.dateFormat = to
        
        dateFormatterQueue.sync {
            formattedDate = dateFormatter.string(from: date)
        }
        return formattedDate
    }
}
1

There are 1 best solutions below

4
vadian On BEST ANSWER
  1. No, a static property in an extension of DateFormatter would be the better choice.
  2. No.
  3. No.
  4. No Dateformatter needed

The API call returns most likely JSON.

In the model define the property representing the date

let date: Date

and add the .iso8601 strategy to the decoder to convert the ISO string to Date on the fly

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601

In the cell format the date the modern way

cell.date = item.date.formatted( Date.FormatStyle()
                     .year()
                     .month(.wide)
                     .day(.twoDigits)
                     .hour(.twoDigits(amPM: .abbreviated))
                     .minute()
                     .locale(.init(identifier: "en_US")))

The style can also be defined externally.