Search code examples
swiftwatchkitdateformatter

Include a date converter function inside of a DateFormatter extension


The extension formats the API date then the converter function converts it to the formatting I like.

The code works, but the call seems verbose. Is this something that can be refactored? Can I combine the two?

It seemed logical to make the DateFormatter of the convertDateFormater function the iso8601Full property. There were no errors, but the attempted call didn't work.

Please post an answer with code so I can mark it correct.

extension DateFormatter {
    static let iso8601Full: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
        formatter.calendar = Calendar(identifier: .iso8601)
        formatter.timeZone = TimeZone(secondsFromGMT: 0)
        formatter.locale = Locale(identifier: "en_US_POSIX")
        return formatter
    }()
}

    func convertDateFormater(date: String) -> String {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
        let date = dateFormatter.date(from: date)
        dateFormatter.dateFormat = "dd/MM/yyyy"
        return  dateFormatter.string(from: date!)
    }

call

row.creationDateLabel.setText("\(convertDateFormater(date: DateFormatter.iso8601Full.string(from: value.creationDate)))")

Solution

  • You are creating date formatters inside your method, which can be expensive. You might want to save both inside static’s:

    extension DateFormatter {
        static let iso8601Full: DateFormatter = {
            let formatter = DateFormatter()
            formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
            formatter.calendar = Calendar(identifier: .iso8601)
            formatter.timeZone = TimeZone(secondsFromGMT: 0)
            formatter.locale = Locale(identifier: "en_US_POSIX")
            return formatter
        }()
    
        static let dateOnly: DateFormatter = {
             let formatter = DateFormatter()
             formatter.dateFormat = "dd/MM/yyyy"
             return formatter
        }()
    
        static func string(iso string: String) -> String {
            let date = DateFormatter.iso8601Full.date(from: string)!
            return  DateFormatter.dateOnly.string(from: date)
        }
    }
    

    And then you can do:

    row.creationDateLabel.setText(DateFormatter.string(iso: value.creationDate))
    

    This is both more concise and avoids redundant instantiation of formatters.

    Personally, rather than using dateFormat inside dateOnly, I’d use dateStyle, so the date string (presumably shown in the UI) is properly localized, but you can do whatever you want.

    (By the way, I typed this in on my phone, so I apologize if there are any syntax errors, but hopefully this illustrates the basic idea.)


    If creationDate is already a Date then you don’t need any of that iso-specific stuff. I was basing my answer upon your method that took a String as a parameter. If it’s already a Date then it’s just:

    row.creationDateLabel.setText(DateFormatter.dateOnly.string(from: value.creationDate))