-
Notifications
You must be signed in to change notification settings - Fork 9
RKValueTransformer Example: Mapping NSString to NS_ENUM
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.