土台から構築する iPhone のチャット・アプリケーション

巧みな iPhone アプリケーション、そして対応するサーバー・コンポーネントを作成する

この記事では、サーバーからフロントエンドのユーザー・インターフェースに至るまで、iPhone のチャット・アプリケーションを構築する全過程を説明します。

Jack D. Herrington, Senior Software Engineer, Fortify Software, Inc.

Photo of Jack HerringtonJack Herrington はサンフランシスコ湾岸地域に住む技術者、著作者、講演者です。彼の最新の活動や記事を http://jackherrington.com で知ることができます。



2011年 1月 05日

iPhone のチャット・アプリケーションを設計する

よく使われる頭文字語

  • DOM: Document Object Model
  • IDE: Integrated Development Environment
  • SAX: Simple API for XML
  • SQL: Structured Query Language
  • UI: User Interface
  • W3C: World Wide Web Consortium
  • XIB: Xml Interface Builder
  • XML: Extensible Markup Language

4000万台もの iPhone が使われているなか、iOS アプリケーションを作成することに興味がない開発者がいるとしたら、その開発者はどうかしています。では、初めて iOS アプリケーションを作成するとしたら、どこから手をつければよいのでしょう?ほとんどのアプリケーションはネットワークに接続されることになるので、ネットワークの両端にまたがるプロジェクトというのはどうでしょう?例えば、チャット・アプリケーションとか。そこでこの記事では、サーバー・コンポーネントとクライアント・コンポーネントの両方からなるチャット・アプリケーションを構築する方法を説明します。この記事を読めば、iOS アプリケーションの作成方法について多くを学べるので、記事を読み終える頃にはきっと、iOS アプリケーションを自分でも作成してみたくなるはずです。

アプリケーションの構築は、ソリューションを設計するところから始まります。図 1 に、iOS 機器 (この例では iPhone) が 2 つの PHP ページを介してサーバーに接続するアーキテクチャーを示します。

図 1. チャット・アプリケーションのクライアント/サーバー・アーキテクチャー
iPhone クライアントが add.php および messages.php ページを使用してデータベースに接続する様子を示す図


add.php と messages.php という 2 つの PHP ページは、データベースに接続して、それぞれメッセージの送信、メッセージの取得を行います。この記事で提供するコードでは、データベースに MySQL を使用していますが、必要に応じて DB2 やその他のデータベースを使用するのでも構いません。

プロトコルには XML を使用します。add.php ページはメッセージの投稿が成功したかどうかを伝える XML メッセージを返し、messages.php ページはサーバーに投稿された最新のメッセージを返します。

作業に取り掛かる前に、この記事で説明する内容を紹介しておきます。

  • データベース・アクセス: PHP を使用してデータベースに行を追加し、追加した行を取得する方法を説明します。
  • XML エンコーディング: サーバー・コードによってメッセージを XML にパッケージ化する方法を具体的に示します。
  • iOS インターフェースの作成: アプリケーションのユーザー・インターフェースを作成する手順を説明します。
  • サーバーに対するクエリーの実行: Objective-C コードで messages.php ページに GET リクエストを送信し、最新のチャット・メッセージを取得します。
  • XML の構文解析: iOS 開発者のために用意されている XML パーサーを使用して、messages.php から返された XML を構文解析することができます。
  • メッセージの表示: このアプリケーションは、カスタム・リスト項目を使ってチャット・メッセージを表示します。この手法から、iOS アプリケーションのルック・アンド・フィールをカスタマイズする方法を理解できるはずです。
  • メッセージの POST 送信: アプリケーションがサーバーにデータを POST 送信する際に実行される add.php を使ってこのプロセスを説明します。
  • タイマー: タイマー・タスクを使用して messages.php を定期的にポーリングし、新着のチャット項目を調べます。

このように、1 つのサンプル・アプリケーションで盛り沢山の内容を説明するので、読者の皆さんが構築しようとしているクライアント/サーバー iOS アプリケーションがどのようなタイプであれ、そのアプリケーションを開発する上で十分参考になるはずです。


