马上要开始一个关于体育赛事直播的新项目,数据逻辑比较复杂,数据处理这块如果按照以前的积累无疑会选择 AFNetworking+JSONKit+MagicalRecord.这种模式使用起来轻便,可定制性高,缺点就是在处理复杂数据时太费时间。考量得失和项目的需要,决定采用RestKit。其实RestKit在开源社区早就享有盛名,许多人会拿他和AFNetworking相互比较,在我看来两者侧重点不同,没有任何可比性。AFNetworking是一个功能非常齐全的网络数据请求库,侧重于处理各种类型的api风格,提供了远程图片异步加载,上传下载等常用功能,如果项目数据量很小它会是很好的选择。RestKit则是注重从远程数据请求到本地数据解析的存储和流程化业务处理,它专注于RESTful API,如果你项目服务器不是RESTful,那就别考虑了。RestKit包含json解析,object映射和coreData自动存储,很好很强大。如果你非要在这两者之间分个你死我活,请移步 RestKit-vs-AFNetworking-What-are-the-pros-and-cons 。
之前并无使用RestKit的经验,摘录一篇觉得很不错的入门文章:
原文链接: Introduction to RestKit Tutorial
翻译正文
你可以连接很多的web service(俗称web服务)来获取精彩有用的信息。举个例子,你可以通过Twitter的web service来获取list和发送Tweets,可以通过连接Foursquare的web service 来获取你附近的餐馆列表。如果你想用使用这些API,你可以直接使用NSURLRequest发起一个网络请求或者使用类似AFNetworking的库。然而还有一个更便捷的方法:使用RestKit!
RestKit是一个非常流行且便于使用的框架。它可以在处理api时候让你少写很多糟糕的代码,比如解析json和映射对象。
在这篇RestKit教程中,我们将要尝试使用FourSquare的api写一个简单的应用来实现现实附近咖啡馆的功能,因为我们都热爱和需要咖啡。

