iOS/iPhone/iPad/watchOS/tvOS/MacOSX/Android プログラミング, Objective-C, Cocoa, Swiftなど
おそらく、Twitterフレームワークの為だと思うが、iOS5からNSJSONSerializationというJSONを扱うクラスが追加された。
このクラスは非常にシンプルで、用意されているメソッドは以下の5つだけだ。
ようするに、JSONデータとFondationの配列/辞書に相互に変換するメソッド。JSONデータについては、NSDataとストリームの2種類に対応ということだ。
これで、前回のRESTを使ったアプリケーションをJSON対応に変更できるはずだ。ただ、まだ、NSJSONSerializationを使った情報が世の中には少ないのと、railsアプリケーションが返す結果が、著者が想像していたのと事なってので、試行錯誤はあったが。
以下が、試行錯誤したコードだ。
- (IBAction)sendPost:(id)sender
{
NSURL *url = [NSURL URLWithString:@"http://localhost:3000/people.json"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setHTTPMethod:@"POST"];
NSMutableDictionary *person = [NSMutableDictionary dictionary];
[person setValue:@"MURAKAMI Yukio" forKey:@"name"];
[person setValue:@"18" forKey:@"age"];
NSError *error = nil;
NSData *content = [NSJSONSerialization dataWithJSONObject:person options:NSJSONWritingPrettyPrinted error:&error];
[request setHTTPBody:content];
NSHTTPURLResponse *response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
content = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
if (!error) {
NSString *s = [[NSString alloc] initWithFormat:@"status code: %d\ndata: %@", [response statusCode], content];
self.textView.text = s;
NSLog(@"%@", self.textView.text);
}
else {
NSString *s = [[NSString alloc] initWithFormat:@"error: %@", error];
self.textView.text = s;
NSLog(@"%@", self.textView.text);
}
}
- (IBAction)sendGet:(id)sender
{
NSURL *url = [NSURL URLWithString:@"http://localhost:3000/people/1.json"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"GET"];
NSHTTPURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
NSDictionary *content = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
if (!error) {
NSString *s = [[NSString alloc] initWithFormat:@"status code: %d\ndata: %@", [response statusCode], content];
self.textView.text = s;
NSLog(@"%@", self.textView.text);
}
else {
NSString *s = [[NSString alloc] initWithFormat:@"error: %@", error];
self.textView.text = s;
NSLog(@"%@", self.textView.text);
}
}
- (IBAction)sendGetList:(id)sender
{
NSURL *url = [NSURL URLWithString:@"http://localhost:3000/people.json"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"GET"];
NSHTTPURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
NSDictionary *content = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
if (!error) {
NSString *s = [[NSString alloc] initWithFormat:@"status code: %d\ndata: %@", [response statusCode], content];
self.textView.text = s;
NSLog(@"%@", self.textView.text);
}
else {
NSString *s = [[NSString alloc] initWithFormat:@"error: %@", error];
self.textView.text = s;
NSLog(@"%@", self.textView.text);
}
}
- (IBAction)sendPut:(id)sender
{
NSURL *url = [NSURL URLWithString:@"http://localhost:3000/people/1.json"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setHTTPMethod:@"PUT"];
NSMutableDictionary *person = [NSMutableDictionary dictionary];
[person setValue:@"MURAKAMI Yukio" forKey:@"name"];
[person setValue:@"81" forKey:@"age"];
NSError *error = nil;
NSData *content = [NSJSONSerialization dataWithJSONObject:person options:NSJSONWritingPrettyPrinted error:&error];
[request setHTTPBody:content];
NSHTTPURLResponse *response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
content = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
if (!error) {
NSString *s = [[NSString alloc] initWithFormat:@"status code: %d\ndata: %@", [response statusCode], content];
self.textView.text = s;
NSLog(@"%@", self.textView.text);
}
else {
NSString *s = [[NSString alloc] initWithFormat:@"error: %@", error];
self.textView.text = s;
NSLog(@"%@", self.textView.text);
}
}
- (IBAction)sendDelete:(id)sender
{
NSURL *url = [NSURL URLWithString:@"http://localhost:3000/people/1.json"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setHTTPMethod:@"DELETE"];
NSHTTPURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
NSDictionary *content = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
if (!error) {
NSString *s = [[NSString alloc] initWithFormat:@"status code: %d\ndata: %@", [response statusCode], content];
self.textView.text = s;
NSLog(@"%@", self.textView.text);
}
else {
NSString *s = [[NSString alloc] initWithFormat:@"error: %@", error];
self.textView.text = s;
NSLog(@"%@", self.textView.text);
}
}
JSONのContent-Typeは、application/jsonなのですね。RESTの時のapplication/xmlのままにしていたら、railsアプリケーションの方でXMLとして処理をしようとして、エラーになってしまった。
また、成功したら、HTTPステータスは200だと思っていたら、そうではなかった。それは、railsアプリケーションのpeople_controller.rbで以下のように記述しているからだと思う。
# POST /people
# POST /people.json
def create
@person = Person.new(params[:person])
Rubyを知らず、仕様も確認しないで当てずっぽうだが、POSTの場合、:statusを:createdに設定しているのでHTTPステータスは201(created)を返す。
# PUT /people/1
# PUT /people/1.json
def update
@person = Person.find(params[:id])
PUTの場合、:statusを:no_contentに設定しているので、HTTPステータスは204(no content)を返すようだ。何故、エラーとなるのか、少し、驚いてしまった。