Swift:Cachable一個協議抽象持久化

前言

以前在這篇文章裏 咱們定義了一個Parsable 用於反序列化swift

protocol Parsable {
  static func parse(data: Data) -> Result<Self>
}
複製代碼

若是咱們要給自定義的類型添加一個持久化的功能,一樣也能夠定義一個協議,使得遵照這個協議的類型都擁有持久化這個功能。緩存

Cachable

public protocol Cachable {
  /// 存入沙盒
  ///
  /// - Parameter path: 路徑
  /// - Returns: 結果
  @discardableResult
  func save(at path: String) -> Result<Void, Error>

  /// 從沙盒中取
  ///
  /// - Parameter path: 路徑
  /// - Returns: 結果
  static func get(fromCache path: String) -> Self?
}
複製代碼

Cachable定義了兩個方法 存和取,接下來咱們給Cachable添加默認實現。post

Codable

大部分的反序列化與序列化都會使用很是方便Codableui

extension Cachable where Self: Codable {
  @discardableResult
  public func save(at path: String) -> Result<Void, Error> {
    let url = URL(fileURLWithPath: path)
    do {
      let data = try JSONEncoder().encode(self)
      try data.write(to: url)
      return .success(())
    } catch {
      print("Cachable: 存儲沙盒失敗 - \(error.localizedDescription)")
      return .failure(error)
    }
  }

  public static func get(fromCache path: String) -> Self? {
    let url = URL(fileURLWithPath: path)
    do {
      let data = try Data(contentsOf: url)
      return try JSONDecoder().decode(self, from: data)
    } catch {
      print("Cachable: 從沙盒中獲取失敗 - \(error.localizedDescription)")
      return nil
    }
  }
}
複製代碼

如此一來當一個類型遵循了Codable且也想遵循Cachable就能夠無償使用上面的兩個方法。url

struct Car: Codable {
  var engine: String
  var name: String
  var type: String
}

extenstion Car: Cachale {}

// 獲取沙盒中緩存的car
let carCachePath = getMyCarCachePath()
let myCacheCar = Car.get(fromCache: carCachePath)

// 緩存bmw
let bmw = Car(engine: "V8", name: "X7", type: "xDrive50i")
bmw.save(at: carCachePath)
複製代碼

注:咱們使用@discardableResult標記了save(at:)方法,目的是讓編譯器不警告咱們沒有使用save(at:)方法的返回值。不少鏈式調用的方法都有這個標記,例如Alamofire就使用了這個標記。spa

String、Data

咱們也能夠給StringData也添加這個功能。code

extension Cachable where Self == String {
  @discardableResult
  public func save(at path: String) -> Result<Void, Error> {
    let url = URL(fileURLWithPath: path)
    do {
      if let data = self.data(using: .utf8) {
        try data.write(to: url)
        return .success(())
      } else {
        return .failure(WWError.stringToDataError)
      }
    } catch {
      print("Cachable: 存儲沙盒失敗 - \(error.localizedDescription)")
      return .failure(error)
    }
  }

  public static func get(fromCache path: String) -> Self? {
    let url = URL(fileURLWithPath: path)
    do {
      let data = try Data(contentsOf: url)
      return self.init(data: data, encoding: .utf8)
    } catch {
      print("Cachable: 從沙盒中獲取失敗 - \(error.localizedDescription)")
      return nil
    }
  }
}
複製代碼

Data也是同樣的,你們能夠自行去實現。對象

SwiftProtobuf

若是有使用SwiftProtobuf也能夠給SwiftProtobufMessage添加這個功能。ip

extension Cachable where Self: SwiftProtobuf.Message {
  @discardableResult
  func save(at path: String) -> Result<Void, Error> {
    let url = URL(fileURLWithPath: path)
    do {
      let data = try serializedData()
      try data.write(to: url)
      return .success(())
    } catch {
      print("Cachable: 存儲沙盒失敗 - \(error.localizedDescription)")
      return .failure(error)
    }
  }

  static func get(fromCache path: String) -> Self? {
    let url = URL(fileURLWithPath: path)
    do {
      let data = try Data(contentsOf: url)
      return try self.init(serializedData: data)
    } catch {
      print("Cachable: 從沙盒中獲取失敗 - \(error.localizedDescription)")
      return nil
    }
  }
}
複製代碼

添加了默認實現後,咱們的Cachable已經能夠大展身手了,只要是知足Codable或是SwiftProtobuf裏的Message而且遵循了Cachable就能夠免費持久化了。get

總結

protocol絕對是用來描述某種功能的利器,並且不受類型的限制,萬物皆可遵循。想要JSON轉model就遵循Decodable,想要持久化某個對象就遵循咱們的Cachable...

只要你想提供某種功能,使用protocol抽象,給它添加默認實現,誰想用這個功能就遵循它,不想用也不會多餘。

相關文章
相關標籤/搜索