サーバーを構築する

最初の作業は、データベースを作成することです。ここで作成するデータベースには「chat」という名前を付けましたが、皆さんが作成するデータベースにはどんな名前を付けても構いません。別の名前にした場合には、必ず PHP のデータベース接続用ストリングを変更して、データベースの名前に合わせるようにすればよいのです。リスト 1 に、このアプリケーション用の単一のテーブルを作成するために使った SQL スクリプトを記載します。

リスト 1. chat.sql
DROP TABLE IF EXISTS chatitems;
CREATE TABLE chatitems (
    id BIGINT NOT NULL PRIMARY KEY auto_increment,
    added TIMESTAMP NOT NULL,
    user VARCHAR(64) NOT NULL,
    message VARCHAR(255) NOT NULL
);

単一のテーブルからなるこの単純なデータベースには、以下の 4 つのフィールドしかありません。

  • 行の ID (自動的にインクリメントされる整数にすぎません)
  • メッセージが追加された日付
  • メッセージを追加したユーザー
  • メッセージ自体のテキスト

これらのフィールドのサイズは、そこに取り込む内容に応じて変更することができます。

本番システムでは、ユーザーの名前とパスワードを格納するユーザー・テーブルや、ログイン・ユーザー・インターフェースなども必要になってくるはずですが、この例ではデータベースをシンプルにしておきたかったので、データベースには 1 つのテーブルしかありません。

まず始めに作成しなければならないのは、リスト 2 に示す add.php スクリプトです。

リスト 2. add.php
<?php
header( 'Content-type: text/xml' );
mysql_connect( 'localhost:/tmp/mysql.sock', 'root', '' );
mysql_select_db( 'chat' );
mysql_query( "INSERT INTO chatitems VALUES ( null, null, '".
    mysql_real_escape_string( $_REQUEST['user'] ).
    "', '".
    mysql_real_escape_string( $_REQUEST['message'] ).
    "')" );
?>
<success />

このスクリプトはデータベースに接続し、メッセージを投稿したユーザーの user フィールドと message フィールドを使用してメッセージを保管します。この単純な INSERT 文では、SQL 構文に支障をきたす可能性のある不適切な文字 (単一引用符など) に対処するために、2 つの値をエスケープしています。

次に、add.php スクリプトをテストするために test.html ページを作成します (リスト 3 を参照)。このページは、add.php スクリプトにフィールドを POST 送信するだけのページです。

リスト 3. test.html
<html>
<head>
    <title>Chat Message Test Form</title>
</head>
<body>
    <form action="add.php" method="POST">
        User: <input name="user" /><br />
        Message: <input name="message" /><br />
        <input type="submit" />
    </form>
</body>
</html>

この単純なページには 1 つのフォームしかありません。add.php を指すこのフォームには、ユーザー用とメッセージ用の 2 つのテキスト・フィールド、そして POST 送信を実行する送信ボタンがあります。

test.html ページを組み込んだら、add.php スクリプトのテストを開始することができます。ブラウザーでこのテスト・ページを開くと、図 2 のように、「jack」という値が設定された「User (ユーザー)」フィールド、「This is a test」という値が設定された「Message (メッセージ)」フィールド、そして「Submit Query (クエリーの送信)」ボタンが表示されます。

図 2. メッセージの POST 送信をテストするページ
「User (ユーザー)」と「Message (フィールド)」の 2 つのフィールド、そして「Submit Query (クエリーの送信)」ボタンを表示するページのスクリーン・キャプチャー

このテスト・ページで、何らかの値を追加して「Submit Query (クエリーの送信)」ボタンをクリックします。何も問題がなければ、図 3 のような内容が表示されるはずです。

図 3. メッセージの POST 送信完了
ブラウザーに表示された、XML ファイルのスクリーン・キャプチャー。<success/> 要素が表示されています。

