Swiftで遊ぼう! on Hatena

あしたさぬきblogでやってた初心者オヤジのiOSプログラミング奮闘記がHatenaに来ました

Swiftで遊ぼう! - 482 - URLSessionの基礎

2020年3月29日:Combineフレームワークの勉強のためDataTaskPublisherを使ってSwiftUIのデモに書き換えました。興味がある人は、Swiftで遊ぼう! - 1015 - URLSessionDataTaskPublisherの基礎 - Swiftで遊ぼう! on Hatena をチェックしてください
2017年4月7日:コードを修正*1

以前NSURLSessionの使い方を勉強していましたがいまはURLSessionに変更されています。ここのチュートリアルを勉強した後に次のページを読んでください:Swiftで遊ぼう! - 488 - URLSessionを使ってテキストデータをダウンロード - Swiftで遊ぼう! on Hatena

以前取り組んでいたNSURLSessionの勉強は次のページが発端です。
Swiftで遊ぼう! - 454 - Swift 2 NSURLSessionの理解から混沌にハマる...(URLSessionへ飛躍) - Swiftで遊ぼう! on Hatena

ここで取りあげている一般的なNSURLSession利用のコードは、WWDC2015のセッションをみて取りだしたものです。

https://developer.apple.com/videos/play/wwdc2015-711/

// WWDC2015で紹介されていた一般的なNSURLSession利用のコード
let sessionConfig = 
       NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: sessionConfig)
let url = NSURL(string: "https://www.example.com")!
let task = session.dataTaskWithURL(url) {
  (data: NSData?, response: NSURLResponse?, error: NSError?) 
       in
         ...
 }
task?.resume()

// Swift 3で使用するURLSession利用の一般的コード
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let url = URL(string: "https://www.example.com")
let task = 
    session.dataTask(with: url!) {(data, response, error) 
       in
        ...
}
task.resume()

今日のテーマはURLSessionの使い方です。

新しいプロジェクトを作ります。「Simple View Application」を選んで、適当な名前を付けます。私は「URLSessionTest」にしました。Languageは「Swift」で、Deviceは「Universal」にします。

MainStroyboadを開いて、UIImageViewとUIButtonの2つのオブジェクトを画面に設置します。

f:id:yataiblue:20170407141502j:plain

当然Autolayoutでオブジェクトの位置決めをしてください。ここでは「Stack」を利用する必要はないので、基本的な知識で取り組んでください。

Swiftで遊ぼう! - 204 - フィリングを使ってレイアウト調整(Auto Layoutのまとめ) - Swiftで遊ぼう! on Hatena

基本的にオブジェクトを画面のセンターに位置決めして上部のマージンを設定すればいいんです。Image Viewは大きさを一定にします。heightを「240」でWidthを「240」の正方形を確保します。

次にボタンは「ダウンロード」に改名します。そして空のImage Viewにデフォルトのイメージをロードさせます。

アプリのテスト環境は「iPhone 7」にします。すると、プロジェクトで使用するイメージのサイズは2種類でいいんです*2。先ず、プロジェクト・ナビゲータでAssets Xcassetsを選択して、下部にある「+」ボタンを選び、「New Folder」を選択します。フォルダーの名前を「Placeholder」に変更してから「+」ボタンを再び選択して「New Image Set」を選ぶと「1X」「2X」「3X」の空のスロットができます。

次に何でもいいので「png」イメージを用意します。オブジェクトのサイズを「240×240ピクセル」にしているので、1Xはそのまま、2Xは「480×480ピクセル」、そして3Xは「720×720ピクセル」の画像を用意するのですが、ここでは2Xスロットだけセットします。

f:id:yataiblue:20170407142401j:plain

このイメージをImage Viewに設定するのは非常に簡単です。Image Viewを選択した状態でアトリビュート・インスペクタを開きます。Image Viewの「Image」を選ぶと、Assets Xcassetsに設定したイメージがメニューに出てきます。私の場合は「backgroundimage」です。それを設定するとMain.storyboardのイメージが変化します。

f:id:yataiblue:20170407142457j:plain

