Skip to content

RKValueTransformer Example: Mapping NSString to NS_ENUM

mozeryansky edited this page Jun 10, 2015 · 4 revisions

Here is an example on how to transform NSString values into a corresponding NS_ENUM.

We will create a RKValueTransformer and attach it to a specific attribute mapping, which we will then add the attribute mapping as a property mapping. In this example we will use a RKBlockValueTransformer, but you can use anything that adopts the protocol RKValueTransforming.

NS_ENUM will be the type you defined (i.e. NSInteger), so you will need compare the string value and return a NSNumber set to the corresponding NSInteger value for the enumerated type. The below example uses a dictionary to map NSStrings to NSNumbers. As long as you return a NSNumber this will work.

typedef NS_ENUM(NSInteger, BarnAnimal){
        BarnAnimalUnknown, // I use a unknown type for invalid returned values
        BarnAnimalPig,
        BarnAnimalCow,
        BarnAnimalGoat
}

RKValueTransformer *enumTransformer = [RKBlockValueTransformer valueTransformerWithValidationBlock:^BOOL(__unsafe_unretained Class sourceClass, __unsafe_unretained Class destinationClass) {
        // We transform a NSString into a NSNumber
        return ([sourceClass isSubclassOfClass:[NSString class]] && [destinationClass isSubclassOfClass:[NSNumber class]]);

} transformationBlock:^BOOL(id inputValue, __autoreleasing id *outputValue, Class outputValueClass, NSError *__autoreleasing *error) {
        // Validate the input and output
        RKValueTransformerTestInputValueIsKindOfClass(inputValue, [NSString class], error);
        RKValueTransformerTestOutputValueClassIsSubclassOfClass(outputValueClass, [NSNumber class], error);

        // Perform the transformation
        NSDictionary *enumMap = @{
            @"Pig" : @(BarnAnimalPig),
            @"Cow" : @(BarnAnimalCow),
            @"Goat" : @(BarnAnimalGoat)
        };

        NSNumber *enumNum = enumMap[inputValue];
        if (!enumNum) {
            enumNum = @(BarnAnimalUnknown);
        }

        *outputValue = enumNum;
        return YES;
    }];

    RKAttributeMapping *enumMapping = [RKAttributeMapping attributeMappingFromKeyPath:@"farmAnimal" toKeyPath:@"farmAnimal"];
    enumMapping.valueTransformer = enumTransformer;


    RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[self class]];
    [mapping addPropertyMapping:enumMapping];

Why does this work? You would be correct to ask this question, a NSNumber is not a NSInteger. However, RestKit uses KVC to perform the setting of the variables, specifically setValue:forKeyPath. KVC has handling for scalar types by performing wrapping and unwrapping: Scalar and Structure Support. In short, KVC will tell RestKit your NSInteger is an NSNumber, and when RestKit sets the NSNumber, KVC will convert it to an NSInteger.

Clone this wiki locally