何らかの問題が起こった場合には、データベース接続に失敗したこと、または INSERT 文が機能しなかったことを通知する PHP スタック・トレースが表示されることになります。

メッセージ追加スクリプトが機能することを確認したら、今度はメッセージの一覧を返す messages.php スクリプトを作成します。リスト 4 に、このスクリプトを記載します。

リスト 4. messages.php
<?php
header( 'Content-type: text/xml' );
mysql_connect( 'localhost:/tmp/mysql.sock', 'root', '' );
mysql_select_db( 'chat' );
if ( $_REQUEST['past'] ) {
    $result = mysql_query('SELECT * FROM chatitems WHERE id > '.
        mysql_real_escape_string( $_REQUEST['past'] ).
        ' ORDER BY added LIMIT 50');
} else {
    $result = mysql_query('SELECT * FROM chatitems ORDER BY added LIMIT 50' );    
}
?>
<chat>
<?php
while ($row = mysql_fetch_assoc($result)) {
?>
<message added="<?php echo( $row['added'] ) ?>" id="<?php echo( $row['id'] ) ?>">
    <user><?php echo( htmlentities( $row['user'] ) ) ?></user>
    <text><?php echo( htmlentities( $row['message'] ) ) ?></text>
</message>
<?php
}
mysql_free_result($result);
?>
</chat>

このスクリプトは、メッセージの追加スクリプトよりも多少複雑になっています。スクリプトが最初に行うことは、クエリーを組み立てることです。このクエリーには、以下の 2 通りが考えられます。

  • past パラメーターが指定されている場合、スクリプトはこのパラメーターに指定された ID よりも後に送信されたメッセージだけを返します。
  • past パラメーターが指定されていなければ、すべてのメッセージを返します。

past パラメーターを使用している理由は、クライアントを賢くするためです。具体的に言うと、クライアントに表示済みのメッセージを記憶させ、まだ表示していない、それ以降のメッセージだけを要求させるようにします。クライアント・ロジックは簡単で、検出した ID の最大値を保持しておき、それを past パラメーターとして送信すればよいのです。最初のうちは値として 0 を送信することができます。これは、何も指定しないことと同じです。

スクリプトの後半では、クエリーの結果セットからレコードを取得して、これらのレコードを XML にエンコードします。スクリプトのこの部分が機能していれば、任意のブラウザーでこのページにアクセスすると、図 4 のように表示されることになります。

図 4. チャット・メッセージのリスト
XML にコーディングされた短いチャット・メッセージを表示する画面のスクリーン・キャプチャー。この XML には、タイムスタンプ、ID、ユーザー、メッセージ・テキストが含まれています。

サーバーに必要な作業はこれですべてです。もちろん、必要であれば任意のロジックやチャネル、ユーザー検証およびログインなどを追加することもできますが、このサンプル・チャット・アプリケーションには、このスクリプトで十分です。次は、このサーバーを使用する iOS アプリケーションを構築する作業に移ります。


クライアントを構築する

iOS IDE は、XCode と呼ばれています。XCode をまだインストールしていない場合は、Apple Developer サイト (「参考文献」を参照) からダウンロードしてください。同製品の最新バージョンは XCode 3 で、この記事のスクリーン・キャプチャーでも、このバージョンを使用しています。これよりも新しい、ユーザー・インターフェース・エディターを IDE に組み込んだ XCode 4 バージョンもありますが、現時点ではまだプレビュー・モードの状態です。

XCode をインストールしたら、「New Project (新規プロジェクト)」ウィザード (図 5 を参照) を使用してアプリケーションの構築に取り掛かります。

図 5. ビュー・ベース iPhone アプリケーションの構築
テンプレート選択された画面のスクリーン・キャプチャー。「iPhone OS」 > 「Application and Product (アプリケーションと製品)」 > 「iPhone」>「View-based Application (ビュー・ベース・アプリケーション)」の順に選択します。

