酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

Xamarin.iOSでインターネット上のコンテンツをダウンロードして表示させる

本記事では、Xamarin.iOSを使って、ネットワークを利用してインターネット上のコンテンツをダウンロードし、UILabelに表示させてみましょう。インターネット上のコンテンツと一口で言っても画像を含めバイナリファイルや、テキストファイルなど様々なタイプのものが存在しています。ここでは、私のブログのhtmlをダウンロードすることにします。下図はhtmlをダウンロードして表示させました。

f:id:ch3cooh393:20130322185405p:plain

HttpWebRequestクラスを使って文字列を同期ダウンロードする

.NET Frameworkを使っての開発で、もっとも定番なHTTP通信をおこなうHttpWebRequestクラスを使って文字列を同期ダウンロードしてみましょう。

void DownloadUsingHttpWebRequest (object sender, EventArgs e)
{ 
    var text = default(string);

    var req = (HttpWebRequest)HttpWebRequest.Create("http://ch3cooh.hatenablog.jp/");
    using (var res = req.GetResponse())
    using (var strm = res.GetResponseStream())
    using (var reader = new StreamReader(strm))
    {
        text = reader.ReadToEnd();
    }

    label.Text = text;
}

HttpWebRequestクラスには、CreateHttpメソッドが実装されておらず、GetResponseAsyncメソッドに至っては存在しません。oh...ってことで、非同期ダウンロードさせる場合にはWebClientクラス、またはNSUrlConnectionクラスを使用します。

WebClientクラスを使って文字列を非同期ダウンロードする

WebClientオブジェクトと生成して、DownloadStringAsyncメソッドを使って非同期ダウンロードを開始します。ダウンロードが完了すると、DownloadStringCompletedイベントが発生しますので、ダウンロードしたHTML(文字列)をUILabelで表示させています。しかし、Xamarin.iOSでもRxが使いたいなぁ…

void DownloadUisnHttpClient (object sender, EventArgs e)
{
    var client = new WebClient();
    client.DownloadStringCompleted += (sx, ex) => {
        this.BeginInvokeOnMainThread(() => {
            label.Text = ex.Result;
        });
    };
    client.DownloadStringAsync(new Uri("http://ch3cooh.hatenablog.jp/"));
}

そうそう、現行バージョンのXamarin.iOSでは、awaitを使うことができません*1

NSUrlConnectionクラスを使った文字列を非同期ダウンロードする

iOSアプリらしくNSUrlConnectionクラスを使って書きたい場合は、NSUrlConnectionDelegateクラスを継承したDelegateクラスを作成し、そのクラス内でデータを受信して、通信が完了したらViewControllerに通知を行います。……が、あんまりオススメしません

// NSUrlConnectionを使ったダウンロード中に発生するイベントを受け止めるCustomDelegateクラス
public class CustomDelegate : NSUrlConnectionDelegate
{
    public byte[] ResponseData { get; set; }
    private long _receivedDataLength { get; set; }
    private WebRequestSampleViewController _delegate;

    public CustomDelegate(WebRequestSampleViewController viewController)
    {
        _delegate = viewController;
    }

    public override void ReceivedResponse (NSUrlConnection connection, 
                                           NSUrlResponse response)
    {
        long length = response.ExpectedContentLength;
        if (length == -1) {
            length = 1* 1024 * 1024;
        }
        ResponseData = new byte[length];
        _receivedDataLength = 0;
    }

    public override void ReceivedData (NSUrlConnection connection, NSData data)
    {
        System.Runtime.InteropServices.Marshal.Copy(
            data.Bytes, ResponseData, (int)_receivedDataLength, (int)data.Length);
        _receivedDataLength += data.Length;
    }

    public override void FinishedLoading (NSUrlConnection connection)
    {
        _delegate.DidDownloaded(this, new EventArgs());
    }

    public string GetResultText()
    {
        return System.Text.UTF8Encoding.UTF8.GetString(
            ResponseData, 0, (int)_receivedDataLength);
    }
}

通信開始とダウンロード完了後のコードはこんな感じ。

public void DidDownloaded (object sender, EventArgs e)
{
    var customDelegate = sender as CustomDelegate;
    label.Text = customDelegate.GetResultText();
}

void DownloadUsingNSUrlRequest (object sender, EventArgs e)
{
     var downloadedDelegate = new CustomDelegate(this);

     var req = new NSUrlRequest(new NSUrl("http://ch3cooh.hatenablog.jp/"));
     var connection = new NSUrlConnection(req, downloadedDelegate);
     connection.Start();
}

サンプルプロジェクト

サンプルプロジェクトをGitHubで公開しています。

006_WebRequestSample

*1:async/awaitに対応したXamarin.iOSのα版が出ています。 http://blog.xamarin.com/brave-new-async-mobile-world/