Creating an NSDateFormatter in Swift? [closed]

2019-01-11 13:21发布

问题:

I am new to swift and am very unfamiliar with Objective-C. Could someone help me convert this to Swift? I got this code from Ray Wenderlich's best iOS practices - http://www.raywenderlich.com/31166/25-ios-app-performance-tips-tricks

Where would you put this code? Would it go in a class file full of global variables?

- (NSDateFormatter *)formatter {
    static NSDateFormatter *formatter;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _formatter = [[NSDateFormatter alloc] init];
        _formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy"; // twitter date format
    });
    return formatter;
}

回答1:

  1. If you're really looking for direct analog to the method in your question, in Swift 3 you could do something like:

    class MyObject {
    
        // define static variable
    
        private static let formatter: DateFormatter = {
            let formatter = DateFormatter()
            formatter.dateFormat = "EEE MMM dd HH:mm:ss Z yyyy"
            return formatter
        }()
    
        // you could use it like so
    
        func someMethod(date: Date) -> String {
            return MyObject.formatter.string(from: date)
        }
    }
    

    Or in Swift 2:

    class MyObject {
    
        // define static variable
    
        private static let formatter: NSDateFormatter = {
            let formatter = NSDateFormatter()
            formatter.dateFormat = "EEE MMM dd HH:mm:ss Z yyyy"
            return formatter
        }()
    
        // you could use it like so
    
        func someMethod(date: NSDate) -> String {
            return MyObject.formatter.stringFromDate(date)
        }
    }
    

    The static properties, like globals, enjoy dispatch_once behavior for their default values. For more information, see the dispatch_once discussion at the end of the Files and Initialization entry in Apple's Swift blog.

  2. Regarding best practices with date formatters, I would suggest:

    • Yes, it is prudent to not unnecessarily create and destroy formatters that you're likely to need again. In WWDC 2012 video, iOS App Performance: Responsiveness, Apple explicitly encourages us to

      • Cache one formatter per date format;

      • Add observer for NSLocale.currentLocaleDidChangeNotification in Swift 3 (NSCurrentLocaleDidChangeNotification in Swift 2) through the NSNotificationCenter, and clearing/resetting cached formatters if this occurs; and

      • Note that resetting a format is as expensive as recreating, so avoid repeatedly changing a formatter's format string.

      Bottom line, reuse date formatters wherever possible if you're using the same date format repeatedly.

    • If using NSDateFormatter to parse date strings to be exchanged with a web service (or stored in a database), you should use en_US_POSIX locale to avoid problems with international users who might not be using Gregorian calendars. See Apple Technical Q&A #1480. (Or in iOS 10 and macOS 10.12, use ISO8601DateFormatter.)

      But when using dateFormat with NSDateFormatter/DateFormatter, use the current locale for creating strings to be presented to the end user, but use en_US_POSIX local when creating/parsing strings to be used internally within the app or to be exchanged with a web service.

    • If formatting date strings for the user interface, localize the strings by avoiding using string literals for dateFormat if possible. Use dateStyle and timeStyle where you can. And if you must use custom dateFormat, use templates to localize your strings. For example, rather than hard-coding E, MMM d, in Swift 3, you would use dateFormat(fromTemplate:options:locale:):

      formatter.dateFormat = DateFormatter.dateFormat(fromTemplate: "EdMMM", options: 0, locale: Locale.current)
      

      Or in Swift 2, dateFormatFromTemplate(_:options:locale:):

      formatter.dateFormat = NSDateFormatter.dateFormatFromTemplate("EdMMM", options: 0, locale: NSLocale.currentLocale())
      

      This will automatically show, for example, "Mon, Sep 5" for US users, but "Mon 5 Sep" for UK users.

    • The NSDateFormatter is now thread safe on iOS 7 and later and 64-bit apps running on macOS 10.9 and later.



回答2:

As Rob said, you must be careful with performance creating DateFormatters, use the tips from that WWDC session.

I am doing this as extensions of NSDate and NSDateFormatter (code tested in Swift 2, 3 and 4):

extension NSDate {
    func PR2DateFormatterUTC() -> String {
        return NSDateFormatter.PR2DateFormatterUTC.stringFromDate(self)
    }
    //... more formats
}

extension NSDateFormatter {
    fileprivate static let PR2DateFormatterUTC: NSDateFormatter = {
        let formatter = NSDateFormatter()
        let timeZone = NSTimeZone(name:"UTC")
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
        formatter.timeZone = timeZone
        return formatter
    }()
    //... more formats
}

Usage:

    let dateStringUTC  = NSDate().PR2DateFormatterUTC()


回答3:

I've tried to apply the Singleton approach found here. This creates a singleton that could be loaded up with different the different formats you use within your application. For example, you could access it anywhere in your application with formatter.fromDateTime(myString)

let formatter = FormatterFormats()

class FormatterFormats {
    var dateTime: NSDateFormatter = NSDateFormatter()

    class var sharedInstance:FormatterFormats {
        return formatter
    }

    func fromDateTime(dateString: String) -> NSDate {
        return dateTime.dateFromString(dateString)!
    }

    init() {
        dateTime.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
    }

}


回答4:

You could do it like that and move the NSDateFormatter and the onceToken outside your function. Then you can do it like in your example:

var theFormatter:NSDateFormatter!
var token: dispatch_once_t = 0
func formatter() ->NSDateFormatter {
    dispatch_once(&token) {
        theFormatter = NSDateFormatter()
        theFormatter.dateFormat = "EEE MMM dd HH:mm:ss Z yyyy"
    }
    return theFormatter

}

But as David suggested, you should take a look at the dispatch_once singleton question.