最も簡単に取り掛かれるアプリケーションのタイプは、ビュー・ベースのアプリケーションです。このタイプのアプリケーションでは、コントロールを任意の場所に配置することができ、UI デザインの大部分は開発者に委ねられます。コントロールを選択した後、iPhone または iPad のいずれかを選択してください。どちらを選択するかは、シミュレーションにどの機器を使用するかによります。コードは、iPhone または iPad、あるいは Apple に次に登場する他の i 機器で機能するように作成することができます。

Choose (選択)」をクリックすると、アプリケーションの名前を入力するように求められます。私のアプリケーションには「iOSChatClient」という名前を付けましたが、皆さんのお好きな名前にして構いません。名前を入力すると、XCode IDE がコア・アプリケーション・ファイルを作成するので、これらのファイルをいったんコンパイルして、アプリケーションを起動して問題がないことを確認してください。

ユーザー・インターフェースを作成する

アプリケーションを作成したら、今度はインターフェースを開発することができます。その出発点となるのは、Resources フォルダー内にあるビュー・コントローラー XIB ファイルです。このフォルダーをダブルクリックすると、Interface Builder という名前の UI ツールキットが立ち上がります。

図 6 . インターフェースのレイアウト
メッセージ・テキスト・ボックス、「Send (送信)」ボタン、チャット項目のリストからなる UI のスクリーン・キャプチャー

このサンプル・アプリケーションでは、3 つのコントロールを図 6 のように配置しました。最上部にあるのは、送信するメッセージを入力するテキスト・ボックスです。このテキスト・ボックスの右側に、「Send (送信)」ボタンがあります。その下にあるのは、すべてのチャット項目を表示する UITableView オブジェクトです。

Interface Builder でこのようにすべてのコントロールを配置する方法を詳しく説明することもできますが、プロジェクト・コードをダウンロードして、実際に試してみることをお勧めします。このプロジェクトは、皆さん独自のアプリケーションのテンプレートとして遠慮なく使ってください。

ビュー・コントローラーのヘッダーを作成する

ユーザー・インターフェースに必要な作業はまさにこれだけです。次は、XCode IDE に戻り、リスト 5 に記載するようにビュー・コントローラーのクラス定義にメンバー変数、プロパティー、メソッドを追加します。

リスト 5. iOSChatClientViewController.h
#import <UIKit/UIKit.h>

@interface iOSChatClientViewController : UIViewController 
                             <UITableViewDataSource,UITableViewDelegate>    {
    IBOutlet UITextField *messageText;
    IBOutlet UIButton *sendButton;
    IBOutlet UITableView *messageList;

    NSMutableData *receivedData;
    NSMutableArray *messages;
    int lastId;

    NSTimer *timer;

    NSXMLParser *chatParser;
    NSString *msgAdded;
    NSMutableString *msgUser;
    NSMutableString *msgText;
    int msgId;
    Boolean inText;
    Boolean inUser;
}

@property (nonatomic,retain) UITextField *messageText;
@property (nonatomic,retain) UIButton *sendButton;
@property (nonatomic,retain) UITableView *messageList;

- (IBAction)sendClicked:(id)sender;

@end

先頭から説明すると、クラス定義には UITableViewDataSource と UITableViewDelegate を追加しました。このコードは、メッセージの表示を操作するためのコードです。ビュー・コントローラーのクラスに追加したこの 2 つのメソッドが呼び出されると、データおよびレイアウト情報の両方がテーブル・ビューにフィードされます。

インスタンス変数は、4 つのグループに分かれています。一番上にあるのは各種の UI 要素 (送信するメッセージのテキスト・フィールド、送信ボタン、メッセージ・リスト) へのオブジェクト参照です。

その下にあるインスタンス変数は、返された XML、メッセージのリスト、そして lastID を保持するバッファーとなります。lastID は、メッセージに付けられている ID の最大値に設定され、最初はゼロですが、その後 past パラメーターの値としてサーバーに送り返されます。

