Cocoaはやっぱり!
インターネットにアクセスしよう
Web Kit #6 : 経過表示

今回のテーマ

今回のテーマは、「ダウンロードの経過表示」です。技術的には、WebResourceLoadDelegateというプロトコルの解説になりますので、このプロトコルにまつわる内容も扱います。

推奨環境 この解説は、以下の環境を前提に作成し、動作確認等を行っています。ご確認ください。

改版履歴

サンプルの概要

前回作成したWebブラウザでは、一応経過表示は行っていましたが、フレームの読み込み開始と完了というタイミングでしたので、非常に荒い経過表示となっていました。HTMLや画像単位で更新することで細かい経過表示を行うように改良してみます。画面のデザインやインスタンスの構成は前回と同じです。

デリゲートを設定

今回使用するWebResourceLoadDelegateは、前回扱ったWebFrameLoadDelegateと似通っています。呼び出されるタイミングがフレーム単位だったものがリソース単位になります。このリソースというのは、HTMLや画像、CSS、JavaScriptなどWebコンテンツを構成するものをいいます。ですので、「読み込みを開始しました」とか「読み込みが完了しました」というような通知は、より細かく行われることが分かると思います。

MyWebViewDelegateのインスタンスWebResourceLoadDelegateに設定するには、WebViewの setResourceLoadDelegate : メソッドを使用します。これを前回のアプリケーション起動時のところに組み込みます。

デリゲートの設定: MyWebViewDelegate.m
- (void) applicationDidFinishLaunching : (NSNotification *) aNotification { // デリゲートの設定 [ vWeb setFrameLoadDelegate : self ]; [ vWeb setResourceLoadDelegate : self ]; // (1) リソースデリゲートの設定 // ユーザインターフェイスの更新 [ self updateUserInterface ]; }

(1) setResourceLoadDelegate : メソッドで、自分自身をデリゲートに設定しています。

WebResourceLoadDelegateへの通知のシーケンス

今回もWebResourceLoadDelegateがどのように呼ばれてくるのか、全体の流れをシーケンス図を使って説明しておきます。

何らかのアクションでリソースの読み込み処理を開始されます。まず最初に webView: identifierForInitialRequest: fromDataSource: というメソッドが呼ばれますが、ここで デリゲートが行う処理は、「ダウンロードする各リソースについて識別子をつける」というものです。

この後のデリゲートの呼び出しの時に、ここでつけた識別子がパラメータとして渡ってくるので、特定のリソースを追跡する場合に利用します。例えば、リソースの読み込みにかかった時間を計測したいとします。「読み込み開始」と「読み込み完了」の通知で時刻を見ることになりますので、識別子をキーとする辞書 ( NSDictionary ) を作っておきます。読み込み開始時にそのときの時刻を格納し、読み込み完了時に辞書から時刻を取り出して差を計算すればよいことになります。

「リソースの識別はURLでいいんじゃないの?」と思われるかもしれませんが、Webサーバのリダイレクト ( Redirect )によってURLは変化することがあります。「download.cgiというリンクをクリックしてファイルをダウンロードしたら別のファイル名になっていた」というような経験をしたことはないでしょうか。これは、download.cgiから別のURLへ転送されたためです。このように同じリソースでもURLは途中で変化することがあります。そのため識別子を用いて追跡する必要があるわけです。

識別子の形式は特に指定されていませんので、自分が扱いやすいものにしておけばよいです。また、特に追跡の必要性がない場合は、このメソッドを書く必要はありません。今回は経過表示を行うだけですので、このメソッドは書きません。

識別子を付けたら、サーバにリクエストを送信する直前にリクエスト内容を知らせてくれます。webView: resource: willSendRequest: redirectResponse: fromDataSource: メソッドが呼ばれます。サーバに送信するリクエストの内容を変更したい場合にはこのメソッドを書いておきます。特殊なブラウザで、IDとパスワードをHTTPヘッダーに埋め込むなんていうこともできるわけですね。また、このメソッドは先程書いたリダイレクトが発生して再度リクエストを送信する時にも呼ばれます。

その後、サーバにリクエストを送信してレスポンスが返ってきたところで、「レスポンスを受信しました」という通知が来ます。MIMEタイプをチェックしたり、コンテンツの容量をチェックしたりといった処理が行えます。

そして、データが流れてくる度に「データ受信しました」が繰り返し呼ばれます。最後に、「データ送信完了しました」の通知が来ます。

細かな経過表示

では、経過表示の処理を見ていきましょう。

リソースの読み込み完了 : MyWebViewDelegate.m
- (void) webView : (WebView *) sender resource : (id ) identifier didFinishLoadingFromDataSource : (WebDataSource *) dataSource { [ self updateUserInterface ]; // (1) 経過表示など }

(1) リソースの読み込みが完了したときに呼ばれるメソッド webView: resource: didFinishLoadingFromDataSource: を実装します。各リソースの読み込みが完了した時に経過表示を更新するためにupdateUserInterfaceメソッドを呼ぶだけです。これで、きめの細かい経過表示になります。

この説明ではリソースの識別子を付けていませんが、第2パラメータのidentifierにはNSObjectのインスタンスが渡ってきます。詳細は不明ですが、WebView側で管理している何らかのインスタンスなのかもしれません。