JSONObjectMapper

JSON to Core Data mapper for iOS


Keywords
coredata, ios, json, mapper, objective-c, swift
License
MIT
Install
pod try JSONObjectMapper

Documentation

JSONObjectMapper

Version License Platform

JSONObjectMapper can efficiently map JSON objects to corresponding Core Data managed objects by using your custom mapping definitions. It frees you from writing the same boilerplate code over and over again.

It's also pretty fast. Unlike some other implementations, rather than fetch each individual object as needed while JSON data is being processed, this mapper converts JSON into an intermediate internal representation and then fetches the necessary objects in "buckets" (grouped by entity), which is a lot more efficient.

Installation

JSONObjectMapper is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'JSONObjectMapper'

Swift

JSONObjectMapper can be used in Swift code. To set this up:

  1. Importing Objective-C frameworks in Swift requires a little extra effort. You have two options:

    • Add a bridging header to your project with with the following import statement:

      #include <JSONObjectMapper/JSONObjectMapper.h>

      This will make JSONObjectMapper classes visible to your Swift code.

    • Add use_frameworks! to your Podfile and import the framework as usual:

      import JSONObjectMapper
  2. Add @objc(ClassName) annotations to your managed object classes so that they can be found by Objective-C code:

    @objc(Post)
    class Post: NSManagedObject {
        ...
    }

ExampleSwift contains a complete example written in Swift.

Example

In order to use JSONObjectMapper, you need to modify your model classes (subclasses of NSManagedObject) to return some metadata about their entities that describes how to map JSON properties to Core Data entity attributes and relationsihps. For instance, to avoid creation of duplicate objects, each entity should have a primary key attribute, and JSON objects mapped to this entity should also contain a non-null value for the key.

Here is an example of how to add mapping support to a class:

// In general, you will implement these two methods of the JSONMappingProtocol protocol:

+ (JSONObjectMapping *)defaultMapping {
    // Return an object mapping that consists of attribute and relationship
    return [JSONObjectMapping mappingWithDictionary:@{
        @"postID": @"post_id",
        @"content": @"content",
        @"rating": @"rating",
        @"viewCount": @"view_count",
        @"author": [JSONRelationshipMapping mappingWithJSONKey:@"author_id"],
        @"comments": [JSONRelationshipMapping mappingWithJSONKey:@"comments"]
    }];
}

+ (NSString *)primaryKey {
    // Return the key which you use to uniquely identify objects in the Core
    // Data store. This is used by the mapper to decide whether it needs to
    // create a new object or update an existing one.
    return @"postID";
}

@end

How to map JSON data to managed objects:

    JSONObjectMapper *objectMapper = [[JSONObjectMapper alloc]
                                      initWithManagedObjectContext:self.managedObjectContext];
    ...

    NSURLSession *session = NSURLSession.sharedSession;
    NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:SomeAPIURL]
                                        completionHandler:^(NSData *data,
                                                            NSURLResponse *response, NSError *error) {
        if (error != nil) {
            NSLog(@"Error fetching data from API: %@", error);
            return;
        }

        NSError *JSONError;
        NSDictionary *responseDictionary =
            [NSJSONSerialization JSONObjectWithData:data options:0 error:&JSONError];
        NSLog(@"API response: %@", responseDictionary);
        if (JSONError != nil) {
            NSLog(@"Could not parse JSON data: %@", JSONError);
            return;
        }

        NSArray<Post *> *posts = [objectMapper mapArray:responseDictionary[@"posts"]
                                          toEntityClass:Post.class];
        ...
    }];
    [task resume];

See the example project for a complete example.

Custom attribute transformers

If you want to perform some additional conversion of attribute values on many model classes, one way is to use custom value transformers (subclasses of NSValueTransformer). You can register value transformers on your JSONObjectMapper instance and refer to them in your Core Data model via User Defined Attributes using JSONValueTransformer as the key and the name of your transformer class as the value.

Screenshot

For instance, if you're using an API that returns relative file URLs everywhere and you want to convert them to absolute URLs you could do it like this:

@interface FileURLTransformer : NSValueTransformer

- (instancetype)initWithBaseURL:(NSURL *)baseURL;

@interface FileURLTransformer ()

@property (nonatomic, readonly) NSURL *baseURL;

@end

@implementation FileURLTransformer

+ (Class)transformedValueClass {
    return [NSString class];
}

+ (BOOL)allowsReverseTransformation {
    return NO;
}

- (instancetype)initWithBaseURL:(NSURL *)baseURL {
    if (self = [super init]) {
        _baseURL = baseURL;
    }
    return self;
}

- (id)transformedValue:(id)value {
    NSString *relativeURLString = value;
    if (relativeURLString.length != 0) {
        return [NSURL URLWithString:relativeURLString relativeToURL:self.baseURL];
    }
    return nil;
}

@end

And then somewhere during initialization of your mapper:

[objectMapper setValueTransformer:fileURLTransformer forName:@"FileURLTransformer"];

Author

Sergey Zolotarev, sryze@protonmail.com

Changelog

0.1.3

  • Updated podspec
  • Updated README

0.1.2

  • Added a Swift example proejct
  • Updated README
  • Updated pod description

0.1.1

Updated the README with more information and examples

0.1.0

Initial release

License

JSONObjectMapper is available under the MIT license. See the LICENSE file for more info.