もう一つだけ設定しておくべき項目があります。そのままサイズ・インスペクタを開きます。項目の下のほうに「Intrinsic Size」があるので、「Placeholder」を選び、WidthとHeightをそれぞれ「240」を選びます。

Main.storyboardにオブジェクトを並べたら「Ctrl + ドラッグ」でViewController.Swiftの所定の場所に@IBOutletと@IBActionを作ります。

お決まりの「// MARK: -」を付けるのを忘れないようにします。

f:id:yataiblue:20170407142551j:plain

Main.stroyboardのオブジェクトと関連付けられていれば、プロパティやアクション前に「●」が付いています。これが付いていればランしたときにインスタンス化されます。

次は、ネットにある画像をダウンロードする関数を実装します。「getImage()」という関数を作って、ありきたりなURLSessionクラスをコードします。

func getImage(){
    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)
    let url = URL(string: "https://s3.amazonaws.com/
              CoverProject/album/
               album_david_bowie_lets_dance.png")
    let task = session.dataTask(with: url!) 
        { (data, response, error) in
       
      // ここで具体的な作業をさせます。
            
    }
    task.resume()
}

これがお決まりのコーディングです。URLSessionクラスのメソッド「dataTask()」はURLクラス情報と関数(クロージャー)を引数として受け取って、クロージャーの仕事(task)をさせるという、ちょっとプログラミング素人には理解し難いコーディングになっています。このクロージャーは「(Data?, URLResponse?, Error?) -> Void」のことで、通信中にエラーは当然生じるものとして扱っています。

実際のコードを考えてみます。当然パラメータ名は自由にできるのですが、混乱しないように(data, response, error)とするのが一般的です。

まずdataはオプショナル型Dataなので存在するかどうかを最初に判断させます。

guard let getData = data else {
// dataが存在しなければキャンセルするステップです。
    session.invalidateAndCancel()
    return
}
// dataが存在すればgetDataとして処理

そして、dataが存在するのであれば、データのダウンロードは時間がかかるのでバックグラウンド・キューでさせるのですが、キューの意味が分からなければSwiftで遊ぼう! - 302 - マルチスレッド(まとめ) - Swiftで遊ぼう! on Hatenaで勉強しましょう。

DispatchQueue.global(qos: .userInitiated).async {
    let image = UIImage(data: getData)
    DispatchQueue.main.async { [weak self] in
        self?.imageView.image = image
    }
}

これを整理すれば次のメソッドになります。

func getImage(){
    let config = URLSessionConfiguration.default
    let session = URLSession(configuration: config)
    let url = URL(string: "https://s3.amazonaws.com/
                   CoverProject/album/
                    album_david_bowie_lets_dance.png")
    let task = session.dataTask(with: url!) 
        { (data, response, error) in
        guard let getData = data else {
            session.invalidateAndCancel()
            return
        }
        DispatchQueue.global(qos: .userInitiated).async {
            let image = UIImage(data: getData)
            DispatchQueue.main.async { [weak self] in
                self?.imageView.image = image
            }
        }
            
    }
    task.resume()
}

そして、この関数を@IBActionにコードします。

@IBAction func downloadImage(sender: UIButton) {
 getImage()
}

これでコーディングは終了!

ランしてみよう!

f:id:yataiblue:20170407172641j:plain

ダウンロードができました*3

次にURLSessionのまとめをしているので次のページに進んでください。
yataiblue.hatenablog.com

*1:2017年2月26日:Swift 3向けのコードに変更

*2:理由がわからなければSwiftで遊ぼう! - 249 - iOSの座標システム: 2020年3月 - Swiftで遊ぼう! on Hatenaで説明しているScaling値を確かめましょう。

*3:以前はサンプルイメージが保存されているサイトがインセキュアだったためダウンドーロできませんでした。その場合はATSを使って例外サイトの登録をする必要があります→Swiftで遊ぼう! - 455 - Swift 2 NSURLSessionまだ全然わかっていない...ちょっと分かったかな(ATS) - Swiftで遊ぼう! on Hatena