タイマーはサーバーからの新着メッセージを調べるために数秒ごとに起動されます。最後のグループは、XML を構文解析するために必要なすべてのメンバー変数です。メンバー変数の数が多い理由は、XML パーサーがコールバック・ベースのパーサーであることに起因します。つまり、XML パーサーはこのクラスの中に状態を大量に保持するということです。

メンバー変数の下にあるのは、プロパティーとクリック・ハンドラーです。これらのプロパティーとクリック・ハンドラーは、インターフェース要素とこのコントローラー・クラスを関連付けるために Interface Builder で使われます。プロパティーとクリック・ハンドラーのすべてがビュー・コントローラーの中に収まったので、実際に Interface Builder に戻り、コネクター・コントロールを使用してメッセージ・テキスト、送信ボタン、そしてメッセージ自体をそれぞれに対応するプロパティーに関連付け、Touch Inside イベントを sendClicked メソッドに関連付けてみるとよいでしょう。

ビュー・コントローラーのメソッドを実装する

ビュー・コントローラーのヘッダーは片付いたので、次はいよいよプロジェクトの本質に取り掛かり、ビュー・コントローラーを実装します。ビュー・コントローラーを実装するファイルは 1 つだけですが、それぞれのセクションを説明しやすいように、いくつかのリストに分けて記載します。

最初のセクション (リスト 6) は、アプリケーションを起動してビュー・コントローラーを初期化する部分です。

リスト 6. iOSChatClientViewController.m – 起動
#import "iOSChatClientViewController.h"

@implementation iOSChatClientViewController

@synthesize messageText, sendButton, messageList;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
        lastId = 0;
        chatParser = NULL;
    }
    return self;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:
                                       (UIInterfaceOrientation)interfaceOrientation {
    return YES;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

- (void)viewDidUnload {
}

- (void)dealloc {
    [super dealloc];
}

これはかなり標準的な iOS コードです。ここには、メモリー警告や割当解除などの各種システム・イベントに対するコールバックがあります。本番アプリケーションでは、こうしたイベントのすべてをグレースフルに処理する必要がありますが、このサンプル・サプリケーションでは複雑になり過ぎないように、これだけのコードにしておきました。

最初の本格的なタスクが登場するのは、messages.php スクリプトに対して GET リクエストを実行するところです。リスト 7 に、この部分のコードを記載します。

リスト 7. iOSChatClientViewController.m – メッセージの取得
- (void)getNewMessages {
    NSString *url = [NSString stringWithFormat:
        @"http://localhost/chat/messages.php?past=%ld&t=%ld",
        lastId, time(0) ];

    NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
    [request setURL:[NSURL URLWithString:url]];
    [request setHTTPMethod:@"GET"];

    NSURLConnection *conn=[[NSURLConnection alloc] initWithRequest:request delegate:self];
    if (conn)
    {
        receivedData = [[NSMutableData data] retain];
    }
    else
    {
    }
}

- (void)connection:(NSURLConnection *)connection
  didReceiveResponse:(NSURLResponse *)response
{  
    [receivedData setLength:0];
}  

- (void)connection:(NSURLConnection *)connection 
  didReceiveData:(NSData *)data  
{  
    [receivedData appendData:data];
}  

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{  
    if (chatParser)
        [chatParser release];

    if ( messages == nil )
        messages = [[NSMutableArray alloc] init];

    chatParser = [[NSXMLParser alloc] initWithData:receivedData];
    [chatParser setDelegate:self];
    [chatParser parse];

    [receivedData release];

    [messageList reloadData];

    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
    [self methodSignatureForSelector: @selector(timerCallback)]];
    [invocation setTarget:self];
    [invocation setSelector:@selector(timerCallback)];
    timer = [NSTimer scheduledTimerWithTimeInterval:5.0 
        invocation:invocation repeats:NO];
}

- (void)timerCallback {
    [timer release];
    [self getNewMessages];
}

