サーバー連携アプリを簡単に制作してみよう！Google Apps EngineとXcodeの連携の手順

XcodeとGoogle Apps Engine（GAE）を連携させて、サーバー連携アプリを開発してみました。
ついでに手順をまとめてみましたので、ご参考にどうぞ。
長くてすいません。

開発環境は以下の通りです。
・Mac OS 10.9 Mavericks
・Xcode 5.0
・Eclipse 4.3 Kepler（Android開発環境を導入している方は、新たにEclipseをインストールした方がいいかもしれません）

Google Apps Engine側
1.GAEの登録
以下のリンクからGAEへの登録を行いましょう。初回のみ携帯電話を使った認証が求められます。
https://developers.google.com/appengine/?hl=ja

2.Application Identifierの入手
GAEにアプリの登録を行い、Application Identifierを入手します。
詳しくは以下などを参考にどうぞ。
https://developers.google.com/appengine/docs/java/gettingstarted/uploading?hl=ja

Mac側
3.Java1.7のインストール
最新のJavaがインストール済みの方はここは飛ばしてください。
Mavericksには標準でJava1.7がインストールされていませんので（注：インストール済みの場合もありますので、その場合はこの節は飛ばしてください）、手動でインストールする必要があります。
以下のOracleのサイトから、OSX用のJDKをダウンロードし、インストール。
http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html

Eclipse側
4.Java1.7を利用可能にする
・Eclipse->環境設定...->Java->Installed JREsを表示
・Java SE 7が表示されている方はチェックして次の節に進んでください。表示されていない方は以下の手順を進めてください。
・Addをクリック
・Standard VMを選択
・JRE homeに
/Library/Java/JavaVirtualMachines/1.7.0.jdk/Contents/Home
を張り付ける。 （1.7.0.jdkの箇所はインストールしたJavaのバージョンによって異なるので注意）
・JRE nameをJava SE 7などのお好きな名前に変更。
・Finishをクリック
・新しく作成したJREのチェックボックスをオンにする。
参考:http://stackoverflow.com/questions/14105575/eclipse-on-mac-10-8-installed-1-7-0-jre-jdk-but-eclipse-wont-launch

5.Google Plugin for Eclipseのインストール
・EclipseのHelp→Install New Softwareを選択。
・新しいWindowが開くので、Work with:のところに
http://dl.google.com/eclipse/plugin/4.3
と入力。
・全て選択しNext。
・ライセンスに同意しFinish
以上の手順でインストールが始まります。Security Warinigが表示される場合はOKを押します。

6.プロジェクトの作成
・左上のGoogleのマークが描かれたアイコンをクリックし、New Web Application Projectを選択。
・プロジェクト名、パッケージ名（com.myhogehogeappなど）を設定しFisnish。

7.JSONICのインストール
サーバー側の処理を簡単に行うためにここではJSONICというライブラリを使います。
・以下のサイトから、JSONICの最新版をダウンロードし解凍します。
http://jsonic.sourceforge.jp/
・プロジェクトのwar->WEB-INF->libを右クリックし、Importを選択。
・Generalの中のFile Systemを選択しNextボタンをクリック。
・Browzeで先ほど解凍したフォルダを選択。
・jsonic-***.jarをチェックし、Finish。
JSONICに関しての詳細は上記のサイトで。

8.サーブレットの作成
・com.myhogehogeapp.server（com.myhogehogeappはプロジェクト作成時に設定したパッケージ名）の上で右クリックし、New->Classを選択。
・NameをGAETestServiceにして、Finish。
・GAETestService内に、以下の記述をします。このクラスはアプリ側から命令を受け取ったときの処理を行うクラス。POST（アプリ→サーバーへの投稿）の際はcreateメソッドが、GET（アプリからリクエスト→サーバーから値をアプリ側に返す）の際はfindメソッドが呼ばれます。

```java
package com.myhogehogeapp.server;

import java.util.List;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;

public class GAETestService {
	
	private static final PersistenceManagerFactory PMF = 
			JDOHelper.getPersistenceManagerFactory("transactions-optional");
	
	//Post Request
	public void create(GAERecievedData gRData){
		
		PersistenceManager pm = PMF.getPersistenceManager();
		try {
			pm.makePersistent(gRData);
		} finally {
			pm.close();
		}
		
		System.out.println(gRData.key1);
		System.out.println(gRData.key2);

	}

	// GET Request
	public List<GAERecievedData> find(GAERecievedData gRData) {
		PersistenceManager pm = PMF.getPersistenceManager();
		//Queryを作成
		Query query = pm.newQuery(GAERecievedData.class);
		//Queryを実行
		List<GAERecievedData> results = (List<GAERecievedData>)query.execute();
		
		System.out.println("サイズ：　" + results.size());
		System.out.println("key1 First：　" + results.get(0).key1);
		System.out.println("key1 Last：　" + results.get(results.size() -1).key1);
		
		return results;
	}
}
```

この時点ではまだGAERecievedDataクラスを作成していないのでエラーが表示されています。
・同様の手順でGAERecievedDataクラスを作成し、中に以下の記述をします。このクラスの中に、JASONなどの形式で受け取ったデータが格納され、GAETestServiceクラスに引数として渡されます。

```
package com.myhogehogeapp.server;

import javax.jdo.annotations.*;
import com.google.appengine.api.datastore.Key;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class GAERecievedData {
	
	@PrimaryKey
	@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
	public Key key;
	
	@Persistent
	public String key1;
	
	@Persistent
	public String key2;
	
}
```
・ここで、key1とkey2にはそれぞれをJSONのキーとしたときの対応する値が自動的に格納されます。JSONIC便利ですね。

