0

In my iOS application, I have an object that has a timeZone property of type NSTimeZone. I want to POST this as JSON (as well as other data) using RestKit 0.22.0.

I setup a value transformer block, but it is causing an error, which I have been unable to figure out.

RKObjectMapping *eventMapping = [RKObjectMapping requestMapping];

RKValueTransformer *timeZoneTransformer = [RKBlockValueTransformer valueTransformerWithValidationBlock:^BOOL(__unsafe_unretained Class sourceClass, __unsafe_unretained Class destinationClass) {

        // We transform a `NSTimeZone` into `NSString`
        return ([sourceClass isSubclassOfClass:[NSTimeZone class]] && [destinationClass isSubclassOfClass:[NSString class]]);

    } transformationBlock:^BOOL(id inputValue, __autoreleasing id *outputValue, Class outputValueClass, NSError *__autoreleasing *error) {

        // Validate the input and output
        RKValueTransformerTestInputValueIsKindOfClass(inputValue, [NSTimeZone class], error);
        RKValueTransformerTestOutputValueClassIsSubclassOfClass(outputValueClass, [NSString class], error);

        // Perform the transformation
        *outputValue = [((NSTimeZone *)inputValue) name];
        return YES;
    }];

    RKAttributeMapping *timeZoneAttributeMapping = [RKAttributeMapping attributeMappingFromKeyPath:@"timeZone" toKeyPath:@"event.zone"];

    timeZoneAttributeMapping.valueTransformer = timeZoneTransformer;

    [eventMapping addPropertyMapping:timeZoneAttributeMapping];

The value I want to submit to my API is the geopolitical ID of the zone. For New York, it would be America\New_York.

I added a breakpoint in Xcode to show me where the exception was occuring. It is happening in the following file of the RK library, at this line return [NSJSONSerialization dataWithJSONObject:object options:0 error:error];.

#import "RKNSJSONSerialization.h"

@implementation RKNSJSONSerialization

+ (id)objectFromData:(NSData *)data error:(NSError **)error
{
    return [NSJSONSerialization JSONObjectWithData:data options:0 error:error];
}

+ (NSData *)dataFromObject:(id)object error:(NSError **)error
{
    return [NSJSONSerialization dataWithJSONObject:object options:0 error:error];
}

The exception is 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (__NSTimeZone)'

What's interesting is that when Xcode breaks at the return statement I mentioned above, In the variables view, the object parameter of the dataFromObject message does actually contain the value (America/New_York) that I want to be submitted to the server. So, it seems to work.

Also, In the variables view, the error parameter of the dataFromObject has a value of "error: summary string parsing error".

musubi
  • 1,256
  • 3
  • 14
  • 22

1 Answers1

0

Upon closer inspection of the variables in Xcode's Variable View, the NSTimeZone object I was trying to map to JSON was not actually mapping to an NSString. I thought it was mapping correctly, because the variable's value was what it was supposed to be (i.e. America/New_York). However, the type was still NSTimeZone. Therefore, the transformation code block was not being called at all.

Then I saw this example code in the RKCLLocationValueTransformer example here.

RKObjectMapping *userRequestMapping = [RKObjectMapping requestMapping];
[userRequestMapping addAttributeMappingsFromArray:@[ @"name" ]];
RKAttributeMapping *attributeMapping = [RKAttributeMapping
    attributeMappingFromKeyPath:@"location" toKeyPath:@"location"];
attributeMapping.propertyValueClass = [NSDictionary class];
attributeMapping.valueTransformer = [RKCLLocationValueTransformer
    locationValueTransformerWithLatitudeKey:@"latitude" longitudeKey:@"longitude"];
[userRequestMapping addPropertyMapping:attributeMapping];

NSError *error = nil;
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor
    requestDescriptorWithMapping:userRequestMapping objectClass:[User class]
    rootKeyPath:@"user" method:RKRequestMethodAny];

The problem was that I was not specifying the propertyValueClass property.

attributeMapping.propertyValueClass = [NSDictionary class];

The propertyValueClass property specifies the type that you want the mapped value to be represented as, or transformed to.

The code should look like this.

RKAttributeMapping *timeZoneAttributeMapping = [RKAttributeMapping attributeMappingFromKeyPath:@"timeZone" toKeyPath:@"event.zone"];

timeZoneAttributeMapping.propertyValueClass = [NSString class];

timeZoneAttributeMapping.valueTransformer = timeZoneTransformer;

[eventMapping addPropertyMapping:timeZoneAttributeMapping];

Here is a link to the documentation for this property.

Details about the cause of the NSInvalidArgumentException can be found here.

Community
  • 1
  • 1
musubi
  • 1,256
  • 3
  • 14
  • 22