我們的大多數應用程式都是某些後端的REST用戶端。在開發此類應用程式期間,我們希望使其保持脫機狀态。在這種情況下,我們必須将資料緩存在裝置本地的某處,以使其無需網際網路即可讀取。
Apple提供了
CoreData
架構,這是在本地存儲應用程式資料的最佳方法。它具有許多出色的功能,可幫助您促進開發。但是,很難将其用作簡單的緩存。大多數時候,我們隻需要顯示緩存的資料,而無需任何其他操作。我認為,我們所需要的隻是純磁盤存儲。本周,我們将讨論如何輕松地為
Codable
結構實作簡單的磁盤存儲。

CodableStorage
首先,為我們的存儲邏輯定義幾個協定。我想分開通路存儲的可寫和可讀部分,這是我們可以使用Swift語言的協定組合功能的地方。
import Foundation
typealias Handler<T> = (Result<T, Error>) -> Void
protocol ReadableStorage {
func fetchValue(for key: String) throws -> Data
func fetchValue(for key: String, handler: @escaping Handler<Data>)
}
protocol WritableStorage {
func save(value: Data, for key: String) throws
func save(value: Data, for key: String, handler: @escaping Handler<String>)
}
typealias Storage = ReadableStorage & WritableStorage
複制
首先,讓我們描述一些用于存儲的根路徑的變量,用于異步工作的
DispatchQueue
和
FileManager
,我們将使用它們來浏覽檔案系統。
class DiskStorage {
private let path: URL
private let queue: DispatchQueue
private let fileManager: FileManager
init(
path: URL,
queue: DispatchQueue = .init(label: "DiskCache.Queue"),
fileManager: FileManager = FileManager.default
) {
self.path = path
self.queue = queue
self.fileManager = fileManager
}
private func createFolders(in url: URL) throws {
let folderUrl = url.deletingLastPathComponent()
if !fileManager.fileExists(atPath: folderUrl.path) {
try fileManager.createDirectory(
at: folderUrl,
withIntermediateDirectories: true,
attributes: nil)
}
}
}
複制
下一步是實作我們存儲的可寫部分。這有點棘手,因為key是檔案系統上資料的路徑。是以,我們需要将ke'y附加到根路徑并生成用于存儲資料的新URL。新URL可以包含子檔案夾,這就是我們建立
createFolders
函數的原因,該函數根據路徑建立所需的檔案夾。
extension DiskStorage: WritableStorage {
func save(value: Data, for key: String) throws {
let url = path.appendingPathComponent(key)
do {
try self.createFolders(in: url)
try value.write(to: url, options: .atomic)
} catch {
throw StorageError.cantWrite(error)
}
}
}
複制
這是存儲協定的可讀部分,我們在其中為傳遞的key實作資料擷取。同樣,我們使用key作為磁盤上資料的路徑。
extension DiskStorage: ReadableStorage {
func fetchValue(for key: String) throws -> Data {
let url = path.appendingPathComponent(key)
guard let data = fileManager.contents(atPath: url.path) else {
throw StorageError.notFound
}
return data
}
}
複制
現在我們有了一個簡單的磁盤存儲的工作示例。下一步是為我們的
DiskStorage
類實作一個簡單的擴充卡,該擴充卡将處理JSON編碼/解碼。
class CodableStorage {
private let path: URL
private var storage: DiskStorage
private let decoder: JSONDecoder
private let encoder: JSONEncoder
init(
path: URL = URL(fileURLWithPath: NSTemporaryDirectory()),
decoder: JSONDecoder = .init(),
encoder: JSONEncoder = .init()
) {
self.path = path
self.storage = DiskStorage(path: path)
self.decoder = decoder
self.encoder = encoder
}
func fetch<T: Decodable>(for key: String) throws -> T {
let data = try storage.fetchValue(for: key)
return try decoder.decode(T.self, from: data)
}
func save<T: Encodable>(_ value: T, for key: String) throws {
let data = try encoder.encode(value)
try storage.save(value: data, for: key)
}
}
複制
CodableStorage
類包裝我們的
DiskStorage
類以添加JSON編解碼邏輯。它使用通用限制來了解如何解碼和編碼資料。現在該在實際示例中使用我們的
CodableStorage
了。
#### 使用示例:
struct Timeline: Codable {
let tweets: [String]
}
let storage = CodableStorage()
let timeline = Timeline(tweets: ["Hello", "World", "!!!"])
// -----同步:-----
//寫
do {
try storage.save(timeline, for: "timeline")
} catch {
print(error)
}
//讀
if let cached: Timeline = try? storage.fetch(for: "timeline") {
print(cached)
}
//讀
do {
let cached: Timeline = try storage.fetch(for: "timeline")
print(cached)
} catch {
print(error)
}
//删單個
do {
try storage.delete(for: "timeline")
} catch {
print(error)
}
//删所有
do {
try storage.deleteAll()
} catch {
print(error)
}
// -----異步:-----
//存
storage.asyncSave(timeline, for: "timeline") { result in
switch result {
case .success(let key):
print("async save \(key) success")
case .failure(let error):
print(error)
}
}
//取
storage.asyncFetch(for: "timeline") { (result: Result<Timeline, Error>) in
switch result {
case .success(let timeline):
print(timeline)
case .failure(let err):
print(err)
}
}
//删單個
storage.asyncDelete(for: "timeline") { (result) in
switch result {
case .success(_):
print("删除成功")
case .failure(let error):
print(error)
}
}
//删除全部
storage.asyncDeleteAll() { (result) in
switch result {
case .success(_):
print("删除成功")
case .failure(let error):
print(error)
}
}
複制
在上面的代碼示例中,您可以看到
CodableStorage
類的用法。
Timeline
是一個簡單的遵循
Codable
協定的結構體,表示存儲在
CodableStorage
中的字元串數組。
今天,我們讨論了一種可存儲可編碼結構的簡單方法,該結構可通過REST API擷取。有時候,我們不需要CoreData的複雜功能即可進行簡單的JSON緩存,這足以實作磁盤存儲。
DEMO已經上傳至GitHub:CodableStorage,将
CodableStorage
檔案夾拖到項目即可直接使用。
DEMO已實作功能:同步/異步 存,取,删
本文内容來自https://swiftwithmajid.com/2019/05/22/storing-codable-structs-on-the-disk/
對原文進行了簡單翻譯,同時修改了部分代碼,便于使用