objective-cobjective-c-blockscore-locationnsvaluetransformergithub-mantle

Trouble when returning non pointer object in a block


I'm using Mantle to help me transform model objects to JSON. One of my objects contains a centroid property defined as follows:

@property (assign) CLLocationCoordinate2D centroid;

When I deal with the server I receive the property in this format:

centroid: {
      lat: "38.416600086777166",
      lon: "-88.29868011101323"
    }

So I decided to implement a custom JSON transformer that's available in the MTLJSONSerializing protocol, defined as follows:

+ (NSValueTransformer*)centroidJSONTransformer {
    return [MTLValueTransformer transformerWithBlock:^CLLocationCoordinate2D(NSDictionary *dict) {
        NSNumber *latitude = dict[@"lat"];
        NSNumber *longitude = dict[@"lon"];

        return CLLocationCoordinate2DMake([latitude doubleValue], [longitude doubleValue]);
    }];
}

The trouble that I run into is that the + (instancetype)transformerWithBlock:(MTLValueTransformerBlock)transformationBlock method inside of MTLValueTransformer takes in a block of type typedef id (^MTLValueTransformerBlock)(id);, which returns a value of type id or any objective-c pointer object.

However what I would like to do instead is return the CLLocationCoordinate2D type, which is a scalar, non-id type. Short of defining my own custome implementation of transformerWithBlock: is there a clean way I can make this happen?


Solution

  • You can wrap primitive/scalar values in NSValue to pass them to/from methods, functions, or blocks expecting an id:

    NSValue *value = [NSValue valueWithBytes:&coordinate objCType:@encode(CLLocationCoordinate2D)];
    

    Then, to get the CLLocationCoordinate2D back out:

    CLLocationCoordinate2D coordinate;
    [value getValue:&coordinate];
    

    As of iOS 6 (and presumably OS X 10.8, though I haven't checked), there's the NSValue MapKit Additions category which adds methods to NSValue to wrap and unwrap CLLocationCoordinate2Ds:

    NSValue *value = [NSValue valueWithMKCoordinate:coordinate];
    CLLocationCoordinate2D coordinate = [value MKCoordinateValue];