Mantle
来源:互联网 发布:万网域名续费退款 编辑:程序博客网 时间:2024/06/10 03:12
Come from OBJECTIVE CAT
Mantle
Mantle is a model framework for iOS that provides a convenient way to create objects from JSON and transform those objects back into JSON. This is especially useful when dealing with a remote API.
We are going to take a look at MTLModel
, MTLJSONAdapter
and why you might want to consider using Mantle for your next project.
MTLModel
MTLModel
provides an easy way to map NSDictionary
objects to Objective-C classes and vice-versa.
To get started, lets look at a simple example. Assume we get the following JSON response from our API endpoint and want to create and populate our (yet to be created) CATProfile
model class.
{ "id": 1, "name": "Objective Cat", "birthday": "2013-09-12 13:29:36 +0100", "website": "http://objc.at", "location": { "lat": "48.2083", "lon": "16.3731" }, "relationship_status": "single", "awesome": true}
Lets create a new MTLModel
subclass that represents the JSON object above.
// CATProfile.htypedef NS_ENUM(NSInteger, CATRelationshipStatus) { CATRelationshipStatusSingle = 0, CATRelationshipStatusInRelationship, CATRelationshipStatusComplicated};@interface CATProfile : MTLModel<MTLJSONSerializing>@property(strong, nonatomic) NSNumber *profileId;@property(strong, nonatomic) NSString *name;@property(strong, nonatomic) NSDate *birthday;@property(strong, nonatomic) NSURL *website;@property(nonatomic) CLLocationCoordinate2D locationCoordinate;@property(nonatomic) CATRelationshipStatus relationshipStatus;@property(nonatomic, getter=isAwesome) BOOL awesome;@end
The CATProfile
class inherits from MTLModel
and implements the MTLJSONSerializing
protocol. The protocol requires us to implement +JSONKeyPathsByPropertyKey
.
// CATProfile.m@implementation+ (NSDictionary *)JSONKeyPathsByPropertyKey { // properties defined in header < : > key in JSON Dictionary return @{ @"profileId": @"id", @"websiteURL": @"website", @"locationCoordinate": @"location", @"relationshipStatus": @"relationship_status", };}@end
+JSONKeyPathsByPropertyKey
returns an NSDictionary with key-value pairs of each model property that should be matched to a value in JSON. This makes sure Mantle knows which JSON key to use to populate a specific model property.
Obiously absent from this list are the name
, birthday
and awesome
properties. If a property is ommited from the dictionary, Mantle will automatically look for a JSON key with the same name as the property defined in the header.
NSValueTransformer
Mantle can handle the conversion of arbitrary types such as NSString
and NSNumber
by default. However, it needs some help with non-arbitrary types such as NSURL
and enums as well as custom structs like CLLocationCoordinate2D
.
Mantle relies on the help of Foundation's NSValueTransformer
to map values between the JSON representation of the model to the actual properties on the Objective-C object.
To create a custom transformer for a model property, we need to implement a class method called +<propertyName>JSONTransformer
and return the desired NSValueTransformer
.
// mapping birthday to NSDate and vice-versa+ (NSValueTransformer *)birthdayJSONTransformer { return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSString *dateString) { return [self.dateFormatter dateFromString:dateString]; } reverseBlock:^(NSDate *date) { return [self.dateFormatter stringFromDate:date]; }];}+ (NSDateFormatter *)dateFormatter { NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; return dateFormatter;}
Mantle calls this method at runtime to determine how to transform the birthday property. The forward block transforms a string to an NSDate
object and the reverse block takes the NSDate
object and converts it back to a string. Nice!
For reference, here is a list of transformer methods for all the other non-arbitrary properties that need our attention.
NSURL ↔︎ JSON string
+ (NSValueTransformer *)websiteURLJSONTransformer { return [NSValueTransformer valueTransformerForName:MTLURLValueTransformerName];}
CLLocationCoordinate2D ↔︎ JSON object
+ (NSValueTransformer *)locationCoordinateJSONTransformer { return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSDictionary *coordinateDict) { CLLocationDegrees latitude = [coordinateDict[@"lat"] doubleValue]; CLLocationDegrees longitude = [coordinateDict[@"lon"] doubleValue]; return [NSValue valueWithMKCoordinate:CLLocationCoordinate2DMake(latitude, longitude)]; } reverseBlock:^(NSValue *coordinateValue) { CLLocationCoordinate2D coordinate = [coordinateValue MKCoordinateValue]; return @{@"lat": @(coordinate.latitude), @"lon": @(coordinate.longitude)}; }];}
enum ↔︎ JSON string
+ (NSValueTransformer *)relationshipStatusJSONTransformer { return [NSValueTransformer mtl_valueMappingTransformerWithDictionary:@{ @"single": @(CATRelationshipStatusSingle), @"relationship": @(CATRelationshipStatusInRelationship), @"complicated": @(CATRelationshipStatusComplicated) }];}
BOOL ↔︎ JSON boolean
+ (NSValueTransformer *)awesomeJSONTransformer { return [NSValueTransformer valueTransformerForName:MTLBooleanValueTransformerName];}
Create model objects from JSON
As soon as the model is fully configured, its time to get the JSON data from the API and convert it to an instance of our model. First, we need to convert the JSON representation to an NSDictionary
, that can be used by Mantle to create our model. Luckily iOS provides a great way to do that via NSJSONSerialization
.
After that, the MTLJSONAdapter
class that ships with Mantle does the heavy-lifting to create our model.
// create NSDictionary from JSON dataNSData JSONData = ... // the JSON response from the APINSDictionary *JSONDict = [NSJSONSerialization JSONObjectWithData:JSONData options:0 error:NULL];// create model object from NSDictionary using MTLJSONSerialisationCATProfile *profile = [MTLJSONAdapter modelOfClass:CATProfile.class fromJSONDictionary:JSONDict error:NULL];
Create JSON from model objects
MTLJSONAdapter
is als capable of creating an NSDictionary
from our model class that can be direcly encoded back into a JSON string.
// create NSDictionary from model class using MTLJSONSerialisationCATProfile *profile = ...NSDictionary *profileDict = [MTLJSONAdapter JSONDictionaryFromModel:profile];// convert NSDictionary to JSON dataNSData *JSONData = [NSJSONSerialization dataWithJSONObject:profileDict options:0 error:NULL];
Note: If you have a property on your model that should not be included when creating a JSON represenatation of your model, return
NSNull.null
. E.g.@{"name": NSNull.null}
in+JSONKeyPathsByPropertyKey
. Mantle will savely ignore this property.
Mapping Arrays and Dictionaries
Most of the time, models have relationships to other models. These relationships are commonly represented via JSON arrays or objects (e.g. owner and friends).
{ "id": 1, "name": "Objective Cat", ..., "owner": { "id": 99, "name": "Alexander Schuch" }, "friends": [ { "name": "Owly", "type": "bird" }, { "name": "Hedgy", "type": "mammal" } ]}
Mantle supports mapping these relationships to new models out of the box. In order to make sure Mantle knows how to transform the relationships we can use one of the following provided category methods on NSValueTransformer
.
+ (NSValueTransformer *)mtl_JSONDictionaryTransformerWithModelClass:(Class)modelClass;+ (NSValueTransformer *)mtl_JSONArrayTransformerWithModelClass:(Class)modelClass;
Of course Mantle needs to know about these relationships and their MTLModel
subclasses they should be transformed to. Its as easy as creating new MTLModel
subclasses and implementing the MTLJSONSerializing
protocol for the objects that should be mapped. Then we can add some new properties to our CATProfile
class and implement two new transformers.
// CATProfile.h@property(strong, nonatomic) CATOwner *owner; // CATOwner is a MTLModel subclass@property(strong, nonatomic) NSArray *friends; // Array of CATFriend objects// CATProfile.m+ (NSValueTransformer *)ownerJSONTransformer { return [NSValueTransformer mtl_JSONDictionaryTransformerWithModelClass:CATOwner.class];}+ (NSValueTransformer *)friendsJSONTransformer { return [NSValueTransformer mtl_JSONArrayTransformerWithModelClass:CATFriend.class];}
Some nice additions
We briefly talked about NSValueTransformer
before. NSValueTransformer
has the nice feature that makes it possible to globally register a transformer by its name. In case you are using the same transformers all over your app, make sure to subclass NSValueTransformer
, register your custom transformer once and subsequently use it in your MTLModel
s.
// In CATProfile.mNSString * const kCATCustomValueTransformerName = @"CATCustomValueTransformer";+ (void)initialize{ // Register NSValueTransformer if (self == CATProfile.class) { CATCustomValueTransformer *transformer = [CATCustomValueTransformer new]; [NSValueTransformer setValueTransformer:transformer forName:kCATCustomValueTransformerName]; }}// Then use the custom transformer to translate properties using Mantle+ (NSValueTransformer *)whateverPropertyJSONTransformer { return [NSValueTransformer valueTransformerForName:kCATCustomValueTransformerName];}
Conclusion
Mantle is a nice addition when working with JSON APIs. However, be aware that it might not be suitable for you if you have to deal with very complex and erratic APIs.
Try Mantle in your next project and let me know what you use it for on Twitter.
- Mantle
- Mantle
- Mantle
- Mantle
- Mantle
- Mantle
- mantle学习
- Mantle Introduce
- TMCache + Mantle
- Mantle 教程
- Mantle 详解
- JSONModel, Mantle
- Mantle 详解
- Mantle简介
- AMD Mantle API 学习笔记 -- Mantle初始化
- AMD Mantle API 学习笔记 -- Mantle简介
- Core Data with Mantle
- Core Data with Mantle
- Java笔记——2
- Activiti源码浅析:Activity与Task
- shell怎么读取网页内容
- FLUENT运行出错
- perl有什么模块可以添加DNS域名吗?
- Mantle
- android开发中,比较常用的11个命令集锦
- 让ckeditor 在线编辑器 不过滤html,head,title
- 跨机房实时大量小文件同步方案
- 互联网乱世之下 一将功成万骨枯
- 中文存入数据库乱码问题
- 是不是只要有主键的InnoDB表都是聚簇索引表
- Cocos2d-HTML5搭载nodejs express3
- Android模拟器(Android Emulator )使用的快捷键、参数、注意事项