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.