GithubHelp home page GithubHelp logo

rmmapper's Introduction

Setup

You can drag the RMMapper folder to your project. This library must be ARC enabled.

RMMapper can be installed by CocoaPods. Add below line to your Podfile to install it.

pod 'RMMapper'

Methods

You can retrieve list of attributes of a class by:

+ (NSDictionary *)propertiesForClass:(Class)cls;

If you have an object and you want to populate it's attributes from a dictionary, there is a method for that:

+ (id) populateObject:(id)obj fromDictionary:(NSDictionary*)dict;

There is a use case that you need to build NSDictionary params for AFNetworking:

+ (NSDictionary*) dictionaryForObject:(id)obj;
+ (NSDictionary*) dictionaryForObject:(id)obj include:(NSArray*)includeArray;
+ (NSMutableDictionary*) mutableDictionaryForObject:(id)obj;
+ (NSMutableDictionary*) mutableDictionaryForObject:(id)obj include:(NSArray*)includeArray;

You can convert an object to NSDictionary so that NSLog can print its value too!

If the json is an array, we can also convert the NSArray of dictionary into NSArray of object with predefined class. You can see the example for more detail.

+ (NSArray*) arrayOfClass:(Class)cls fromArrayOfDictionary:(NSArray*)array;
+ (NSMutableArray*) mutableArrayOfClass:(Class)cls fromArrayOfDictionary:(NSArray*)array;

RMMapper supports relationship in your class as well. Lets assume we now have JSON for a room as below:

{
    "id":879302,
    "title":"My room",
    "address":"Singapore",
    "host":{"id":34045, "name":"David", "age":30, "email":"[email protected]"}
}

You can define class RMRoom as below:

// RMRoom.h
#import "RMUser.h"

@interface RMRoom : NSObject

@property (nonatomic, retain) NSNumber* id;
@property (nonatomic, retain) NSString* title;
@property (nonatomic, retain) NSString* address;
@property (nonatomic, retain) RMUser* host;

@end

Then if you want to access the host email, you can use room.host.email

If you want to map JSON key to class property, you can do so. In your model class, implement method rm_dataKeysForClassProperties in RMMapping protocol:

#import "RMRoom.h"

@implementation RMRoom

- (NSDictionary *)rm_dataKeysForClassProperties
{
    // country_code is json key, countryCode is class property
    return @{
        @"countryCode" : @"country_code",
        @"currencyCode" : @"currency_code",
    };
}

@end

If your property is an array of another models, you can provide the model class so it can be automatically parsed:

-(Class)rm_itemClassForArrayProperty:(NSString *)property {
    if ([property isEqualToString:@"topping"]) {
        return [RMTopping class];
    }

    return nil;
}

Usage of RMMapper

RMMapper is very helpful when you want to archive custom object into NSUserDefaults, or make an object copyable.

If you want to make RMUser class archivable so that you can save it into NSUserDefaults, just add this into the header:

#import "NSObject+RMArchivable.h"

Then done, your class is ready to be archived! You can use category NSUserDefaults+RMSaveCustomObject to help you archive faster:

#import "NSUserDefaults+RMSaveCustomObject.h"

// ...
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults rm_setCustomObject:user forKey:@"SAVED_DATA"];

To retrieve the custom object from NSUserDefaults:

user = [defaults rm_customObjectForKey:@"SAVED_DATA"];

If you want to exclude some properties from being archived, you can implement method rm_excludedProperties in RMMapping protocol in your class:

#import "RMUser.h"

@implementation RMUser


- (NSArray *)rm_excludedProperties
{
return @[@"display"];
}

@end

To make a class copyable, just include below code into your class:

#import "NSObject+RMCopyable.h"

Usage

This library converts common data structure like NSDictionary or NSArray into objects with predefined class. It is similar to Jackson or Gson library in Java.

One common use case for developer is retrieving the data from server, and populating it to the views. If you use AFNetworking, it already converts the string into NSDictionary or NSArray, and you can use that to build your view.

Let's assume we retrieve a user json like this:

{
  "id": 50234
  "name":"David",
  "age":40,
  "email":"[email protected]"
}

We can build the view by:

