RestKit valueTransformer not being called

363 views Asked by At

I am using RestKit to talk to my JSON endpoints. The endpoint returns a UNIX timestamp in 'number of milliseconds'. But RestKit's default transformer assumes that it is 'number of seconds' and I get a 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's 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 am adding this transformer to RestKit's default transformer.

RKValueTransformer* transformer = [self timeIntervalInMillisecondsSince1970TwoWayTransformer];
[[RKValueTransformer defaultValueTransformer] addValueTransformer:transformer];

But, my transformer never gets called. The NSLog statement I wrote in it never gets executed!


So then I tried this -- writing a transformer on the attributeMapping like this:

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. It says "Property valueTransformer not found on object of type 'RKAttributeMapping *'". I don't understand this. All the examples I have seen on the internet, including the one with RestKit's documentation say the same thing. All examples set the valueTransformer property of the AttributeMapping object. Even the RestKit Class Reference says that I can set it. But then why doesn't it compile?

3

There are 3 answers

0
Rahul Jiresal On BEST ANSWER

So I managed to finally figure out what the problem was.

My podfile was configured to use RestKit 0.20.3. And then I had added RKValueTransformers as another Pod. However, RestKit 0.20.3 and earlier come with their own version of RKValueTransformer (.h and .m) files. And those older versions of RestKit doesn't support adding your own Transformers, because it doesn't make use of the newer RKValueTransformers library.

When I upgraded my RestKit version to the newest version (actually anything above 0.21.0 would work), things started working fine. I didn't need to add the pod RKValueTransformers, because it gets added automatically as a dependency of RestKit.

1
Wain On

Look at using insertValueTransformer:atIndex: instead of addValueTransformer: so that you can add it at the start of the list rather than the end.

0
Besi On

In swift I could successfully create a value transformer. Its purpose is to just add a timestamp downloadedOn to my model, so I don't use the inputValue at all:

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 outputValue pointer until I figured out that I don't have to use an inout parameter but rather access the memory property of the outputValue parameter.

Note: I am using a generic keyPath of @self for the other side of my property mapping, so that I can use this mapping for various classes that have the downloadedOn property.