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?
Even though it looks like the initialiser should return a full NSCalendar object, it appears as though it behaves as an implicit optional (NSCalandar!). If you look at the object in the debugger it comes up as an NSCalendar:
(NSCalendar) $R1 = 0x0000000000000000 { ObjectiveC.NSObject = parent is NULL }
Even more strange is that the following code does not produce any runtime errors, when accessing a nil NSCalendar - at least for me:
let x = NSCalendar(calendarIdentifier: "asdasda")
let y = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
if (x.isDateInToday(NSDate())) {
println("x works")
}
if (y.isDateInToday(NSDate())) {
println("y works")
}
if (x == nil) {
println("x not calendar")
}
if (y == nil) {
println("y not calendar")
}
For me this outputs 'y works' and 'x is not a calendar'. This would in pure Swift have a runtime error if using a nil implicit optional but pqnet points out that calling a method on a nil pointer in objective c doesn't produce an error, it is just ignored - which is what seems to be doing. So strange, but it does appear in this case safe to do this:
let x = NSCalendar(calendarIdentifier: "asdasda")
if (x != nil) {
//do what you need with the calendar
}
However a more Swift way would be to use an explicit optional - i.e. do this:
let x : NSCalendar? = NSCalendar(calendarIdentifier: "asdasda")
if let cal = x {
//do what you need with the calendar as cal
}
You could also use an implicit optional let x : NSCalendar =...
and check with if x != nil
as above - your choice, it seems sometimes different contexts cause implicit or explicit optionals...
Edit:
From the Xcode release notes:
Swift does not support object initializers that fail by returning null. (16480364)! Workaround: If there is a factory method, use it instead. Otherwise, capture the result in an optional. For example:
let url: NSURL? = NSURL(string: "not a url")
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