RKGistのチュートリアルを適当に訳しました。
このチュートリアルはRestkit 0.20.0を使用しています。
このチュートリアルはRestkitに関する概念や使用方法を理解するためのものです。チュートリアルを読んだ後には、以下の内容に関して理解が得られるでしょう。
- Restkitとは何か、またそのメリットについて
- Restkitには、どのような思想があって、どう設計されているか
- Restkitの導入方法とwebサービスとの連携方法
- RestkitにおけるCore Dataの使用について
- RestkitとwebサービスAPIとの通信における抽象化について
このチュートリアルは初めてRestkitを導入したいと思っている方への入門編です。 Restkitを使用した開発経験は必須ではありませんが、Cocoaフレームワークに関する基本的な理解が必要です。 また、以下の環境を用意する必要があります。
- Xcode >= 4.6
- CocosPods >= 1.6.2
Restkitはwebサーバーと連携するクライアントアプリ用のフレームワークです。Restkitを触れる際に、CocoaフレームワークやObjec tive-Cについての知識が必要になります。それらの基礎を理解した上ででRestkitに触れることをお勧めします。
Restkitの根幹を理解するために、Cocoaフレームワークに関する下記の知識を深める必要があります。
- REST Restkitは、RESTfulクライアントアプリケーションを実装するための様々なツールを提供します。
- Key-Value Coding
- NSOperation NSOperationはCocoa Foundatioinをカプセル化した抽象クラスです。 RestKitでは、多くのクラスがNSOperationクラスをサブクラス化して実装されています。
- Core Data Core Dataはオブジェクトグラフの管理および永続化のためのフレームワークです。 RestKitは、RESTFul webサービスのクライアント側におけるバックエンドの永続化をCore Dataとの連携によって実現しています。
- AFNetworking AFNetworkingは快適な通信を可能にするネットワークライブラリです。 RestKitでは、通信処理をAFNetworkingを活用して実装しています。
この章では、RestKitの背景と概念を説明します。 >一旦この章を飛ばして、チュートリアルを一通り終えた後にこの章の説明を読んでも構いません。 >次章はRestKitのハンズオンを通して理解を深めます。
RestKitは、RESRful webサービス/クライアントアプリケーションをiOS/Mac OSXで実装するためのモダンなObjective-C用フレームワークです。 RestKitは、webサービスとやりとりするJSON、もしくはXMLデータと、アプリ内のデータモデルの両者を結合できるような構造になって いて、クライアントアプリ開発のスピードを向上できるように設計されています。 このライブラリを使用することで、実装上で良く目にする問題、特にCore Dataとの連携周りが今までより少ないコードで実装可能となっています。 また、RestKitは綺麗でテスト可能なコードとなっているため、メンテナンス性が非常に高くなっています。
RestKitは、様々なAPIの使用用途が想定されており、いくつかのデザインパターンが適用出来るようになっています。 しかし、全てのAPIにとってRestKitが最適というわけではなく、特にRPCには適していないでしょう。 どれだけ以下の状態に順応しているかが、RestKitの評価に繋がるでしょう。
- RESTful、もしくはRESTfulのようなAPIが使用されている。
RestKitは複数の別々の要素で構成されています。それぞれの要素は最低限の依存性を保ちつつ、独立したライブラリとしても機能します。 詳細部分の理解はひとまず置いておき、まずは個々の要素の機能について学びましょう。
オブジェクトマッピング要素は非常に多くに機能を提供しています。 キーバリューコーディングとダイナミックなObjective-cの機能間において、オブジェクトのマッピングが可能となっています。
ネットワーキング要素は、AFNetworkingによるHTTP通信とオブジェクトマッピング要素を統合しています。 JSON/XMLのパース、HTTPリクエスト/リスポンスでのオブジェクトマッピング、URLの生成機能、ローカルオブジェクトからHTTPへのパラメータ化が可能となっています。オブジェクトマネージャーやオブジェクトリクエストオペレーションなど、非常に良く使用されるクラスが含まれています。
Core Data要素は、RestKitによるネットワーク要素のオブジェクトマッピングと、Appleのデータモデルや永続化フレームワークとの統合が可能になっています。 基本的にこの要素は、オブジェクトマッピングとネットワーキング面に特化していますが、Core Dataにおける関係性の連結などの機能も含まれています。
まずはRKGistプロジェクトをGithubからクローンしましょう。 そして、必要なライブラリのインストールを行います。
$ git clone git@github.com/RestKit/RKGist.git
Podfileが下記のようになっているか確認してください。
platform :ios, 6.0
pod 'RestKit', '~> 0.20.0rc'
# Include optional Testing and Search components
pod 'RestKit/Testing', '~> 0.20.0rc'
pod 'RestKit/Search', '~> 0.20.0rc'
# Import Expecta for Testing
target :test do
link_with :RKGistTests
pod 'Expecta', '0.2.1'
end
下記のコマンドでライブラリをインストールしましょう。
$ pod install
//
// Prefix header for all source files of the 'RKGist' target in the 'RKGist' project
//
#import <Availability.h>
#ifndef __IPHONE_5_0
#warning "This project uses features only available in iOS SDK 5.0 and later."
#endif
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#endif
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#import <SystemConfiguration/SystemConfiguration.h>
#import <MobileCoreServices/MobileCoreServices.h>
#else
#import <SystemConfiguration/SystemConfiguration.h>
#import <CoreServices/CoreServices.h>
#endif
// Make RestKit globally available
#import <RestKit/RestKit.h>
Gist APIを使用してデータをマッピングします。 Gistリソースに関してはGithub API v3 documentationに目を通してください。 認証無しで、パブリックなgistを取得することが出来ます。https://api.github.com/gists/publicを叩くと、以下のようなJSONを取得出来ます。
{
"url": "https://api.github.com/gists/4774273",
"forks_url": "https://api.github.com/gists/4774273/forks",
"commits_url": "https://api.github.com/gists/4774273/commits",
"id": "4774273",
"git_pull_url": "https://gist.github.com/4774273.git",
"git_push_url": "https://gist.github.com/4774273.git",
"html_url": "https://gist.github.com/4774273",
"files": {
"gistfile1.txt": {
"filename": "gistfile1.txt",
"type": "text/plain",
"language": null,
"raw_url": "https://gist.github.com/raw/4774273/b8737fef0dd6403b2dc5d51b6fb2fd0a9141f484/gistfile1.txt",
"size": 58
}
},
"public": true,
"created_at": "2013-02-12T22:58:52Z",
"updated_at": "2013-02-12T22:58:53Z",
"description": "My first gist!",
"comments": 0,
"user": {
"login": "RKGistExample",
"id": 3541778,
"avatar_url": "https://secure.gravatar.com/avatar/cccf809ee0b340976d241cb93cc0b601?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png",
"gravatar_id": "cccf809ee0b340976d241cb93cc0b601",
"url": "https://api.github.com/users/RKGistExample",
"followers_url": "https://api.github.com/users/RKGistExample/followers",
"following_url": "https://api.github.com/users/RKGistExample/following",
"gists_url": "https://api.github.com/users/RKGistExample/gists{/gist_id}",
"starred_url": "https://api.github.com/users/RKGistExample/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/RKGistExample/subscriptions",
"organizations_url": "https://api.github.com/users/RKGistExample/orgs",
"repos_url": "https://api.github.com/users/RKGistExample/repos",
"events_url": "https://api.github.com/users/RKGistExample/events{/privacy}",
"received_events_url": "https://api.github.com/users/RKGistExample/received_events",
"type": "User"
},
"comments_url": "https://api.github.com/gists/4774273/comments"
}
上記はAPIで取得出来るパラメータの一部です。
- id : gistのID
- url : JSONを取得出来るURL
- html_url : HTMLを取得出来るURL
- description : gistの説明
- public : 公開か非公開かのフラグ
- created_at : 作成日
- id : ユーザID
- login : Githubのユーザ名
- avatar_url : ユーザーのアバター画像URL
- url : JSONを取得出来るURL
取得したJSONデータをカプセル化するために、Core Dataのデータモデルが必要になります。 RestKitを使用する際に、Core Dataを利用してオブジェクトのモデル化を行いましょう。 HTTPリクエストで取得したオブジェクトは、管理すべきものと管理しないオブジェクトを自由に組み合わせられます。
Core Dataのエンティティを作成するには、RKGist.xcdatamodeld
ファイルを追加する必要があります。 もし既にテンプレートによる既存ファイルがある場合は、そのファイルを削除してください。 次に、Gistエンティティを追加しましょう。 以下の属性を追加してください。
属性名 | 型 |
---|---|
gistID | Stinrg |
jsonURL | Transformable |
htmlURL | Transformable |
descriptionText | String |
public | Boolean |
createdAt | Date |
updatedAt | Date |
次はUserエンティティの作成です。
属性名 | 型 |
---|---|
userID | Integer32 |
jsonURL | Transformable |
login | String |
gravatarID | String |
avatarURL | Transformable |
最後にFileエンティティの作成です。
属性名 | 型 |
---|---|
filename | String |
rawURL | Transformable |
size | Integer32 |
全てのエンティティの作成が終わったら、以下の通りにリレーションの設定を行います。
エンティティ | 関係名 | 関連 | 逆関連 | 多対多関係 |
---|---|---|---|---|
File | gist | Gist | files | × |
Gist | files | File | gist | ○ |
Gist | user | User | gists | × |
User | gists | Gist | user | ○ |
RKGAppDelegate.m
に以下のコードを追加してください。
#import <RestKit/RestKit.h>
#import "RKGAppDelegate.h"
#import "RKGMasterViewController.h"
@implementation RKGAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSError *error = nil;
NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"RKGist" ofType:@"momd"]];
// NOTE: Due to an iOS 5 bug, the managed object model returned is immutable.
NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
// Initialize the Core Data stack
[managedObjectStore createPersistentStoreCoordinator];
NSPersistentStore __unused *persistentStore = [managedObjectStore addInMemoryPersistentStore:&error];
NSAssert(persistentStore, @"Failed to add persistent store: %@", error);
[managedObjectStore createManagedObjectContexts];
// Set the default store shared instance
[RKManagedObjectStore setDefaultStore:managedObjectStore];
// Override point for customization after application launch.
UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
RKGMasterViewController *controller = (RKGMasterViewController *)navigationController.topViewController;
controller.managedObjectContext = managedObjectStore.mainQueueManagedObjectContext;
return YES;
}
@end
まず、AppleがCore Data用に提供しているクラスではなく、RKManagedObjectStore
というRestKitが提供しているクラスを使用します。 'RKManagedObjectStore'は、Core Dataに関する全ての機能をNSManagedObjectContext
と共にカプセル化したクラスです。
createManagedObjectContexts
を実行すると、以下のような親子階層のmanaged object contextsが生成されます。
persistentStoreManagedObjectContext
:persistentStoreCoordinator
と直接関係を持っているルートコンテキストです。NSPrivateQueueConcurrencyType
と一緒に初期化され、 I/O処理をメインスレッド外で行えます。mainQueueManagedObjectContext
:persistentStoreManagedObjectContext
の子関係であり、NSMainQueueConcurrencyType
と一緒に初期化されます。 メインキューコンテキストは汎用的な利用に適しています。
以下のコードを実装することで、コンテキストの調整を視覚的に理解することも可能です
[INSERT FRAPHIC HERE SHOWING THE MOC CONFIGURATION
Core Dataのデータモデルの設定が一通り終わったので、簡単なフォーマットでpublic gistsの表示させてみましょう。
RKGMasterViewController.m
ファイルを開いて、fetchedResultsController
メソッドに移動してください。 NSEntityDescription
のEvent
エンティティをGist
エンティティに変更しましょう。
- NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event" inManagedObjectContext:self.managedObjectContext];
+ NSEntityDescription *entity = [NSEntityDescription entityForName:@"Gist" inManagedObjectContext:self.managedObjectContext];
次に、sort descriptorをtimeStamp
からcreatedAt
へ変更しましょう。
- NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timeStamp" ascending:NO];
+ NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"createdAt" ascending:NO];
これで一旦ビルドして、Gist情報を表示出来るか確認してみましょう。
このアプリケーションは、Core DataからGist情報を表示出来るように設定してあります。 しかし、まだデータ自体がストアされていません。 RestKitを使って、マッピングの設定を行いましょう。
viewDidLoad
メソッドへ移動して、下記のコードを末尾に追加してください。
// Load the public Gists from Github
RKManagedObjectStore *managedObjectStore = [RKManagedObjectStore defaultStore];
RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:@"Gist" inManagedObjectStore:managedObjectStore];
[entityMapping addAttributeMappingsFromDictionary:@{
@"id": @"gistID",
@"url": @"jsonURL",
@"description": @"descriptionText",
@"public": @"public",
@"created_at": @"createdAt"}];
RKLogConfigureByName("RestKit/Network", RKLogLevelTrace);
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping
method:RKRequestMethodGET
pathPattern:@"/gists/public"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://api.github.com/gists/public"]];
RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]];
managedObjectRequestOperation.managedObjectContext = self.managedObjectContext;
[[NSOperationQueue currentQueue] addOperation:managedObjectRequestOperation];
次に、configureCell:atIndexPath:
へ移動して、以下の通りにコードを置き換えてください。
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [[object valueForKey:@"descriptionText"] description];
}
もう一度ビルドをしてアプリを実行すると、Gist情報が見れるようになっているはずです。 コンソールにはJSONデータが表示されているでしょう。
さて、上記のコードを実装するだけで、データ取得と表示が可能になりました。 ここからさらに利用可能なクラスやメソッドを深堀したいところではありますが、一旦今まで行った内容を概念ベースで振り返ってみましょう。
- JSONからCore DataのGistエンティティへのマッピング定義
- HTTPレスポンスとGistエンティティ用のマッピングの結合
- オブジェクトマッピングを使用した非同期通信の実装
- リクエスト情報を取得後に表示処理を行う
次に、それぞれの処理を深堀して、RestKit APIに慣れましょう。
RestKitでの全てのマッピング処理は、オブジェクトマッピングに関するものです。 いくつかの利用可能なマッピングが用意されています。 RKObjectMapping
, RKEntityMapping
, RKDynamicMapping
などです。 しかし、これらは全てRKMapping
を継承しています。 RKEntityMapping
はRKObjectMapping
のサブクラスで、Core Dataに関する基本機能を拡張しています。
RKGist
が永続化のためにCore Dataを使用しているので、KKEntityMapping
を初期化しました。 Core Dataのエンティティをオブジェクトマッピング出来るように、エンティティマッピングが定義されています。 RKEntityMapping
の初期化メソッドはinitWithEntity
ですが、mappingForEntityForNAme:inManagedObjectStore:
を使用する方が一般的です。 事前にアプリ内で設定したmanaged object storeへの参照が、[RKManagedObjectStore defaultStore]
シングルトンで取得出来ます。 mappingForEntityForName:inManagedObjectStore:
にエンティティ名とmanaged object storeインスタンスを渡すことで、オブジェクトマッピングを使用する準備が整います。
これでマッピングの初期化が完了したので、addAttributeMappingsFromDictionary:
に、JSONのキー名と値の対応と、Core DataのGistエンティティのキー名と値を対応させるdictionary構造を渡して登録します。
- (void)addAttributeMappingsFromDictionary:(NSDictionary *)keyPathToAttributeNames
{
for (NSString *attributeKeyPath in keyPathToAttributeNames) {
[self addPropertyMapping:[RKAttributeMapping attributeMappingFromKeyPath:attributeKeyPath toKeyPath:[keyPathToAttributeNames objectForKey:attributeKeyPath]]];
}
}
RKPropertyMapping
が属性や関係のマッピングを定義します。 RKPropertyMapping
はRKAttributeMapping
クラスとRKRelationshipMapping
クラスを持った抽象クラスです。
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping method:RKRequestMethodAny pathPattern:@"/gists/public" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://api.github.com/gists/public"]];
RKManagedObjectRequestOperation *managedObjectRequestOperation = [[RKManagedObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]];
managedObjectRequestOperation.managedObjectContext = self.managedObjectContext;
[[NSOperationQueue currentQueue] addOperation:managedObjectRequestOperation];