1

Is it safe to assume that the following will work on all devices?

let cal = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!

I'm no expert on app localization (or anything tbh), so I'm not sure if the Gregorian calendar will be available on every device. The way the app uses this calendar is not user facing, so I don't really care which calendar the user is using, I just want to be sure that the above initialization will always work.

JAL
  • 41,701
  • 23
  • 172
  • 300
TimSim
  • 3,936
  • 7
  • 46
  • 83
  • 2
    is there any problem using `guard let cal = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian) else { return }` – Leo Dabus Apr 24 '16 at 20:40
  • Well the app uses the calendar to compute some stats (that the user won't see, just for app optimization and analytics), so if the right calendar is not available, that's a problem. I can't simply say never mind, don't do all that processing then. – TimSim Apr 24 '16 at 20:44
  • Just use current calendar – Leo Dabus Apr 24 '16 at 20:45
  • But what if it's some crazy druid calendar or whatever (I don't even know what kind of calendars are there)? I don't know how they work and how many months they have and what have you, I need to use the standard Gregorian calendar. – TimSim Apr 24 '16 at 20:46
  • so just use the guard – Leo Dabus Apr 24 '16 at 20:47
  • 1
    If a given method returns an optional, then you should always assume that it *could* return nil. Even if it doesn't return nil now, it might do in future updates. Designing your code around the how methods are defined, rather than guessing at their internal implementation is always the better way to go. If your app can't recover from a given optional being nil, it's still better to handle that case yourself, allowing you to print your own error message or throw an error etc. This will allow you to debug much more easily. – Hamish Apr 24 '16 at 20:47
  • 1
    Maybe Apple just didn't rewrite this initializer yet to return non-optional value, maybe sometimes it really returns `nil`. I think if your app can't proceed without a calendar, it's normal to use force unwrapping. – Alexander Doloz Apr 24 '16 at 20:47
  • 1
    @TimSim you can do whatever you need before returning from the guard statement in case of failure – Leo Dabus Apr 24 '16 at 20:48
  • 1
    @LeoDabus but if there's a possibility of failure (and the optional return value suggests that there is), I have to redesign a big part of my app to assume that some devices don't know there are 7 days in a week or 12 months in a year. But what if that optional value is there just because the `calendarIdentifier` is a string, and there won't be a failure if I use the defined `NSCalendarIdentifierGregorian` constant? – TimSim Apr 24 '16 at 20:52
  • 1
    It will never fail if you pass the correct calendar identifier – Leo Dabus Apr 24 '16 at 20:54
  • Yes, that's what I wanted to know. :) What's going to happen is I'm just going to forced-unwrap-it, test it (obviously), and if users in Narnia start reporting crashes, I'll deal with it later. – TimSim Apr 24 '16 at 20:57

1 Answers1

1

Nothing is ever 100% safe in programming, especially when dealing with an SDK where the implementation is hidden. I answered a similar question about the constant NSNotFound here.

Is NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)! safe? Ideally, if Apple choses not to change their SDK, or if they provide a bug-free implementation of the NSCalendar initializer. As far as defensive coding goes, you have two options.

Wrap the code in a guard as Leo Dabus suggested in the comments:

guard let cal = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian) else { return }

For future-proofing, Apple may change the availability or implementation of the NSCalendarIdentifier constants (as seen with NSNotFound). For added safety, check the memory location of the constant:

if let str: String = NSCalendarIdentifierGregorian {
    if let _: UnsafePointer<Void> = unsafeAddressOf(str) {
        // NSCalendarIdentifierGregorian is defined on your platform
    }
}

It's a little bit cleaner in Objective-C:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wtautological-compare"
BOOL found = (&NSCalendarIdentifierGregorian != NULL);
#pragma clang diagnostic pop
if (found) {
    // safe
}

I do not work for Apple and cannot guarantee the availability of any Foundation classes.

Community
  • 1
  • 1
JAL
  • 41,701
  • 23
  • 172
  • 300
  • Thanks, but the problem is not that the app will crash if nil is returned (I know how to `guard` the code so it doesn't crash), but that I will have to redesign some significant functionality the app if the Gregorian calendar can't be instantiated on all devices. I might have phrased the question wrong. It should have been Can a Gregorian NSCalendar be instantiated on all devices or not? – TimSim Apr 24 '16 at 22:22
  • can i set globally gregorian. my app is old and I have big project.. and I have used calendar so many places.. Is it possible to method swizzling or something else. – Maulik shah Jun 30 '20 at 08:37