In several places here, it has been suggested that using a computed property within an extension of NSDate
might a good way to obtain a string version of a date via a NSDateFormatter
, like so:
extension NSDate {
public var UTC : String {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
formatter.timeZone = NSTimeZone(abbreviation: "UTC")
return formatter.stringFromDate(self)
}
}
However, allocating a NSDateFormatter
is expensive and it is suggested that they be created once and cached. The above code creates the NSDateFormatter
every time a date is formatted, and I'm wondering if there is a way to create the NSDateFormatter
once inside the extension for reuse?
Obviously, I could create it just once outside the extension, but that seems to defeat the encapsulation that characterizes classes.
I am reminded of: https://xkcd.com/1179/ !!
You create static variable by appending static keyword in front of your variable declaration. We will be using playground to explore more. When we define any variable as let, it means it's values cannot be modified, On the other hand if we define any variable as var it means it's values can be modified.
An extension method must be defined in a top-level static class. An extension method with the same name and signature as an instance method will not be called. Extension methods cannot be used to override existing methods. The concept of extension methods cannot be applied to fields, properties or events.
Creating an extension in Swift Creating extensions is similar to creating named types in Swift. When creating an extension, you add the word extension before the name. extension SomeNamedType { // Extending SomeNamedType, and adding new // functionality to it. }
Swift allows us to use a static prefix on methods and properties to associate them with the type that they're declared on rather than the instance. We can also use static properties to create singletons of our objects which, as you have probably heard before is a huge anti-pattern.
You can add static members to class extensions just the same as on classes. You need to prefix the class name to the static member name when you use it, e.g. NSDate.dateFormatterUTC
, even if you’re using it in the same class.
This works:
extension NSDate {
private static let dateFormatterUTC: NSDateFormatter = {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
formatter.timeZone = NSTimeZone(abbreviation: "UTC")
return formatter
}()
public var UTC : String {
return NSDate.dateFormatterUTC.stringFromDate(self)
}
}
It’s also not the worst thing in the world just to use a private constant:
private let dateFormatterUTC: NSDateFormatter = {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ"
formatter.timeZone = NSTimeZone(abbreviation: "UTC")
return formatter
}()
extension NSDate {
public var UTC : String {
return dateFormatterUTC.stringFromDate(self)
}
}
This is not significantly worse than the static class member, because Swift’s private
is file-private, not type-private. These two declarations of dateFormatterUTC
have the same scope. Even in the first example, NSDate.dateFormatterUTC
is accessible throughout the entire file it’s declared in.
I do agree that the static version is preferable, but for stylistic reasons only: I like the way it’s indented right next to the thing that uses it.
As Gwendal wisely notes above, this approach assumes UTC
will only ever be called from one thread. Although static let
and global let
are both thread-safe in Swift, the Looks like it’s threadsafe starting in iOS 7. Phew.NSDateFormatter
class is not!
Still, always good to keep a thread safety warning next to any mention of singletons. If you do want to use a non-threadsafe helper object from multiple threads, consider either creating a new helper on every call, or using NSThread.currentThread().threadDictionary
to create a per-thread instance. Be sure to do a little profiling to make sure you’re actually solving a performance problem before opting for the more complex thread-local option.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With