上記のコードは、getNewMessages メソッドから始まります。このメソッドはリクエストを作成し、NSURLConnection を確立してリクエストの実行を開始します。さらに、レスポンス・データを保持するデータ・バッファーも作成します。3 つのイベント・ハンドラー (didReceieveResponsedidReceiveDataconnectionDidFinishLoading) が、データ・ロードのさまざまなフェーズを処理します。

上記のメソッドのうち、最も重要なのは connectionDidFinishLoading メソッドです。このメソッドが、データ全体を読み取ってメッセージを抽出する XML パーサーを起動します。

最後にある timerCallback メソッドは、タイマーが新規メッセージのリクエストを開始するために使用します。タイマーが停止すると、getNewMessages メソッドが呼び出され、このメソッドによってプロセスが再び開始され、新規タイマーが作成されます。その新しいタイマーがタイムアウトすると、メッセージの取得プロセスが再び始まるといった具合に繰り返されます。

次に続くのは、XML の構文解析を処理するセクションです (リスト 8)。

リスト 8. iOSChatClientViewController.m – メッセージの構文解析
- (void)parser:(NSXMLParser *)parser 
didStartElement:(NSString *)elementName 
namespaceURI:(NSString *)namespaceURI 
qualifiedName:(NSString *)qName 
attributes:(NSDictionary *)attributeDict {
    if ( [elementName isEqualToString:@"message"] ) {
        msgAdded = [[attributeDict objectForKey:@"added"] retain];
        msgId = [[attributeDict objectForKey:@"id"] intValue];
        msgUser = [[NSMutableString alloc] init];
        msgText = [[NSMutableString alloc] init];
        inUser = NO;
        inText = NO;
    }
    if ( [elementName isEqualToString:@"user"] ) {
        inUser = YES;
    }
    if ( [elementName isEqualToString:@"text"] ) {
        inText = YES;
    }
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
    if ( inUser ) {
        [msgUser appendString:string];
    }
    if ( inText ) {
        [msgText appendString:string];
    }
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName 
   namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
    if ( [elementName isEqualToString:@"message"] ) {
        [messages addObject:[NSDictionary dictionaryWithObjectsAndKeys:msgAdded,
            @"added",msgUser,@"user",msgText,@"text",nil]];

        lastId = msgId;

        [msgAdded release];
        [msgUser release];
        [msgText release];
    }
    if ( [elementName isEqualToString:@"user"] ) {
        inUser = NO;
    }
    if ( [elementName isEqualToString:@"text"] ) {
        inText = NO;
    }
}

上記の XML パーサーは、SAX 構文解析を知っている誰にとってもお馴染みのはずです。この XML パーサーに XML を渡すと、開始タグ、終了タグ、テキストなどが検出されるたびに通知が行われます。つまり、DOM ベースのパーサーではなく、イベント・ベースのパーサーであるということです。イベント・ベースのパーサーには、メモリー・フットプリントが少ないという利点があります。その一方、構文解析中にその状態をすべてホストに保管しなければならないため、少し使いにくいという欠点もあります。

この構文解析のプロセスは、msgAddedmsgUserinUserinText などのメンバー変数のすべてを空ストリングまたは false に初期化するところから始まります。次に、didStartElement メソッドで開始タグを検出するたびに、コードはタグ名を調べ、適切な inUser または inText ブール値を設定します。ここからは、foundCharacters メソッドがテキスト・データを適切なストリングに追加するという作業に対処します。その後、<message> タグの終わりが検出されると、didEndElement メソッドは構文解析されたメッセージをメッセージ・リストに追加することにより、タグの終了を処理します。

次に必要なのは、メッセージを表示するためのコードです。このコードをリスト 9 に記載します。

リスト 9. iOSChatClientViewController.m – メッセージの表示
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)myTableView numberOfRowsInSection:
                                       (NSInteger)section {
    return ( messages == nil ) ? 0 : [messages count];
}


- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:
                                       (NSIndexPath *)indexPath {
    return 75;
}

