Apple の iPhone を Google のクラウド・コンピューティングに接続する

携帯機器のためのクラウド・コンピューティングとソフトウェア開発は、非常にホットな 2 つの技術であり、両者を組み合わせてハイブリッド・ソリューションを作成することが多くなっています。この記事では、Google App Engine と Google のクラウド・コンピューティングを Apple のモバイル・プラットフォームである iPhone と接続する方法を学びます。またオープンソースのライブラリーである TouchEngine を使って App Engine クラウドに接続し、iPhone アプリケーションのデータをオフラインでの使用に備えてキャッシュすることで、そのアプリケーションのデータを動的に制御する方法も学びます。

Noah Gift , Founder, GiftCS, LLC

Photo of Noah GiftNoah Gift は O'Reilly から出版されている『Python For Unix and Linux System Administration』の共著者であり、また Manning から出版予定の『Google App Engine In Action』も執筆中です。彼は著作者、講演者、コンサルタント、そしてコミュニティーのリーダーとして、IBM developerWorks や Red Hat MagazineO'ReillyMacTech などに記事を寄稿しています。彼のコンサルティング会社の Web サイトは http://www.giftcs.com であり、彼が寄稿した記事の大部分は http://noahgift.com で公開されています。彼はカリフォルニア州立大学ロサンゼルス校でコンピューター情報システムの修士号を、カリフォルニア州立工科大学 San Luis Obispo 校で栄養科学の学位を取得しています。また、Apple と LPI 認定のシステム管理者であり、Caltech、Disney Feature Animation、Sony Imageworks などの企業に勤務経験があります。現在はニュージーランドの Weta Digital の業務を行っています。仕事以外の時間には彼の妻である Leah と息子の Liam と時間を過ごし、ピアノ曲を作曲し、マラソンをし、そして神に祈りを捧げています。



Jonathan Saggau, Founder and CEO, Sounds Broken inc

Photo of Jonathan SaggauJonathan Saggau は Sounds Broken inc の創始者であり CEO (最高経営責任者) です。Sounds Broken inc は Mac OS X と iPhone のソフトウェア契約を結んだ企業であり、また技術プロセスやビジネス・プロセスのコンサルタント会社でもあります。彼は飛行機を操縦したりハードウェアやソフトウェアのリバース・エンジニアリングをしたりしている時以外には、Equity Audio、Innovative Audio、Big Nerd Ranch などの顧客と協力しながら傑出した製品やサービス、プロセスの開発を行っています。彼のブログは jonathansaggau.com/blog/ です。



2009年 1月 20日

はじめに

この 2、3 年、爆発的とも言えるほど技術革新が次々と行われ、2008年は技術面で興味深い一年になりました。そのなかでも非常に興味が惹かれる 2 つの技術革新が、クラウド・コンピューティングとモバイル・アプリケーションの技術の発展です。この記事では、この 2 つの技術を活用し、その相乗効果によって開発者の夢を実現するための通信手法について学びます。そのために、Google のクラウド・コンピューティング・プラットフォームである Google App Engine、そして Apple のモバイル・プラットフォームである iPhone を使ってアプリケーションを作成し、そのアプリケーションのデータを「クラウド」からのデータと同期させます。

ここでは単純な方法を使って App Engine から iPhone アプリケーションのデータを取得しますが、面倒な部分は Python と App Engine が処理してくれます。RSS、ATOM、または REST を使って iPhone にデータを配信する一般的な方法は極めて単純ですが、そのためにはパーサーを作成しなければなりません。それよりも、XML プロパティー・リスト、つまり plist を使った方がはるかに簡単です。プロパティー・リストのマニュアル (「参考文献」を参照) によれば、「プロパティー・リストを利用すると、いくつかの Core Foundation 型 (CFString、CFNumber、CFBoolean、CFDate、CFData、CFArray、CFDictionary) を使用した名前付きの値と値のリストとしてデータを構成することができます。これらの型によって、できるだけ効率的に、移植、保存、アクセスすることが可能な、意味のある構造を持ったデータを生成することができます。」

