Open5

iOSアプリにおけるキャッシュ

kamimikamimi

キャッシュされたデータへのアクセス

  • URL ローディングシステムは、メモリとディスクにレスポンスをキャッシュする
  • 目的
    • パフォーマンス向上
    • ネットワークトラフィックの削減
kamimikamimi

NSURLRequest.CachePolicyについて

  • キャッシュされたレスポンスとの相互作用を指定するための定数
  • デフォルトはuseProtocolCachePolicy

実装方法

URLRequestをインスタンス化するときにプロパティに渡す

let imageURL = ""
var request = URLRequest(url: imageURL, cachePolicy: .returnCacheDataElseLoad) // ここで指定する

URLConfigurationをインスタンス化するときにプロパティに渡す

let config = URLSessionConfiguration.default
config.requestCachePolicy = .returnCacheDataElseLoad
キャッシュポリシー ローカルのキャッシュ サーバ
reloadIgnoringLocalCacheData 無視する 排他的にアクセスする
returnCacheDataDontLoad 排他的にアクセスする 無視する
returnCacheDataElseLoad 初回に試す 必要な時のみアクセスする
useProtocolCachePolicy プロトコルによる プロトコルによる

useProtocolCachePolicy が選択された場合のキャッシュのdecition tree

https://developer.apple.com/documentation/foundation/nsurlrequest/cachepolicy/useprotocolcachepolicy

キャッシュに直接アクセスする方法

  • cachedResponse(for:)を呼ぶ
    • キャッシュがあれば、CachedURLResponseオブジェクトが返る。なければnilになる

キャッシュはどこにどのくらい保存されているのか

  • currentDiskCachecurrenctMemoryCacheを調べることで、キャッシュが使っているリソースがどこでどのくらいかを調べることができる

キャッシュを削除する方法

  • URLRequestごとに削除したい場合は、removeCachedResponse(for:)
  • 特定の日付以降のキャッシュを削除したい場合は、removeCachedResponse(since:)
  • 全てのキャッシュを削除したい場合は、removeAllCachedResponse()

キャッシュを手動でハンドリングする方法

  • レスポンスごとにキャッシュの仕組みを管理したい場合に実装する

実装方法

  • URLSessionDataDelegateプロトコルのurlSession(_:dataTask:willCacheResponse:completionHandler:)を呼び出す
  • completionHandlerに次のいずれかを渡して呼び出す必要がある
    • CachedURLResponseオブジェクト(提供されたオブジェクトproposedResponseをそのままキャッシュする)
    • niil(キャッシュしない)
    • 自作したCachedURLRequestオブジェクト(storagePolicyuserInfoをカスタマイズすることができる)
// 以下はhttpsできたレスポンスは、メモリ上でのキャッシュのみを許す実装
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask,
                willCacheResponse proposedResponse: CachedURLResponse,
                completionHandler: @escaping (CachedURLResponse?) -> Void) {
    if proposedResponse.response.url?.scheme == "https" {
        let updatedResponse = CachedURLResponse(response: proposedResponse.response,
                                                data: proposedResponse.data,
                                                userInfo: proposedResponse.userInfo,
                                                storagePolicy: .allowedInMemoryOnly)
        completionHandler(updatedResponse)
    } else {
        completionHandler(proposedResponse)
    }
}

https://developer.apple.com/documentation/foundation/nsurlrequest/cachepolicy

https://medium.nextlevelswift.com/urlrequest-cache-policy-f7c30a96b698

https://developer.apple.com/documentation/foundation/url_loading_system/accessing_cached_data

kamimikamimi

URLCacheについて

  • URL リクエスト(NSURLRequest)と、キャッシュしたレスポンス(CachedURLResponse)のオブジェクトをマップするためのオブジェクト

  • iOSでは、アプリが起動していない時のみ、ディスク容量が少なくなると、ディスクのキャッシュを削除する

private let session: URLSession = {
  let configuration = URLConfiguratio.default
  let urlCache: URLCache = {
    let memoryCapacity = 6 * 1024 * 1024 // 6MB
    let diskCapacity = 6 * 1024 * 1024 // 6MB
    let cacheDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first?.appendingPathComponent("\(Bundle.main.bundleIdentifier!)/hogehoge")
    return URLCache(
      memoryCapacity: memoryCapcity,
      diskCapacity: diskCapacity,
      directory: cacheDir
    )()
  }
  configuration.urlCache = urlCache
  let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: nil)
  return session
}()

https://developer.apple.com/documentation/foundation/urlcache

キャッシュのディスクとメモリについて

kamimikamimi

SwiftUIにおける画像キャッシュ

https://www.avanderlee.com/swiftui/downloading-caching-images/

https://stackoverflow.com/questions/69214543/how-can-i-add-caching-to-asyncimage

https://www.youtube.com/watch?v=KhGyiOk3Yzk

AsyncImage は必要に応じて画像をキャッシュしているとのことだが、以下の記事によるとしていなそうとのこと。

https://matteomanferdini.com/swiftui-asyncimage/

以下のライブラリを参考に、自分でCachedAsyncImageを実装しても良さそうだし、このまま使っても良さそう

https://github.com/lorenzofiamingo/swiftui-cached-async-image