开始
xcode中创建一个Master-View application,输入CoffeeKit作为工程名称,设置device为iPhone
使用RestKit
RestKit由三个部分组成
- Network :RestKit现在使用AFNetworking v1.3.3作为网络操作层,RestKit的维护者正在升级到AFNetworking 2.0
- Object Mapping(对象映射): RestKit提供一个接口来直接映射服务器返回的json/xml数据
- Core Data:RestKit对coreData提供了额外的支持,包括映射远程对象为coreData对象并且进行本地存储
换句话说,这些工作的代码你都不用去写了!
最困难是事情就是安装和配置RestKit,可以选择两种方法:CocoaPods和Git submodule。我宁愿选择CocoaPods ( 推荐CocoaPods教程 )。
Podfile添加内容如下内容,重新pod install即可。
|
platform :ios, '5.0' pod 'RestKit', '~> 0.20.0' |
在AppDelegate.m添加
|
#import <RestKit/RestKit.h> |
运行项目,如果没有出错就是安装成功了。
连接Foursquare服务器
查询附近咖啡店需要用到api如下:
https://api.Foursquare.com/v2/venues/search?ll=37.33,-122.03&categoryId=4bf58dd8d48988d1e0931735
这个api返回json格式的数据,地点被写死在latitude: 37.33 and longitude: -122.03 ,大概是苹果的总部位置,categoryId是咖啡店的一个种类标识。
正常情况api会返回:
|
{ "meta": { "code": 200 }, "notifications": [ { "item": { "unreadCount": 3 }, "type": "notificationTray" } ], "response": { "confident": true, "neighborhoods": [], "venues": [ { "categories": [ { "icon": { "prefix": "https://ss1.4sqi.net/img/categories_v2/food/coffeeshop_", "suffix": ".png" }, "id": "4bf58dd8d48988d1e0931735", "name": "Coffee Shop", "pluralName": "Coffee Shops", "primary": true, "shortName": "Coffee Shop" } ], "contact": { "formattedPhone": "(408) 446-9000", "phone": "4084469000", "twitter": "philzcoffee" }, "hereNow": { "count": 0, "groups": [] }, "id": "51630409498eedc7dd88e60b", "location": { "address": "20686 Stevens Creek Blvd", "cc": "US", "city": "Cupertino", "country": "United States", "crossStreet": "De Anza Blvd", "distance": 936, "lat": 37.32246179607897, "lng": -122.03470838696346, "postalCode": "95014", "state": "CA" }, "name": "Philz Coffee", "referralId": "v-1390061483", "specials": { "count": 0, "items": [] }, "stats": { "checkinsCount": 3790, "tipCount": 40, "usersCount": 1460 }, "verified": true }, { "categories": [ { "icon": { "prefix": "https://ss1.4sqi.net/img/categories_v2/food/coffeeshop_", "suffix": ".png" }, "id": "4bf58dd8d48988d1e0931735", "name": "Coffee Shop", "pluralName": "Coffee Shops", "primary": true, "shortName": "Coffee Shop" } ], "contact": { "formattedPhone": "(650) 321-2161", "phone": "6503212161", "twitter": "philz_coffee" }, "hereNow": { "count": 0, "groups": [] }, "id": "4dd1580eb3adb047f5024231", "location": { "address": "101 Forest Ave", "cc": "US", "city": "Palo Alto", "country": "United States", "crossStreet": "at Alma St.", "distance": 17063, "lat": 37.442086282055726, "lng": -122.16159119091502, "postalCode": "94301", "state": "CA" }, "name": "Philz Coffee", "referralId": "v-1390061483", "specials": { "count": 0, "items": [] }, "stats": { "checkinsCount": 14168, "tipCount": 118, "usersCount": 4044 }, "verified": true } ] } } |
Foursquare提供一个免费的权限来使用他们的Foursquare,但是需要注册app(这和国内新浪等api使用流程相似) 注册页面
开始编码
首先把app的Client ID和Client Secret添加到MasterViewController.m
|
#define kCLIENTID @"Your Foursquare Client ID" #define kCLIENTSECRET @"Your Foursquare Client Secret |
新建一个model命名为Venue,并在MasterViewController.m进行引用
|
@interface Venue : NSObject @property (nonatomic, strong) NSString *name; @end |
在MasterViewController.m添加
|
- (void)viewDidLoad { [super viewDidLoad]; [self configureRestKit]; [self loadVenues]; } - (void)configureRestKit { NSURL *baseURL = [NSURL URLWithString:@"https://api.foursquare.com"]; AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:baseURL]; RKObjectManager *objectManager = [[RKObjectManager alloc] initWithHTTPClient:client]; RKObjectMapping *venueMapping = [RKObjectMapping mappingForClass:[Venue class]]; [venueMapping addAttributeMappingsFromArray:@[@"name"]]; RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:venueMapping method:RKRequestMethodGET pathPattern:@"/v2/venues/search" keyPath:@"response.venues" statusCodes:[NSIndexSet indexSetWithIndex:200]]; [objectManager addResponseDescriptor:responseDescriptor]; } |
RKObjectManager是和RESTful services交互的核心,需要一个AFHTTPClient实例来进行初始化。
RKObjectMapping是用来配置JSON和本地model的映射信息,如果json和本地的model中都有name这个字段,你又需要解析这个字段,那么就要通过addAttributeMappingsFromArray进行添加。
RKResponseDescriptor描述了对HTTP返回数据的映射信息。pathPattern 就是api的具体路径,会被添加到baseURL后面。keyPath 是对象在json数据中的路径。看看上面的json数据,@“response.venues”说明了对象venue所处的逻辑位置,并告诉RestKit去哪找venue。
在 MasterViewController.m添加venues来存储venue变量
|
@interface MasterViewController () @property (nonatomic, strong) NSArray *venues; @end @implementation MasterViewController |
添加如下代码
|
- (void)loadVenues { NSString *latLon = @"37.33,-122.03"; // approximate latLon of The Mothership (a.k.a Apple headquarters) NSString *clientID = kCLIENTID; NSString *clientSecret = kCLIENTSECRET; NSDictionary *queryParams = @{@"ll" : latLon, @"client_id" : clientID, @"client_secret" : clientSecret, @"categoryId" : @"4bf58dd8d48988d1e0931735", @"v" : @"20140118"}; [[RKObjectManager sharedManager] getObjectsAtPath:@"/v2/venues/search" parameters:queryParams success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) { _venues = mappingResult.array; [self.tableView reloadData]; } failure:^(RKObjectRequestOperation *operation, NSError *error) { NSLog(@"What do you mean by 'there is no coffee?': %@", error); }]; } |
这段代向服务器请求数据,getObjectsAtPath中传递的参数是@“/v2/venues/search”,所以RestKit就会自动查找并使用上文配置的RKResponseDescriptor来映射Venue对象.取到数据后tableView就会正确显示信息了。

显示详细信息
我们想要在cell中显示咖啡店距离用户距离。 查看上文中的json数据,包含的距离的数据是
|
"location": { "address": "20686 Stevens Creek Blvd", "cc": "US", "city": "Cupertino", "country": "United States", "crossStreet": "De Anza Blvd", "distance": 936, "lat": 37.32246179607897, "lng": -122.03470838696346, "postalCode": "95014", "state": "CA" } |
新建命名为Location的model
|
@property (nonatomic, strong) NSString *address; @property (nonatomic, strong) NSString *city; @property (nonatomic, strong) NSString *country; @property (nonatomic, strong) NSString *crossStreet; @property (nonatomic, strong) NSString *postalCode; @property (nonatomic, strong) NSString *state; @property (nonatomic, strong) NSNumber *distance; @property (nonatomic, strong) NSNumber *lat; @property (nonatomic, strong) NSNumber *lng; |
在configureRestKit方法中添加
|
RKObjectMapping *locationMapping = [RKObjectMapping mappingForClass:[Location class]]; [locationMapping addAttributeMappingsFromArray:@[@"address", @"city", @"country", @"crossStreet", @"postalCode", @"state", @"distance", @"lat", @"lng"]]; [venueMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"location" toKeyPath:@"location" withMapping:locationMapping]]; |
通过上面的代码,当请求venue到数据后,可以直接通过venue.location.distance.floatValue来访问距离。 
这篇文章的例子中并未设计coreData对象的映射和存储,可以参照 官方文档 ,用法类似。