RestKitTransformer value not being called
I am using RestKit to talk to my JSON endpoints. The endpoint returns the UNIX timestamp in "milliseconds". But RestKit default transformer assumes it is "number of seconds" and I am getting wrong value in NSDate.
So I looked around and found that I need to use a custom valueTransformer to tell RestKit how to transform my timestamp. Here is the code I have.
+ (RKBlockValueTransformer*) timeIntervalInMillisecondsSince1970TwoWayTransformer {
return [RKBlockValueTransformer valueTransformerWithValidationBlock:^BOOL(__unsafe_unretained Class sourceClass, __unsafe_unretained Class destinationClass) {
// This transformer handles `NSNumber` <-> `NSDate` transformations
NSLog(@"checking new transformer");
return (([sourceClass isSubclassOfClass:[NSNumber class]] && [destinationClass isSubclassOfClass:[NSDate class]]) ||
([sourceClass isSubclassOfClass:[NSDate class]] && [destinationClass isSubclassOfClass:[NSNumber class]]));
} transformationBlock:^BOOL(id inputValue, __autoreleasing id *outputValue, __unsafe_unretained Class outputValueClass, NSError *__autoreleasing *error) {
NSLog(@"transforming");
RKValueTransformerTestInputValueIsKindOfClass(inputValue, (@[ [NSNumber class], [NSDate class] ]), error);
RKValueTransformerTestOutputValueClassIsSubclassOfClass(outputValueClass, (@[ [NSNumber class], [NSDate class] ]), error);
if ([outputValueClass isSubclassOfClass:[NSDate class]]) {
if ([inputValue isKindOfClass:[NSNumber class]]) {
*outputValue = [NSDate dateWithTimeIntervalSince1970:[inputValue doubleValue] / 1000];
}
} else if ([outputValueClass isSubclassOfClass:[NSNumber class]]) {
*outputValue = @((NSInteger)[inputValue timeIntervalSince1970] * 1000);
}
return YES;
}];
}
Then I add this transformer to the RestKit default transformer.
RKValueTransformer* transformer = [self timeIntervalInMillisecondsSince1970TwoWayTransformer];
[[RKValueTransformer defaultValueTransformer] addValueTransformer:transformer];
But my transformer is never called. The statement NSLog
I wrote in it never runs!
So I tried this - writing a transformer on the attribute Machine:
RKValueTransformer* transformer = [self timeIntervalInMillisecondsSince1970TwoWayTransformer];
RKAttributeMapping *tokenExpiryMapping = [RKAttributeMapping attributeMappingFromKeyPath:@"access_token_expiration" toKeyPath:@"accessTokenExpirationDate"];
tokenExpiryMapping.valueTransformer = transformer;
[userMapping addPropertyMapping:tokenExpiryMapping];
But the code doesn't compile when I do this. He says: "The valueTransformer property was not found on an object of type 'RKAttributeMapping *'. I don't understand that. All the examples I've seen on the Internet, including those from the RestKit documentation , say the same thing. All examples set an valueTransformer
object property AttributeMapping
. Even the link RestKit class says I can install it, but then why doesn't it compile?
source to share
So, I finally managed to figure out what the problem is.
My podfile was configured to use RestKit 0.20.3. And then I added RKValueTransformers
as another Pod. However, RestKit 0.20.3 and earlier have their own version of RKValueTransformer files (.h and .m). And those old RestKit versions don't support adding their own transformers because they don't use the new library RKValueTransformers
.
When I updated my RestKit version to the newest version (in fact anything above 0.21.0 would have worked) everything started working fine. I didn't need to add the pod RKValueTransformers because it is automatically added as a RestKit dependency.
source to share
In swift, I can successfully create a value transformer. Its purpose is to just add a timestamp downloadedOn
to my model, so I don't use at all inputValue
:
let downloadedMapping = RKAttributeMapping(fromKeyPath: "@self", toKeyPath: "downloadedOn")
downloadedMapping.valueTransformer = RKBlockValueTransformer(validationBlock: nil){ _, outputValue, _, error -> Bool in
outputValue.memory = NSDate()
return true
}
myMapping.addPropertyMapping(downloadedMapping)
The biggest caveat for me was how I should write the value to the pointer outputValue
until I figure out that I don't need to use the parameter inout
, but rather access the property memory
for outputValue
.
Note . I am using a generic keyPath @self
for the other side of my property mapping, so I can use this mapping for different classes that have a property downloadedOn
.
source to share