plist を使用すると、iPhone 上で XML を構文解析するという難問を解消することができます。plist は、Cocoa Touch が自動的に構文解析して意味のあるオブジェクトにしてくれる XML ファイル・フォーマットであるからです。App Engine で Python による plist のライブラリーを使用することで、単純な Python ディクショナリー・オブジェクトのほとんどを最小限の手間で iPhone に送信することが可能になります (ただし Python ディクショナリーの中のデータ型は plist で使用可能なデータ型でなければなりません)。この記事では、この手法について説明します。その説明のなかでは、TouchEngine というオープンソースのライブラリーを使用して、ウィリアム・シェイクスピアのソネットを表示するアプリケーションを作成します。「参考文献」には、Google Code にある、このプロジェクトへのリンクを挙げてあります。

背景

まず、iPhone SDK と Google App Engine についての背景情報を少し説明しましょう。

iPhone SDK

Native iPhone SDK は Objective-C 言語で作成されています。この SDK での開発は、Mac OS X® で Cocoa プログラミングを行う場合と非常によく似ていますが、この SDK にはさらに iPhone のユニークな機能を活用する API が含まれています (GPS やマルチ・タッチ、加速度計、オンスクリーン・キーボードなど)。将来のロードマップにはプッシュ型の通知などの技術のサポートが含まれています。iPhone Native SDK に関する詳しい情報は「参考文献」を参照してください。

iPhone は、モバイル・アプリケーションの開発者にリッチな開発環境を提供します。Objective-C は最近までほとんど NeXT と Apple 専用であったため、多くの開発者にとっては未知の言語だったのですが、Cocoa Touch SDK が公開されてから広く使われるようになりました。iPhone では、まったく新しい世代のモバイル・アプリケーション開発者にとって、Objective-C が開発の中心になっています。

Google App Engine とは何か

クラウド・コンピューティングは、最近、Amazon が S3 ストレージと EC2 エラスティック・コンピューティング・サービスを提供するようになってから、認知度が高まってきました。サービス・ベースのクラウド・コンピューティング・マーケットに新たに登場したものが Google App Engine です。Google App Engine では、スケーラブルで有名な Google のデータ・センターに対する API が Python で提供されています (他の言語でもまもなくサポートされる予定です)。これは従来からの大きな転換であり、ソフトウェア開発者はアプリケーションのスケーラビリティー管理に付きものの複雑な作業から解放され、アプリケーションの作成のみに集中できるようになります。

Google App Engine から plist ファイルを生成する

まず、Google App Engine から plist ファイルを生成する方法を調べてみましょう。この plist ファイルは後ほど、iPhone Cocoa Touch SDK を使って iPhone 上で使用することになります。App Engine は最初は無料なので、問題を解決しようとするモバイル・アプリケーション開発者にとって、プロトタイプを作成する上での興味深い選択肢になります。しかもその API は Python で作成されています。Python は開発を迅速に進める上で有効なことで知られており、また非常に生産性の高いインタープリター言語です。iPhone アプリケーションの開発で面倒な部分やデータのストレージを App Engine と Python を利用してクラウドに任せると、大きな効果を得ることができます。

この記事を読み進むためには App Engine SDK をダウンロードする必要があります。(「参考文献」には最新バージョンへのリンクを挙げてあります。) App Engine を使うと数分で簡単にプロトタイプを動作させることができます。この記事に記載のコード・サンプルはこの記事に付属のソース・コード・ファイルをダウンロードしても入手することができます。

plist ファイルを用意して iPhone アプリケーションで利用できるようにするには、App Engine プロジェクト・ディレクトリーの plistlib.py をインポートし、main.py スクリプトを少し変更し、さらに sonnet.py をインポートします。sonnet.py は、シェイクスピアによるすべてのソネットのテキストのディクショナリーを含んだ Python ソース・ファイルです。リスト 1 は main.py ファイルを示しています。

リスト 1. main.py
#!/usr/bin/env python
#Python sonnet maker

import wsgiref.handlers
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app

#external imports
import sonnet
import plistlib

class MainHandler(webapp.RequestHandler):
    """Returns sonnets dictionary as a converted plist"""
    def get(self):
        plist = plistlib.writeplistToString(sonnet.verses)
        self.response.out.write(plist)

def main():
    application = webapp.WSGIApplication([('/plists/sonnets', MainHandler),
                                        ],
                                        debug=True)
    run_wsgi_app(application)


if __name__ == '__main__':
  main()

この小さなコード・スニペットはシェイクスピアのソネットを含むディクショナリーの内容を取得し、それを XML plist に変換し、/plists/sonnets という URL にリクエストを送信するクライアントに提供します。信じられないかもしれませんが、これがこの記事で説明する Google App Engine アプリケーションの大部分なのです。リスト 2 は sonnet.py のごく一部を示したものです。