- (UITableViewCell *)tableView:(UITableView *)myTableView 
   cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = (UITableViewCell *)[self.messageList 
        dequeueReusableCellWithIdentifier:@"ChatListItem"];
    if (cell == nil) {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"ChatListItem" 
            owner:self options:nil];
        cell = (UITableViewCell *)[nib objectAtIndex:0];
    }

    NSDictionary *itemAtIndex = (NSDictionary *)[messages objectAtIndex:indexPath.row];
    UILabel *textLabel = (UILabel *)[cell viewWithTag:1];
    textLabel.text = [itemAtIndex objectForKey:@"text"];
    UILabel *userLabel = (UILabel *)[cell viewWithTag:2];
    userLabel.text = [itemAtIndex objectForKey:@"user"];

    return cell;
}

これらのメソッドは、すべて UITableViewDataSource および UITableViewDelegate インターフェースによって定義されています。このうち最も重要なメソッドは cellForRowAtIndexPath です。このメソッドは、リスト項目のカスタム UI を作成し、そのテキスト・フィールドを当該メッセージに適したテキストに設定します。

このカスタム・リスト項目を定義する場所は ChatListItem.xib ファイルなので、このファイルを Resources フォルダーに新規に作成する必要があります。ファイル内には、それぞれ 1 と 2 というタグを付けた 2 つのラベルを持つ新規項目 UITableViewCell を作成します。プロジェクトをダウンロードすると (「ダウンロード」を参照)、このファイルも他のすべてのコードと一緒に入手することができます。

cellForRowAtIndexPath メソッド内のコードは、これらの ChatListItem セルのうちの 1 つを割り当て、ラベルのテキスト・フィールドをそのメッセージで検出したテキストおよびユーザー値に設定します。

理解する内容が山ほどあることは承知ですが、ゴールは目前です。ビューを起動し、メッセージ XML を取得して構文解析し、そのメッセージを表示するためのコードはすでに出来上がっています。残る作業は、メッセージを送信するコードを作成することだけです。

メッセージを送信するコードを作成するには、最初にそのユーザー名を対象とした設定を作成します。iOS アプリケーションでは、「Settings (設定)」コントロール・パネルに含めるカスタム設定を定義することができます。設定を作成するには、「New File (新規ファイル)」ウィザードを使用する必要があります。このウィザードで Resources フォルダー内に設定バンドルを作成した後、設定エディター (図 7 を参照) を使って、1 つの設定だけを残して他を削除します。

リスト 7. 設定のセットアップ
設定エディターのスクリーン・キャプチャー

次に、この設定には User というタイトルを付け、user_preference キーを設定する必要があります。これで、この設定を使用して、メッセージ送信コードのためにユーザー名を取得することができます (リスト 10 を参照)。

リスト 10. iOSChatClientViewController.m – メッセージの送信
- (IBAction)sendClicked:(id)sender {
    [messageText resignFirstResponder];
    if ( [messageText.text length] > 0 ) {
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

        NSString *url = [NSString stringWithFormat:
            @"http://localhost/chat/add.php"];

        NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] 
            init] autorelease];
        [request setURL:[NSURL URLWithString:url]];
        [request setHTTPMethod:@"POST"];

        NSMutableData *body = [NSMutableData data];
        [body appendData:[[NSString stringWithFormat:@"user=%@&message=%@", 
             [defaults stringForKey:@"user_preference"], 
             messageText.text] dataUsingEncoding:NSUTF8StringEncoding]];
        [request setHTTPBody:body];

        NSHTTPURLResponse *response = nil;
        NSError *error = [[[NSError alloc] init] autorelease];
        [NSURLConnection sendSynchronousRequest:request 
            returningResponse:&response error:&error];

        [self getNewMessages];
    }

    messageText.text = @"";
}

- (void)viewDidLoad {
        [super viewDidLoad];

    messageList.dataSource = self;
    messageList.delegate = self;

    [self getNewMessages];
}

@end

