iOS RestKit Date parsing

Go To StackoverFlow.com

4

In an old iOS project I had to parse dates that come in the format dd/MM/yyyy so I put these lines in a static method of my AppDelegate and it worked as expected

// Set the Dateformatter for the Dates returned by Knowledge tt/mm/yyyy
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"dd/MM/yyyy"];
[[RKValueTransformer defaultValueTransformer] insertValueTransformer:dateFormatter atIndex:0];

However in the actual projects my dates come in a slightly different format dd.MM.yyyy, so I used the same lines of code, just switched the Date format, but the date is not parsed.

For this date "ExamDate":"20.06.2014" I get (NSDate *) _examDate = 0x08f7dcd0 2014-01-01 01:00:00 CET after parsing and I can't understand why.

Update: I made a small test with this code:

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"dd.MM.yyyy"];
NSDate *date = [dateFormatter dateFromString:@"20.06.2014"];
DLog(@"Date: %@", date);

and got in the log: Date: 2014-06-19 22:00:00 +0000

2014-06-25 11:28
by Hons


7

It looks like your in a timezone with a GMT offset of -2:00. Set your timezone to 0 GMT to have it printed correctly. What we usually do is just use unix time when passing around dates so we can avoid this sort of issue.

dateFormatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];

Since your problem appears to be in RestKit's parsing you'll need to use your own date parser. You can do this by installing what RestKit calls a Value Transformer which is what it uses when mapping data.

Here's one that you can work with to achieve what you want:

[RKObjectMapping alloc]; // This ensures you will actually insert at index 0!

[RKValueTransformer.defaultValueTransformer
 insertValueTransformer:
 [RKBlockValueTransformer
  valueTransformerWithValidationBlock:^BOOL(__unsafe_unretained Class inputValueClass, __unsafe_unretained Class outputValueClass) {
      return (([inputValueClass isSubclassOfClass:[NSDate class]] && [outputValueClass isSubclassOfClass:[NSString class]]) ||
              ([inputValueClass isSubclassOfClass:[NSString class]] && [outputValueClass isSubclassOfClass:[NSDate class]]));
  }
  transformationBlock:^BOOL(id inputValue, __autoreleasing id *outputValue, __unsafe_unretained Class outputClass, NSError *__autoreleasing *error) {
      RKValueTransformerTestInputValueIsKindOfClass(inputValue, (@[ [NSString class], [NSDate class] ]), error);
      RKValueTransformerTestOutputValueClassIsSubclassOfClass(outputClass, (@[ [NSString class], [NSDate class] ]), error);
      if ([inputValue isKindOfClass:[NSString class]]) {
          // We're mapping from a string to a date.
          // You can add your formatter here.
          // Below is mine for mapping from Unix Time to an NSDate.
          NSString* input = inputValue;
          *outputValue = [NSDate dateWithTimeIntervalSince1970:input.integerValue];
      } else if ([inputValue isKindOfClass:[NSDate class]]) {
          // We're mapping from a date to a string.
          // You can add your formatter here (if needed).
          // Below is mine for mapping from an NSDate to Unix Time.
          NSDate* input = inputValue;
          *outputValue = @([input timeIntervalSince1970]);
      }
      return YES;
  }]
 atIndex:0]; // Insert at index 0 so your formatter is always used over the default one.

Alternatively, it looks like RestKit has it's own NSDateFormatter category which adds support for adding it as an RKValueTransformer. You should be able to try something like this:

[RKObjectMapping alloc]; // This ensures you will actually insert at index 0!
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"dd.MM.yyyy"];
dateFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
[RKDefaultValueTransformer insertValueTransformer:dateFormatter atIndex:0];
2014-06-25 12:23
by Sandy Chapman
You're right... this fixes my problem with the dateformatter sample, but still it's not automating the parsing in RestKit, there I still get 1st january 2014 for this dat - Hons 2014-06-25 12:26
Which version of RestKit are you using? @Hon - Sandy Chapman 2014-06-25 12:27
I'm currently on RestKit 0.23. - Hons 2014-06-25 12:34
@Hons, since you're using 0.23.1, you can use a RKValueTransformer. I've pasted one into my answer. You'll need to add your own formatting code where I've commented, but this should get you on the right track - Sandy Chapman 2014-06-25 12:41
I add your code to my project.. The first block, the validation, is called for every attribute, and for my date it returns YES, but the second block is never calle - Hons 2014-06-25 13:11
@Hons That's strange. If it's returning YES and you've inserted it at index 0 it should be using your transformation block. Try removing the RKValueTransformerTest... macro calls and see if that works - Sandy Chapman 2014-06-25 13:40
@Hons It looks like internally RestKit is also making NSDateFormatter compatible with it's RKValueTransformers using a category. I'll edit my answer and give another suggesting - Sandy Chapman 2014-06-25 13:44
By debugging I saw that there is a RKISO8601DateFormatter before my transformer in the list... but I added it at index - Hons 2014-06-25 13:45
@Hons, I've added a line of code to my example which should fix this and allow your custom value formatter to be insert at index 0 - Sandy Chapman 2014-06-25 13:55
You're right. The RKObjectMapping initialize method added the ISODateTransformer at index 0, which happend after I put the transformer there. Thank you very much - Hons 2014-06-25 14:02
I used the second block (Alternately...) to solve a date transformation issue with a web service I'm using. Other than setting the parameters appropriate for my situation, I needed to retrieve the default value transformer: [[RKValueTransformer defaultValueTransformer] insertValueTransformer:dateFormatter atIndex:0];John Baldwin 2014-11-14 18:20
Ads