0

I have a crash trying to add a nil date in an NSDictionary.

My code looks like this:

625  NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
626  NSCalendar *currentCalendar = [NSCalendar autoupdatingCurrentCalendar];
627  NSDate *todayDate = [NSDate date];
628
629  NSDateComponents *dateComponents = [currentCalendar components:NSUIntegerMax fromDate:todayDate];
630
631  NSMutableArray *lastDates = [[NSMutableArray alloc] init];
632
633  dateFormatter.dateFormat = @"y-M-d";
634
635  for (NSInteger i = 0; i < 7; i++) {
636      NSDate *date = [currentCalendar dateFromComponents:dateComponents];
637
638      dateComponents.day--;
639
640      [lastDates addObject:[dateFormatter stringFromDate:date]];
641  }
642
643  NSString *query = @"SELECT date FROM table";
644  NSArray *completedDates = [ZeeSQLiteHelper readQueryFromDB:query];
645
646  NSMutableDictionary *completedLessons = [[NSMutableDictionary alloc] init];
647
648  for (NSString *date in lastDates) {
649      BOOL completed;
650
651      if ([completedDates containsObject:@{ @"date" : date }]) {
652          completed = YES;
653   } else {
654       completed = NO;
655   }
656
657   completedLessons[[dateFormatter dateFromString:date]] = @(YES); // crash
658  }

For some reason, sometimes, dateFromString: returns nil and it crashes.

Crash report:

0     CoreFoundation      0    x231d068b    __exceptionPreprocess + 124
1     libobjc.A.dylib     0    x3458ae17    objc_exception_throw + 36
2     CoreFoundation      0    x230ec8fb    -[__NSDictionaryM setObject:forKey:] + 840
3     MyApp               0    x00148df7    -[MyClass myMethod] (MyClass.m:657)
4     MyApp               0    x001406f3    -[MySecondViewController mySecondVCMethod] (MySecondViewController.m:250)
5     MyApp               0    x0013f8ed    -[MySecondViewController viewDidLoad] (MySecondViewController.m:92)
6     UIKit               0    x2729443d    -[UIViewController loadViewIfRequired] + 1106
7     UIKit               0    x27293fcd    -[UIViewController view] + 22
8     UIKit               0    x27abb559    -[_UIFullscreenPresentationController _setPresentedViewController:] + 70
9     UIKit               0    x275c9339    -[UIPresentationController initWithPresentedViewController:presentingViewController:] + 110
10    UIKit               0    x275ec473    -[UIViewController _presentViewController:withAnimationController:completion:] + 3536
11    UIKit               0    x275eeac5    __62-[UIViewController presentViewController:animated:completion:]_block_invoke + 334
12    UIKit               0    x275eed31    -[UIViewController _performCoordinatedPresentOrDismiss:animated:] + 414
13    UIKit               0    x27388701    -[UIViewController presentViewController:animated:completion:] + 142
14    MyApp               0    x00223429    -[MyFirstViewController myFirstVCSecondMethod] (MyFirstViewController.m:775)
15    MyApp               0    x00221edb    -[MyFirstViewController myFirstVCFirstMethod:] (MyFirstViewController.m:631)
16    UIKit               0    x272ca795    -[UIApplication sendAction:to:from:forEvent:] + 78
17    UIKit               0    x272ca725    -[UIControl sendAction:to:forEvent:] + 62
18    UIKit               0    x272b354b    -[UIControl _sendActionsForEvents:withEvent:] + 444
19    UIKit               0    x272ca085    -[UIControl touchesEnded:withEvent:] + 602
20    UIKit               0    x27286d33    _UIGestureRecognizerUpdate + 10768
21    UIKit               0    x272c36f9    -[UIWindow _sendGesturesForEvent:] + 902
22    UIKit               0    x272c2e43    -[UIWindow sendEvent:] + 620
23    UIKit               0    x272947e5    -[UIApplication sendEvent:] + 202
24    UIKit               0    x27292fdf    _UIApplicationHandleEventQueue + 4932
25    CoreFoundation      0    x23193c3f    __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 12
26    CoreFoundation      0    x2319382d    __CFRunLoopDoSources0 + 450
27    CoreFoundation      0    x23191b9b    __CFRunLoopRun + 792
28    CoreFoundation      0    x230e5249    CFRunLoopRunSpecific + 518
29    CoreFoundation      0    x230e5035    CFRunLoopRunInMode + 106
30    GraphicsServices    0    x2c1c8ad1    GSEventRunModal + 158
31    UIKit               0    x272fa899    UIApplicationMain + 142
32    MyApp               0    x00152f83    main (main.m:16)
33    libdyld.dylib       0    x34cd6873    start + 0