self.nameLabel.text = [dict objectForKey:@"name"];
self.ageLabel.text = [dict objectForKey:@"age"];
self.emailLabel.text = [dict objectForKey:@"email"];

This approach is okay but not scalable. Response from server can contains 20 or more different keys, and typing [dict objectForKey:@"key"] is tedious and error prone. If we type wrong key string, the app will crash! The compiler cannot help us detect error, and we can only find these crash when we do actual test at runtime. If we need to pass the data to another controller, we will have more repetitive tasks to do. And worse, if in the future server includes more attributes, then we have to edit all the ViewControllers to add the extra attributes.

So we have to find a better approach. This time we define a plain model class and convert the NSDictionary/NSArray into the plain model object when we retrieve data from server. Then in the view, we can simply use that class and Xcode can autocomplete the fields for us.

Let define an user class to model above json:

@interface RMUser : NSObject

@property (nonatomic, retain) NSNumber* id;
@property (nonatomic, retain) NSString* name;
@property (nonatomic, retain) NSString* email;
@property (nonatomic, retain) NSNumber* age;

@end

In the view, it becomes:

self.nameLabel.text = user.name;
self.ageLabel.text = [user.age stringValue];
self.emailLabel.text = user.email;

Well this is much cleaner! We have Xcode auto complete the fields for us, so we don't have to worry about the wrong key like in previous approach. But we still need to convert the data into this object:

RMUser* user = [[RMUser alloc] init];
user.name = [dict objectForKey:@"name"];
user.age = [dict objectForKey:@"age"];
user.email = [dict objectForKey:@"email"];
// Continue to parse the value...

Doing above code is boring and luckily we have a better way to do it. If we can retrieve attributes for an object, we can use setValue:forKey method to set value from dictionary to object attribute.

Using RMMapper, the above code becomes super simple:

RMUser* user = [RMMapper objectWithClass:[RMUser class] fromDictionary:dict];

Then all value from dictionary will be parsed into object user!

About

The code to retrieve all attributes of a class is taken from http://stackoverflow.com/questions/754824/get-an-object-attributes-list-in-objective-c/13000074#13000074

Thanks Farthen and all user contributes to that thread!

License

MIT License

rmmapper's People

Contributors

leoru avatar readmecritic avatar thomasdao avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rmmapper's Issues

Deep copy

Is there a way do deep copy an object?
Thanks.

Swift3

not working with swift 3.

Overriding method in category

Overriding method in category is not a good idea. Right now I came to issue, that I added new version of my Core data model to the project. And since then I can't run the app as Core data throw an exception ('NSInternalInconsistencyException', reason: 'Can't modify an immutable model.').

Reason is that when initializing Core data stack using Magical Record, it instantiates immutable model which is completely legit and then copies some NSEntityDescriptions. Which causes initialization to fail because it is an NSObject and uses copyWithZone:. That calls implementation from NSObject+RMCopyable and that crashes because it uses setValue:forKey: which can't be used as model is immutable.

tvOS support

Are you going to add tvOS support and if yes when?

Extending an existing object: adding a new property

I'm not sure if this is a bug, or if I'm just using this library wrong. I'm using version 1.1.1 (installed via CocoaPods).

I need to add a property to one of my objects:

@interface Points : NSObject
@property NSInteger existing;
@property NSInteger newProperty;
@end

The problem is when I run this on a device which has an old version serialised. This line throws an exception when I try to read the object:

_points = [_userDefaults rm_customObjectForKey:@"points"];

I can think of some hacky workarounds, but is there an elegant way of doing this? Ideally to specify a default value for newProperty if one isn't found in NSData from NSDefaults.

this class is not key value coding-compliant for the key observationInfo

This happened with ios8 devices GM - Xcode 6 GM

Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<NSObject 0x3ca13050> valueForUndefinedKey:]: this class is not key value coding-compliant for the key observationInfo.'
--- File : NSObject + RMArchivable.m
--- Line : 25 - 30
--- Code :
for (NSString* key in propertyDict) {
if (!excludedProperties || ![excludedProperties containsObject:key]) {
id value = [self valueForKey:key];
[encoder encodeObject:value forKey:key];
}
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.