【Swift/iOS】Nuke:非同期画像ローディングライブラリの使い方!

【Swift/iOS】Nuke:非同期画像ローディングライブラリの使い方!

この記事からわかること

  • Nukeとは?
  • Xcode/iOS画像ローディングライブラリを使用する
  • LazyImage使い方

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

環境

非同期画像ローディングライブラリ

iOSアプリ開発でサーバー(クラウド)から画像を取得してきてリスト表示するようなUIを実装する場面は多いと思います。その際はURLを文字列として取得してきてそのパスを元にフェッチしてくるような構造をとることが多いと思います。

非同期画像ローディングライブラリの種類と違い

Swift UIではデフォルトでAsyncImageが用意されています。AsyncImage非同期で画像を取得・表示できる便利なViewですが、速度やカスタマイズ性はまだまだ発展途中という感じです。これの代替としてiOSで使える便利な非同期画像ローディングライブラリがいくつか用意されています。

項目 AsyncImage Nuke / NukeUI Kingfisher SDWebImageSwiftUI
キャッシュ ⚪(URLCache依存) ⚪︎ ⚪︎ ⚪︎
画像加工 ⚪︎ ⚪︎ ⚪︎
プレースホルダー ⚪︎(簡易) ⚪︎ ⚪︎ ⚪︎
エラー処理 ⚪︎(限定的) ⚪︎ ⚪︎ ⚪︎
プログレッシブ表示 ⚪︎ ⚪︎ ⚪︎
プリフェッチ ⚪︎ ⚪︎ ⚪︎
動画/アニメGIF ⚪︎ ⚪︎
WebPなど特殊フォーマット ⚪︎(拡張必要) ⚪︎ ⚪︎

今回はその中でもNukeの使用方法をまとめていきます。

Nuke

Nuke」はiOS / macOS / watchOSなどで使用できる非同期画像ローディングライブラリです。URLから画像を取得して表示するだけでなく、メモリ/ディスクへのキャッシュ・画像加工・プリフェッチなど様々な機能を簡単に利用できるようになっています。

ライブラリの導入は「Swift Package Manager(SPM)」のみ?がサポートされているようです。以下のURLを指定してNukeを導入していきます。

https://github.com/kean/Nuke

パッケージには以下の4つが含まれているので必要なもののみをピックアップして使用します。

Nuke.loadImage

NukeではNuke.loadImageメソッドでURLを元に画像を読み込むことが可能です。これを使用するだけで非同期ロード → メモリ/ディスクキャッシュ → UIImageViewに変換までを行なってくれます。

import Nuke

let url = URL(string: "https://appdev-room.com/image/swift.png")!
Nuke.loadImage(with: url, into: imageView)

ImagePipeline

Nukeのコア機能として提供されているのがImagePipelineです。キャッシュ設定やその他機能などを柔軟にカスタマイズして使用することが可能です。

import Nuke

// パイプライン作成
let pipeline = ImagePipeline {
  // プログレッシブJPEG表示
    $0.isProgressiveDecodingEnabled = true      
    // メモリキャッシュ
    $0.imageCache = ImageCache.shared           
    // ディスクキャッシュ
    $0.dataCache = try? DataCache(name: "com.example.datacache") 
}

// 画像URL
let url = URL(string: "https://appdev-room.com/image/swift.png")!

// 画像リクエスト
let request = ImageRequest(url: url)

// ロード実行
pipeline.loadImage(with: request) { result in
    switch result {
    case .success(let response):
        print("取得成功: \(response.image)")
    case .failure(let error):
        print("取得失敗: \(error)")
    }
}

キャッシュを活用する

ImagePipelineではメモリキャッシュとディスクキャッシュが用意されています。この違いは保存するデータと期間の違いです。特徴として高速に処理することが可能ですが永続化はされないため、アプリをキルしたり、メモリが解放されたりするとキャッシュも消えてしまいます。

両方有効にしていた場合は最初にメモリキャッシュを確認します。メモリになかった場合にディスクキャッシュが参照され、それでもなければネットワーク通信を行い画像を取得します。

メモリキャッシュ(ImageCache)

メモリキャッシュはImageCacheクラスで管理されています。

パイプラインではimageCacheプロパティにImageCacheを格納しておくだけです。sharedが用意されシングルトン設計になっています。

let pipeline = ImagePipeline {
    // メモリキャッシュ
    $0.imageCache = ImageCache.shared           
}

ディスクキャッシュ(DataCache)

ディスク(データ)キャッシュはImageCacheクラスで管理されています。

パイプラインではdataCacheプロパティにDataCacheを格納しておくだけです。引数にはキャッシュを保存するための名前を指定します。この名前はキャッシュ全体につけるための名前なので画像単位でつける必要はないようです。

// パイプライン作成
let pipeline = ImagePipeline {   
    // ディスクキャッシュ
    $0.dataCache = try? DataCache(name: "com.example.datacache") 
}

NukeUI:LazyImage

Nukeでは「NukeUI」としてUIコンポーネントに特化したライブラリも用意していくれています。Swift UIならLazyImage、UIKitならImageViewが用意されており、内部的にNukeのCore機能を使用してくれています。

公式リファレンス:LazyImage

Swift UIのLazyImageは挙動的にはAsyncImageと似ていますが内部的な画像処理にNukeが利用されています。引数にURLを渡すだけと簡単に利用することが可能で、このViewが画面に表示されたタイミングで読み込みを開始し、表示されなくなると読み込みは自動でキャンセルされます。そして再度表示されると中断した箇所から読み込みを再開することが可能です。

LazyImage(url: URL(string: "https://appdev-room.com/image/swift.png"))
  .frame(width: 200, height: 200)

デフォルト(特に指定なし)では読み込み完了までにグレーアウトするプレースホルダーが表示されるようになっており、細かくカスタマイズしたい場合は引数makeContentのクロージャからLazyImageState型で取得状況や画像などを参照することが可能です。

LazyImage(url: URL(string: "https://appdev-room.com/image/swift.png")) { state in
    if let image = state.image {
        image
            .resizable()
            .scaledToFit()
    } else if state.error != nil {
        // エラー時
        Color.red
    } else {
        // ローディング中
        ProgressView() 
    }
}.frame(width: 200, height: 200)

LazyImageはデフォルトではメモリキャッシュのみ有効になっているので、ディスクキャッシュを有効にするためには明示的に指定する必要があります。pipelineメソッドでインスタンス化したパイプライン設定を反映させることが可能です。

LazyImage(url: URL(string: "https://appdev-room.com/image/swift.png"))
    .pipeline(pipeline)
    .frame(width: 200, height: 200)

他にも .priority(.high)ロードの優先度を制御したり、processors画像を加工することも可能です。

LazyImage(url: URL(string: "https://appdev-room.com/image/swift.png"))
    .priority(.high)
    .processors(
        [
            // リサイズ
            ImageProcessors.Resize(size: CGSize(width: 200, height: 200), contentMode: .aspectFill),
            // 円形切り取り
            ImageProcessors.Circle(),
            // ぼかし
            ImageProcessors.GaussianBlur(radius: 8)
        ]
    )
    .pipeline(pipeline)
    .frame(width: 200, height: 200)

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

Search Box

Sponsor

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index