9.サーブレットの設定
・war->WEB-INF->web.xmlをダブルクリックして開く。
・以下の記述を追加

```
  <servlet-mapping>
    <servlet-name>myServlet</servlet-name>
    <url-pattern>*.json</url-pattern>
  </servlet-mapping>
```

この記述により、.jsonという拡張子を持ったURLにアクセスするとmyServletという名前のサーブレットが呼び出されるようになります。

・以下のサーブレットの記述を追加

```
  <servlet>
    <servlet-name>myServlet</servlet-name>
    <servlet-class>net.arnx.jsonic.web.RESTServlet</servlet-class>
    <init-param>
        <param-name>config</param-name>
        <param-value>
        {
            "debug": true,
            "mappings": {
                "/GAETest.json": "com.myhogehogeapp.server.GAETestService"
            }
        }
        </param-value>
    </init-param>
  </servlet>
```

com.myhogehogeappはパッケージ名ですので、プロジェクト作成時に決めたパッケージ名と合わせてください。
この記述により、GAETest.jsonが呼ばれた場合はcom.myhogehogeapp.server.GAETestService内の命令が実行されることになります。

Xcode側
10.POST（アプリ→サーバーへの投稿）
以下のメソッドをViewController上に記述し、StoryBoard上のボタンと接続します。

```objc
-(IBAction)POSTJSON:(id)sender{
    //JSON
    NSDictionary *jsonDic = @{@"key1": @"Value1",
                              @"key2": @"Value2"
                              };
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDic options:0 error:nil];
    
    //リクエストの作成
    NSURL *serviceURL = [NSURL URLWithString:@"http://localhost:8888/GAETest.json"];
    NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:serviceURL];
    [req setHTTPMethod:@"POST"];
    [req addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    [req setHTTPBody:jsonData];
    
    // 通信先と接続（同期通信）
    [NSURLConnection sendSynchronousRequest:req returningResponse:nil error:nil];
}
```

11.GET（アプリからリクエスト→サーバーから値をアプリ側に返す）
以下のメソッドをViewController上に記述し、StoryBoard上のボタンと接続します。

```objc
-(IBAction)GETJSON:(id)sender{
    //パラメータ付きのURLを作成
    NSString *url = @"http://localhost:8888/GAETest.json";
    NSString *parameter = [NSString stringWithFormat:@"key1=%@", @"Value1"];
    NSString *urlWithParameter = [NSString stringWithFormat:@"%@?%@", url, parameter];
    
    //サーバーと通信（URLリクエストを作成:GET）
    NSURL *serviceURL = [NSURL URLWithString:urlWithParameter];
    NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:serviceURL];
    [req setHTTPMethod:@"GET"];
    [req addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    
    //通信先と接続（同期通信）
    NSData *receivedData = [NSURLConnection sendSynchronousRequest:req returningResponse:nil error:nil];

    //結果を配列に格納し表示
    NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:receivedData options:0 error:nil];
    NSLog(@"Received JSON: %@", jsonArray);
}
```

Eclipse側
12.テスト
・プロジェクト名を右クリックし、Debug As->Web Applicationを選択します。
・ローカル環境、http://localhost:8888でこのウェブアプリが実行されます。

Xcode側
13.テスト
先程のPOSTとGETの記述を行ったプロジェクトをシミュレータで実行します。
・POSTのボタンで投稿が行われ、サーバー側（この場合はローカル）に保存されます。GETのボタンで保存した内容をがアプリ側に返ります。Xcodeのコンソールで確認しましょう。
・必要に応じて、Eclipse側にブレークポイントを設定して動作を確認しましょう。

Eclipse側
14.デプロイ
GAEに作成したウェブアプリを送信します。
・war->WEB-INF->appengine-web.xmlを開き、上の方にある<application></application>の間に2.で取得したApplication Identifierをコピペします。
・左上のgと書かれたGoogleのマークのアイコンから、Deploy to App Engineを選択します。
・ウィンドウ右下のDeployをクリックします。
・ローカル上のウェブアプリがGAEにアップされます。少々時間がかかりますが、Successfullyという完了のメッセージが表示されるまで待ちます。

Xcode側
15.GAEテスト
・コード上のhttp://localhost:8888/GAETest.jsonの箇所を、
http://(Your App ID).appspot.com/GAETest.json
に変更します。(Your App ID)は2.で取得したGAEのApplication Identifierです。
・アプリをシミュレータで実行します。POSTのボタンで投稿、GETのボタンでデータの取得ができるはずです。
・GAEの管理画面->LogsでGAE上のこのアプリへのアクセス履歴を確認することができます。POST、GETが正しく行われていることを確認しましょう。

16.最後に
GAE、とても便利ですね。
慣れるまでが少々大変ですが、慣れる価値は十分あるかと思います。
Webサイトなどもアップできるようですので、機会があればその方法を書いていきたいと思います。
サーバー側でさらに込み入った処理を行いたい方は、以下などを参考にどうぞ。
https://developers.google.com/appengine/docs/java/datastore/jdo/creatinggettinganddeletingdata?hl=ja

本記事の作成に当たり、以下のセミナーで学んだ内容を参考にさせていただきました。
http://school.rainbowapps.com/yokohama/gae

また、以下の書籍も参考にさせていただきました。
http://www.amazon.co.jp/%E3%81%BE%E3%82%8B%E3%81%94%E3%81%A8%E5%AD%A6%E3%81%B6iPhone%E3%82%A2%E3%83%97%E3%83%AA%E5%88%B6%E4%BD%9C%E6%95%99%E5%AE%A4-%E7%80%AC%E8%B0%B7-%E5%95%93%E4%BB%8B/dp/4797364629