リスト 2. sonnet.py のサンプル
verses={"verses":[["I","""FROM fairest creatures we desire increase,
That thereby beauty's rose might never die,
But as the riper should by time decease,
His tender heir might bear his memory:
But thou, contracted to thine own bright eyes,
Feed'st thy light'st flame with self-substantial fuel,
Making a famine where abundance lies,
Thyself thy foe, to thy sweet self too cruel.
Thou that art now the world's fresh ornament
And only herald to the gaudy spring,
Within thine own bud buriest thy content
And, tender churl, makest waste in niggarding.
  Pity the world, or else this glutton be,
  To eat the world's due, by the grave and thee."""]}

[NOTE: EDITED FOR SPACE]

main 関数は /plists/sonnets という URL を MainHandler クラスにルーティングします。クライアントが HTTP GET を使ってデータを要求した場合には、この関数はリスト 3 のような結果を返します。

リスト 3. HTTP GET の結果
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD plist 1.0//EN" 
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>verses</key>
    <array>
        <array>
            <string>I</string>
            <string>FROM fairest creatures we desire increase,
That thereby beauty's rose might never die,
But as the riper should by time decease,
His tender heir might bear his memory:
But thou, contracted to thine own bright eyes,
Feed'st thy light'st flame with self-substantial fuel,
Making a famine where abundance lies,
Thyself thy foe, to thy sweet self too cruel.
Thou that art now the world's fresh ornament
And only herald to the gaudy spring,
Within thine own bud buriest thy content
And, tender churl, makest waste in niggarding.
  Pity the world, or else this glutton be,
  To eat the world's due, by the grave and thee.</string>
        </array>
        <array>
[NOTE: EDITED FOR SPACE]

isonnet プロジェクトのページ (「参考文献」にリンクがあります) にアクセスすれば、この出力を実際に見ることができます。この URL を表示すると、シェイクスピアのソネットが、編集された完全な plist として表示されます。ただしブラウザーによっては、これを巨大なフラット・テキスト・ファイルとして表示するかもしれません。XML plist は妥当な XML であり、大部分のブラウザーはそれを表示しようとします。ページのソースを表示すると、フォーマット設定された plist を見ることができます。

参考文献」には、Python や App Engine の詳細な例や、Google App Engine に関する詳細なチュートリアルへのリンクを挙げてあります。しかしここでは、iPhone アプリケーションがこの plist のデータを取り込んでどのようにしてアプリケーションのデータを変更するのかを調べることにしましょう。

Google App Engine から XML plist ファイルを動的に読み取ってキャッシュする iPhone アプリケーションを作成する

TouchEngine には一連のオブジェクトが含まれており、これらを利用すると非常に簡単に iPhone に XML plist をダウンロードしてキャッシュすることができます。ソネットの plist をダウンロードしてキャッシュするためにここで使用するオブジェクトは、そのオブジェクトのヘッダー・ファイル (GRplistController.h) に記述されています (リスト 4)。

リスト 4. GRplistController.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import "Reachability.h"
#import "GRplistControllerDelegateProtocol.h"

typedef enum {
    kGRplistDownloadCannotInitiate = 0,
    kGRplistConnectionFailure,
    kGRplistFileFormatFailure
} GRErrorCode;

#define GR_ERROR_DOMAIN @"GRplistController_Error_Domain"

@class GRplistModel;

// This class will grab a remote plist from the server whenever update 
// is called.

// Also registers with the Reachability object for notifications when
// the remote host changes availability and updates accordingly
// asking the delegate first if downloading new data is desirable.

@interface GRplistController : NSObject {
    NSURL *remoteURL;
    NSObject <GRplistControllerDelegate> *delegate;

    NetworkStatus remoteHostStatus;
    NetworkStatus internetConnectionStatus;
    NetworkStatus localWiFiConnectionStatus;
    BOOL loadingData;
@private
    GRplistModel *_model;
    BOOL hostIsReachable;
    NSMutableDictionary *plistIndex;
    NSMutableData *receivedData;
    NSURLConnection *connection;
}

@property(nonatomic, retain)NSURL *remoteURL;
@property(nonatomic, assign)NSObject *delegate;
@property(nonatomic, readonly)GRplistModel *model;

//date of last download
@property(nonatomic, retain)NSDate *lastUpdate;
@property(nonatomic, getter=isLoadingData)BOOL loadingData;
@property NetworkStatus remoteHostStatus;
@property NetworkStatus internetConnectionStatus;
@property NetworkStatus localWiFiConnectionStatus;

//designated Initializer
- (id)initWithRemoteURL:(NSURL *)aRemoteURL;

- (void)updateDataFromDisk;
- (void)download;
- (void)cancelDownload;
@end

このクラスを使うためには、XML plist が置かれる NSURL を用意して、データをダウンロードするか、あるいはディスクからデータを取得するようにこのクラスに命令します。GRplistController はユーザーのデータ保存ディレクトリーの中に plist ディクショナリー・ファイルを生成し、キャッシュされた plist の場所と最後にダウンロードした日付を保存してリモートの URL と関連付けます。plist が一度ダウンロードされると、その後はディスクから直接データがロードされます。また、指定された URL から最後にダウンロードした日付と時刻を、lastUpdate プロパティーを使って GRplistController オブジェクトに照会し、いつ Web からのデータで更新すればよいのかを判断します。GRplistController は必ず NSURLConnection を使って非同期にデータをダウンロードするため、新しいデータを待つ間にユーザー・インターフェースがフリーズすることはありません。新しいデータがない場合や新しいデータにアクセスできない場合には、新しいデータが入手可能になって完全にダウンロードされるまで、キャッシュされたデータを使い続けることができます。また、あるオブジェクトを GRplistController の委譲オブジェクトとして設定することもできます。そうすることによってデータのダウンロードを細かく制御したり、更新データの到着の通知をしたりすることができ、またリモート・データにアクセスできないことがわかった場合には詳細なエラー・レポートを提供することができます。GRplistController の委譲メソッドは GRplistControllerDelegateProtocol.h の中で定義することができます (リスト 5)。

リスト 5. GRplistControllerDelegateProtocol.h
@class GRplistController;

@protocol GRplistControllerDelegate

@optional

// the list controller will automatically try to update data when the network status
// changes, so it's asking permission.
- (BOOL)listControllerShouldDownloadRemoteData:(GRplistController *)listController;
- (void)listController:(GRplistController *)
listController downloadDidFailWithError:(NSError *)err;

// if the data from the server has changed...
- (void)listControllerDataWillChange:(GRplistController *)listController;
- (void)listControllerDataDidChange:(GRplistController *)listController;

@end

この記事の iPhone デモ・アプリケーション、Sonnet のソース・コードは「ダウンロード」セクションに含まれています。Sonnet は、App Engine サーバー上にある isonnet プロジェクトからシェイクスピアのソネットをすべて取得し、そのソネットを訂正したもの (誤字や不正確な部分など) を適宜アップロードすることができます。通常は訂正をしようとすると、データがアプリケーションに同梱されている場合には再コンパイルとアプリケーションの更新が必要になります。アプリケーション用の一般的なデータは随時更新したいのですが、UI は変更されていないのでアプリケーションの再コンパイルは避けたいものです。Sonnet では、アプリケーション・データの更新は、機能の追加やバグ修正とは切り離されています。しかもユーザーは、インターネットに接続されている状態でアプリケーションを起動すればいつでも最新版のデータを入手することができ、アプリケーションの更新が iPhone App Store に現れるまで待つ (iPhone App Store に現れるまで、多少時間がかかる場合があります) 必要がなくなります。

Sonnet 用のユーザー・インターフェースは非常に単純です。このアプリケーションはまず、UITableView と共に RootViewController をロードします。UITableView は、キャッシュされていて利用可能なすべてのデータの中から各ソネットの最初の 3 行を即座に表示した後、更新されたすべてのデータを表示します。

図 1. iSonnet のテーブル・ビュー
iSonnet のテーブル・ビュー

ユーザーがこのテーブル・ビューの中の 1 つのセルに触れると、RootViewControllerGRSonnetViewController を画面にプッシュします。すると GRSonnetViewController は対応するソネット全体を表示します。

図 2. ソネットの表示
ソネットの表示

Sonnet を最初に実行したときには、このアプリケーションにはユーザーに表示するデータがありません。そのため、App Engine サーバーからデータを取得しようとしても取得できない場合には、エラーを表示することが最善の選択肢です。そうしないとユーザーはブランクのテーブル・ビューが表示された状態から始めなければなりません。(Sonnet の出荷版にはアプリケーション・バンドルの中にあるバージョンのデータが含まれていますが、デモ版には含まれていません。)

図 3. エラーを表示する
エラーを表示する

Sonnet がユーザーにネットワーク通信エラーを表示するのは、この時のみです。他の場合には、このアプリケーションはさまざまに使われて既にデータがダウンロードされ、キャッシュされているからです。サンプル・コードを見ると UI の実装の詳細がわかるはずです。

Sonnet では、RootViewController オブジェクトが GRPlistController の委譲オブジェクトとして動作します。RootViewController の初期化コードをリスト 6 に示します。

リスト 6. RootViewController の初期化コード
(void)viewDidLoad {
   [super viewDidLoad];
   // Add the following line if you want the list to be editable
   NSString *rootURL = [[self class] defaultURL];
   self.sonnetsController = [[[GRplistController alloc] 
       initWithRemoteURL:[NSURL URLWithString:rootURL]] autorelease];
   self.sonnetsController.delegate = self;

   [self.sonnetsController updateDataFromDisk];
   self.sonnets = nil;
   [self updateSonnetsFromModel];

   //hit the web for new information
   [self updateSonnets];
  }

Sonnet が起動すると、RootViewController はまず、キャッシュされた plist データをディスクからロードしようとします。データがキャッシュされている場合には、そのデータが即座にアプリケーションにロードされるため、アプリケーションも即座に使用可能になります。キャッシュされた何らかのデータがロードされると、アプリケーションは新しいデータがないかどうか、App Engine サイトに非同期で問い合わせをします。(GRplistController.m で、「#pragma mark Downloading of data」の行の下にある NSURLConnection の実装の詳細を参照してください。) 新しいデータがある場合には、そのデータはキャッシュされたデータと比較され、もしそのデータが変更されていると、GRPlistControllerlistControllerDataDidChange メソッドを使って RootViewController に通知します。すると RootViewController は、ソネットのテーブルを新しいデータと共にリロードします。

まとめ

App Engine と iPhone 開発を組み合わせると、モバイル・アプリケーションの作成やプロトタイピングのための強力なツールとなります。このサンプル Web アプリケーションでは、Apple の App Store から無料でダウンロードできる単純な iPhone アプリケーション Sonnet を強力にする方法を説明しました。Google App Engine と iPhone 開発とを組み合わせると、アプリケーションのさまざまな部分のプロトタイプを Python で迅速に作成することができます (それ以外の方法では、Objective-C を使って退屈なコーディングをせざるを得ません)。しかもオンラインでもオフラインでもデータを保存できるという高い柔軟性が得られるため、この組み合わせは強力です。また TouchEngine によって、さまざまな分野の最高の部分を組み合わせることができます。ハイブリッド・アプリケーションを作成する場合、TouchEngine を使えばデータをローカルに iPhone 上にキャッシュできる一方、クラウドから非同期にデータを更新することができるため、ソフトウェアの作成が容易になります。こうすることでアプリケーションがユーザー入力に迅速に応答することができ、しかもデータはオンラインに保持されるため更新を随時行うことができます。

iPhone 開発者コミュニティーも Google App Engine 開発者コミュニティーも、迅速に開発が行えるように豊富なリソース・セットを提供しています。いずれかのプラットフォームでの開発に関心がある場合、あるいはこの記事で紹介したように 2 つのプラットフォームで提供されるものを組み合わせることに関心がある場合には、正式なドキュメントをいくつか読んでみることをお薦めします。Google にも Apple にも、素晴らしいドキュメント・ベースのチュートリアルがあり、また非常に優れたビデオ・ベースのチュートリアルも用意されています。Google App Engine は全世界で「Hack-A-Thon」というイベントを開催しています。そのイベントが皆さんの地域でも行われているかどうか、Google のサイトを調べてみてください。また、Apple の WWDC カンファレンスも多くの iPhone SDK プログラマーにとって貴重なリソースとして実証されています。


ダウンロード

内容ファイル名サイズ
Sample iPhone and Google App Engine CodeiPhone_Google_App_Engine_code.zip2919KB

参考文献

学ぶために

製品や技術を入手するために

  • App Engine SDK の最新版をダウンロードしてください。
  • iPhone SDK を使って開発を始めてください。

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development
ArticleID=370316
ArticleTitle=Apple の iPhone を Google のクラウド・コンピューティングに接続する
publish-date=01202009