NSCalendar(calendarIdentifier: calendarName)
can return nil
if calendarName
is not valid - this is the original Objective-C behaviour, and is also true in Swift. However, it appears that the compiler believes the initializer returns an NSCalendar
rather than an NSCalendar?
, as follows:
let c1 = NSCalendar(calendarIdentifier: "gregorian")// _NSCopyOnWriteCalendarWrapper
let c2 = NSCalendar(calendarIdentifier: "buddhist")// _NSCopyOnWriteCalendarWrapper
//let c3:NSCalendar = NSCalendar(calendarIdentifier: "rubbish") // run-time error
let c3:NSCalendar? = NSCalendar(calendarIdentifier: "rubbish") // nil
So if the initializer can return nil, my understanding is that I should be able to do
if let c4 = NSCalendar(calendarIdentifier: "rubbish") as? NSCalendar {
//error: conditional downcast from 'NSCalendar' to 'NSCalendar' always succeeds
}
However, this is a compile-time error as shown.
What am I misunderstanding here, and how can I safely test that a named calendar actually exists?
Note: The following only applies to Swift 1.0. In Swift 1.1, there are failable initializers.
This works:
if let c4 = NSCalendar(calendarIdentifier: "rubbish") as NSCalendar? {
}
In Swift 1.0, there is a known issue in the Xcode release notes about Swift not supporting Objective-C initializers that return nil
. Basically, what is happening is that according to the Swift language, the expression NSCalendar(...)
has type NSCalendar
, which is a non-optional type (cannot be nil
). However, this is really an imported initializer from Objective-C, where it can return nil
.
So what currently happens is that when you call this and it returns nil
, you have a value that at runtime is nil
, but Swift thinks is a non-optional NSCalendar
(which cannot be nil
). This is a really bad situation in which you have a value that is not possible for the type. The Xcode release notes mention a "workaround" where you convert the result of the initializer to an optional type before using it. The reason that this works is that at runtime, both optional and non-optional object pointers are represented as simple pointers, where nil
object pointer is the null pointer, and non-optional ones are assumed to not be null pointers. The operation of converting from a non-optional to optional object pointer is a simple assignment (in both cases it's a non-null pointer). But in the case where the value is nil
, a simple assignment turns it into a (valid) nil
value of optional type. So everything is happy.
But if you don't convert it to optional first, all hell breaks loose, as you have a value that is not supposed to be possible for that type.
Your attempt to downcast using as?
from NSCalendar
to NSCalendar
isn't allowed, because a cast from NSCalendar
to NSCalendar
cannot fail (theoretically).