上記は、「Send Message (メッセージ送信)」ボタンのクリック・ハンドラーのコードです。このコードはまず、add.php スクリプトの URL を組み込んだ NSMutableURLRequest を作成し、続いてメッセージの本体を、POST 形式でエンコードしたユーザーおよびメッセージ・データが含まれるストリングに設定します。次に NSURLConnection を使ってメッセージ・データをサーバーに同期送信し、getNewMessages を使ってメッセージの取得を開始します。

このファイルの最後にある viewDidLoad メソッドは、ビューのロード時に呼び出されます。すると、このメソッドはメッセージ取得プロセスを開始し、メッセージ・リストとこのオブジェクトを関連付けることで、メッセージ・リストにデータが取り込まれるようにします。

これでコード全体が完成したので、次はアプリケーションをテストします。それにはまず、「Settings (設定)」ページ (図 8) でユーザー名を設定します。

図 8. 「Settings (設定)」ページ
「General (一般)」、「Safari」、「Photos (写真)」、および「iOSChatClient」という選択肢が表示された「Settings (設定)」ページのスクリーン・キャプチャー

このページで iOSChatClient アプリケーションをクリックすると、図 9 の設定ページが表示されます。

図 9. ユーザー名の設定
「User (ユーザー)」フィールドに「Megan」という値が設定された「iOSChatClient」設定ページのスクリーン・キャプチャー

続いて、通常の iPhone の操作でアプリケーションに戻り、キーボードを使ってメッセージを入力します (図 10 を参照)。

図 10. 新規メッセージの入力
QWERTY キーボード、メッセージ・テキスト・ボックス、「Send (送信)」ボタン、そしてユーザー jack からのテスト・メッセージが表示された画面のスクリーン・キャプチャー

「Send (送信)」ボタンを押すと、メッセージがサーバーに送信されて追加された後に、messages.php からそのメッセージが返されてきます (図 11 を参照)。

図 11. 完成したチャット・アプリケーション
メッセージ・テキスト・ボックス、「Send (送信)」ボタン、そしてユーザー jack と Megan からの 2 つのテスト・メッセージが表示された画面のスクリーン・キャプチャー

コードからわかるように、送信ボタンとメッセージ・リストは、直接関連付けられてはいません。したがって、メッセージをリストに追加する唯一の方法は、サーバーを介してデータを正常にデータベースに挿入することです。すると、message.php コードによってメッセージ・リストに表示するメッセージが正常に返されます。


まとめ

確かに盛り沢山の内容を説明しましたが、この記事から多くのことを学んだはずです。バックエンドでは XML を使ってデータベースの作業を行いました。そして、サーバーへのデータの送信と、サーバーからのデータの取得をするためのカスタム・ユーザー・インターフェースを備えた iOS アプリケーションを構築しました。さらに XML パーサーを使ってサーバーからのレスポンス XML を構文解析し、メッセージを見栄え良く表示するカスタム・リスト UI も作成しました。

Apple では、iPhone や iPad で皆さんが目指しているあらゆる構想を実現するためのツールを提供しており、次にどの道に進むかは、読者の皆さん次第です。この記事では、独自のネットワーク対応アプリケーションを構築する際のロードマップを説明しました。ぜひ、皆さん独自のアプリケーションの構築にチャレンジしてください。そして素晴らしいアプリケーションが完成したら、ぜひ私にご一報ください。必ず App Store で確かめてみます。


ダウンロード

内容ファイル名サイズ
Source code for articleChatProject.zip29KB

参考文献

学ぶために

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

  • Apple 開発者向けポータル: IDE を入手して、テスト用機器をプロビジョニングし、完成したアプリケーションを App Store にアップロードしてください。
  • IBM 製品の評価版: DB2®、Lotus®、Rational®、Tivoli®、および WebSphere® のアプリケーション開発ツールとミドルウェア製品を体験するには、評価版をダウンロードするか、IBM SOA Sandbox のオンライン試用版を試してみてください。

議論するために

コメント

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=XML, Open source
ArticleID=620316
ArticleTitle=土台から構築する iPhone のチャット・アプリケーション
publish-date=01052011