[iOS/Swift] アプリ開発の実務的アプローチで学ぶデザインパターン ~Flyweight~


この記事シリーズは、iOS/Swiftエンジニアである執筆者個人が、
ごく普通のiOSアプリ開発でよくある状況
Swiftのコアライブラリやフレームワークで使われているパターン
着目してデザインパターンを学び直してみた記録です。

関連記事一覧
[iOS/Swift] アプリ開発の実務的アプローチで学ぶデザインパターン

Flyweightパターン概要

  • 一度生成したインスタンスを使いまわしてメモリ消費や処理時間を抑える手法、すなわちキャッシュのことです。
  • キャッシュはstatic変数またはSingletonで宣言します(単一インスタンスでないとキャッシュとして意味をなさないので)。
  • GoFのでデザインパターンでは構造に関するパターンに分類されます。

使い所

  • サーバー上の画像をアプリ上で表示する時、一度取得した画像はアプリ内にキャッシュしておき、毎回サーバーにアクセスすることを避ける、とか。

サンプルコード

Swiftバージョンは 5.1 です。

class ImageRepository {
    // 画像キャッシュ
    static var imageCache = NSCache<AnyObject, AnyObject>()

    class func requestImage(from urlString: String, completion: @escaping (UIImage?) -> Void ) {
        // キャッシュを探して存在する場合はそれを使う
        if let cacheImage = imageCache.object(forKey: urlString as AnyObject) as? UIImage {
            completion(cacheImage)
            return
        }

        guard let url = URL(string: urlString) else {
            return
        }

        // キャッシュに存在しない場合はサーバーから取得する
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            if let error = error {
                print("画像取得失敗: ", error)
                return
            }

            guard let data = data, let image = UIImage(data: data) else {
                return
            }
            // 画像を取得できたらキャッシュに保存する
            imageCache.setObject(image, forKey: urlString as AnyObject)
            completion(image)
        }.resume()
    }
}

// Usage
class ViewController: UIViewController {
    // 省略
    @IBAction func tappedButton(_ sender: Any) {
        let imageView = UIImageView()
        let urlString = "https://upload.wikimedia.org/wikipedia/commons/5/56/Donald_Trump_official_portrait.jpg"
        ImageRepository.requestImage(from: urlString) { (image) in
            DispatchQueue.main.async {
                imageView.image = image
            }
        }
    }
}

補足:
アプリ開発実務においては、UIImageViewのextensionとする方が使い勝手は良いかも知れません。