Jerry Lee

stay hungry,stay young.

Welcome to my world.


Mantle的初步使用

Mantle 是一个iOS模型框架,它可以简便的创建对象模型,当然也可以把模型转变成JSON,在处理远程接口时,这特别有用。 我们来看一看MTLModel, MTLJSONAdapter的实现,或许在你的下一个项目中,你会考虑使用Mantle.

##MTLModel

MTLModel提供了一种简单的方法来映射NSDictionary对象和Objective-C类,反之亦然。 开始之前,我们看一个简单的例子,假设我们得到以下从服务器返回来的JSON,我们想要创建和填充我们的(未设立)CATProfile模型类。

{
  "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
}

接下来我们创建一个MTLModel子类来代表上面的JSON对象。

// CATProfile.h
typedef 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

CATProfile类继承MTLModel并且遵守MTLJSONSerializing协议,这个协议要求我们实现以下方法:

+ (NSDictionary *)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返回一个字典,它的每个模型属性的键值对都是和JSON值匹配的,这样可以使Mantle知道哪个JSON键用 来填充特定的模型属性。 很显然,三个属性name,birthday,awesome不在列表里,如果一个属性不在字典列表中,Mantle将会自动从JSON中寻找一个和其名字相同的key作为属性定义在header中。

NSValueTransformer

Mantle默认可以处理任意类型的转换,如NSString and NSNumber,然而对于一些特殊的类型列如:NSURL,enums,以及自定义结构CLLocationCoordinate2D需要做些特殊的处理。 Mantle很大程度上依赖于Foundation中的NSValueTransformer帮助,而NSValueTransformer可以很好的映射Objective-C对象中实际的属性和JSON中所代表的数据。

为一个模型属性创建特定的transformer,我么需要实现一个类方法+JSONTransformer,它返回我们期望的类型格式。

// 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调用这个方法时是通过runtime来决定怎样转换birthday属性,前一个block把字符串转换成NSDate对象,后一个block把NSDate对象转换成字符串。

下面列车了一些值得我们注意的特殊格式属性的转换方法,以供参考:

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];
}

##由JSON创建模型

// create NSDictionary from JSON data
NSData JSONData = ... // the JSON response from the API
NSDictionary *JSONDict = [NSJSONSerialization JSONObjectWithData:JSONData options:0 error:NULL];

// create model object from NSDictionary using MTLJSONSerialisation
CATProfile *profile = [MTLJSONAdapter modelOfClass:CATProfile.class fromJSONDictionary:JSONDict error:NULL];

##由模型创建JSON

// create NSDictionary from model class using MTLJSONSerialisation
CATProfile *profile = ...
NSDictionary *profileDict = [MTLJSONAdapter JSONDictionaryFromModel:profile];

// convert NSDictionary to JSON data
NSData *JSONData = [NSJSONSerialization dataWithJSONObject:profileDict options:0 error:NULL];

##映射多个数组和多个字典 大多时候,模型数据之间都是有关联的,这些关联通常表示是通过JSON数组或对象(列如:owner and friends)。

{
  "id": 1,
  "name": "Objective Cat",
  ...,

  "owner": {
  "id": 99,
  "name": "Alexander Schuch"
 },
  "friends": [
 {
  "name": "Owly",
  "type": "bird"
 },
 {
  "name": "Hedgy",
  "type": "mammal"
 }
]
}

Mantle支持将这些关系映射到新的模式,为了确保Mantle知道怎样转换这些关系,我们可以运用以下关于NSValueTransformer的方法:

+ (NSValueTransformer *)mtl_JSONDictionaryTransformerWithModelClass:(Class)modelClass;
+ (NSValueTransformer *)mtl_JSONArrayTransformerWithModelClass:(Class)modelClass;

当然,Mantle需要知道这些关系和它的MTLModel子类应该转变,简单的方法就是创建一个新的MTLModel子类,并且为那些应该映射的对象实现MTLJSONSerializing协议, 我们可以为CATProfile添加一些新的属性,并且实现转换。

// 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];
}

##最后

在处理JSON API时,Mantle是个不错的选择,然而,要意识到它不太适合处理特别复杂并且无规则的API。 文章翻译自OBJECTIVE CAT,感谢原作者!

更多关于Mantle的文章请参考:

使用Mantle处理Model层对象

Mantle

为什么唱吧iOS 6.0选择了Mantle

最近的文章

优秀博文收集

整理如下收集了一些写的比较有针对性,并且写的很有参考性的文章,留着参考: IMP指针的作用 Runtime系列 iOS~runtime理解 runtime 完整总结 Objective-C Runtime Objective-C Runtime 运行时之一:类与对象 Objective-C Runtime 运行时之二:成员变量与属性 Objective-C Runtime 运行时之三:方法与...…

博文继续阅读
更早的文章

使用CocoaPods管理第三方类库

在项目开发中,一些常用的第三方类库如AFNetworking,MBProgressHUD,ReactiveCocoa,Masonry,MJRefresh等,如何去管理它们呢, 这些类库每次更新都要手动导入项目中吗?如何知道它们版本更新了呢?用CocoaPods解决以上问题。安装CocoaPods 查看是否安装了Ruby和git,ruby -v命令查看Ruby的版本,以及git –version 更新gemsudo gem update --system 移除官方镜像源gem sour...…

CocoaPods继续阅读