【Swift/iOS】Nuke:非同期画像ローディングライブラリの使い方!
この記事からわかること
- Nukeとは?
- Xcode/iOSで画像ローディングライブラリを使用する
- LazyImageの使い方
index
[open]
\ アプリをリリースしました /
環境
- Xcode:16.3
- iOS:18.4
- Swift:6
- macOS:Sequoia 15.4
非同期画像ローディングライブラリ
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・・・ImagePipeline・ImageRequestなどを備えたコアフレームワーク
- NukeUI・・・UIコンポーネント: LazyImage(SwiftUI) ・ImageView(UIKit、AppKit)
- NukeExtensions・・・UIImageView(UIKit、AppKit)の拡張機能
- NukeVideo・・・短いビデオをデコードして再生するためのコンポーネント
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クラスで管理されています。
- 保存形式:デコードされたUIImage
- 保存場所:メモリ
- 特 徴:高速・メモリが解放されると破棄される
パイプラインではimageCacheプロパティにImageCacheを格納しておくだけです。sharedが用意されシングルトン設計になっています。
let pipeline = ImagePipeline {
// メモリキャッシュ
$0.imageCache = ImageCache.shared
}
ディスクキャッシュ(DataCache)
ディスク(データ)キャッシュはImageCacheクラスで管理されています。
- 保存形式:画像のバイナリデータ(JPEG/PNG/WebPなど)
- 保存場所:アプリのストレージ
- 特 徴:メモリよりは低速だがネットワーク通信よりは高速・明示的に破棄するまで永続化
パイプラインでは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機能を使用してくれています。
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)
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。