Also, I am 99% sure that this occurs in Brazil, on 18 October, where Daylight Saving Time occured.

Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
  • 1
    Try printing your date string and verify if it's format is same as format you using i.e. y-M-d – rptwsthi Oct 27 '15 at 08:25
  • I can't, I get there crash reports through `Crittercism`. I couldn't reproduce it myself. But from the looks, having no external factors, it should be ok. – Iulian Onofrei Oct 27 '15 at 08:26
  • 1
    For workaround, you could check the date is not null before adding in to dictionary.. – Vijay Oct 27 '15 at 08:29
  • I have compiled your code like 10 times it never crashed. – rptwsthi Oct 27 '15 at 08:29
  • I guess `dateComponents.day` == 0 (from `dateComponents.day--;`) can cause the error. – tuledev Oct 27 '15 at 08:30
  • @Vijay, I know I can do this, but I would want to know what caused this problem. And solve at it's core. – Iulian Onofrei Oct 27 '15 at 08:30
  • Try to set `dateComponents.day = 1;dateComponents.day--;`. And what happen? – tuledev Oct 27 '15 at 08:33
  • @anhtu, No, it just goes to the previous month. – Iulian Onofrei Oct 27 '15 at 08:36
  • Why aren't you storing the _dates_ in your array? Formatting them as strings and then parsing those strings to get a date back again is expensive and, as you can see, error prone. (Also, please mark line 657 for us.) – DarkDust Oct 27 '15 at 08:42
  • @DarkDust, It's because I need the date string to make some comparisons, but yes, it would be less expensive, though I don't agree to your comment's last part. It shouldn't be error prone. – Iulian Onofrei Oct 27 '15 at 08:47
  • 1
    @IulianOnofrei: Depending on the date format, you're losing information. For example, with your date format `y-M-d`, you're losing the time and time zone information. – DarkDust Oct 27 '15 at 08:51
  • @DarkDust, I don't need the time. In my database, I have a `STRING` column named `date` which contains dates in that format (e.g. 2-15-10-18). – Iulian Onofrei Oct 27 '15 at 09:12

1 Answers1

1

This answer and @DarkDust's comment pointed me in the right direction.

It was indeed a problem with the Daylight Saving Time occuring at midnight in Brazil. So between 17 and 18 October, there was no midnight. So because I had the date format set to y-M-d, dateFromString: was trying to return a date at the local midnight, which does not exist, hence it returned nil.

Steps to reproduce:

dateFormatter.timeZone = [NSTimeZone timeZoneWithName:@"America/Sao_Paulo"];
NSLog([dateFormatter dateFromString:@"2015-10-18"]); // returns nil

I still have to find out a solution that would return me a date like 2015-10-18 01:00:00 for this time zone.

Community
  • 1
  • 1
Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
  • Ah, Jon's answer is a classic I forgot about… Since you can detect the error either due to the `nil` return or by using [`getObjectValue:forString:range:error:` which returns an error in the NSCocoaErrorDomain](https://gist.github.com/DarkDust/5fbfafb3c9350b38666a) you could modify the string by appending a time and falling back to a different date formatter which includes the time. You might also want to experiment with [CFDateFormatter](https://developer.apple.com/library/mac/documentation/CoreFoundation/Reference/CFDateFormatterRef/): maybe it has the same issue, maybe not. – DarkDust Oct 27 '15 at 11:22
  • @DarkDust, I could do something like `dateComponents.hour = 8`, but will it solve it in 100% times? – Iulian Onofrei Oct 27 '15 at 11:25
  • I guess you can't be sure. We could look into the timezone infos today and might determine that yes, it would work every time. But what about the next time [Kim Jong-Un smokes too much pot](https://en.wikipedia.org/wiki/Time_in_North_Korea) or some other country comes up with something stupid? Since it's a fallback for a seldom occurring issue you might be reasonably safe with such a workaround. But probably the best way to solve this is to eliminate the call to `dateFromString:` altogether by storing both the string and date in your first loop. – DarkDust Oct 27 '15 at 12:42