From 8a3c2b2572a70114e9c3090856538513df919bae Mon Sep 17 00:00:00 2001 From: Lucas Nelaupe Date: Fri, 20 Apr 2018 13:02:41 +0800 Subject: [PATCH] Make JobInfo conform to Codable protocol --- Sources/SwiftQueue/Constrains+Network.swift | 2 +- Sources/SwiftQueue/JobInfo.swift | 121 ++++++++++++++++++++ Sources/SwiftQueue/Utils.swift | 23 ++++ 3 files changed, 145 insertions(+), 1 deletion(-) diff --git a/Sources/SwiftQueue/Constrains+Network.swift b/Sources/SwiftQueue/Constrains+Network.swift index c6f23823..83d6cfe6 100644 --- a/Sources/SwiftQueue/Constrains+Network.swift +++ b/Sources/SwiftQueue/Constrains+Network.swift @@ -8,7 +8,7 @@ import Reachability #endif /// Kind of connectivity required for the job to run -public enum NetworkType: Int { +public enum NetworkType: Int, Codable { /// Job will run regardless the connectivity of the platform case any = 0 /// Requires at least cellular such as 2G, 3G, 4G, LTE or Wifi diff --git a/Sources/SwiftQueue/JobInfo.swift b/Sources/SwiftQueue/JobInfo.swift index 5e8a65f2..36b3484d 100644 --- a/Sources/SwiftQueue/JobInfo.swift +++ b/Sources/SwiftQueue/JobInfo.swift @@ -91,6 +91,127 @@ struct JobInfo { } } +extension JobInfo: Decodable { + + enum JobInfoKeys: String, CodingKey { + case type = "type" + case uuid = "uuid" + case override = "override" + case group = "group" + case tags = "tags" + case delay = "delay" + case deadline = "deadline" + case requireNetwork = "requireNetwork" + case isPersisted = "isPersisted" + case params = "params" + case createTime = "createTime" + case interval = "runCount" + case maxRun = "maxRun" + case retries = "retries" + case runCount = "interval" + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: JobInfoKeys.self) + + let type: String = try container.decode(String.self, forKey: .type) + let uuid: String = try container.decode(String.self, forKey: .uuid) + let override: Bool = try container.decode(Bool.self, forKey: .override) + let group: String = try container.decode(String.self, forKey: .group) + let tags: Set = try container.decode(Set.self, forKey: .tags) + let delay: TimeInterval? = try container.decodeIfPresent(TimeInterval.self, forKey: .delay) + let deadline: Date? = try container.decodeIfPresent(Date.self, forKey: .deadline) + let requireNetwork: NetworkType = try container.decode(NetworkType.self, forKey: .requireNetwork) + let isPersisted: Bool = try container.decode(Bool.self, forKey: .isPersisted) + let params: [String: Any] = try container.decode([String: Any].self, forKey: .params) + let createTime: Date = try container.decode(Date.self, forKey: .createTime) + let interval: TimeInterval = try container.decode(TimeInterval.self, forKey: .interval) + let maxRun: Limit = try container.decode(Limit.self, forKey: .maxRun) + let retries: Limit = try container.decode(Limit.self, forKey: .retries) + let runCount: Double = try container.decode(Double.self, forKey: .runCount) + + self.init( + type: type, + uuid: uuid, + override: override, + group: group, + tags: tags, + delay: delay, + deadline: deadline, + requireNetwork: requireNetwork, + isPersisted: isPersisted, + params: params, + createTime: createTime, + interval: interval, + maxRun: maxRun, + retries: retries, + runCount: runCount) + } +} + +extension JobInfo: Encodable { + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: JobInfoKeys.self) + try container.encode(type, forKey: .type) + try container.encode(uuid, forKey: .uuid) + try container.encode(override, forKey: .override) + try container.encode(group, forKey: .group) + try container.encode(tags, forKey: .tags) + try container.encode(delay, forKey: .delay) + try container.encode(deadline, forKey: .deadline) + try container.encode(requireNetwork, forKey: .requireNetwork) + try container.encode(isPersisted, forKey: .isPersisted) + try container.encode(params, forKey: .params) + try container.encode(createTime, forKey: .createTime) + try container.encode(interval, forKey: .interval) + try container.encode(maxRun, forKey: .maxRun) + try container.encode(retries, forKey: .retries) + try container.encode(runCount, forKey: .runCount) + } +} + +extension KeyedDecodingContainer { + + public func decode(_ type: Data.Type, forKey key: KeyedDecodingContainer.Key) throws -> Data { + let json = try self.decode(String.self, forKey: key) + guard let data = json.data(using: .utf8) else { + throw DecodingError.dataCorrupted(DecodingError.Context( + codingPath: [key], + debugDescription: "Unable to convert string to utf-8") + ) + } + return data + } + + public func decode(_ type: [String: Any].Type, forKey key: KeyedDecodingContainer.Key) throws -> [String: Any] { + let data = try self.decode(Data.self, forKey: key) + guard let dict = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] else { + throw DecodingError.dataCorrupted(DecodingError.Context( + codingPath: [key], + debugDescription: "Decoded value is not a dictionary") + ) + } + return dict + } + +} + +extension KeyedEncodingContainer { + + public mutating func encode(_ value: [String: Any], forKey key: KeyedEncodingContainer.Key) throws { + let jsonData = try JSONSerialization.data(withJSONObject: value) + guard let utf8 = String(data: jsonData, encoding: .utf8) else { + throw DecodingError.dataCorrupted(DecodingError.Context( + codingPath: [key], + debugDescription: "The given data was not valid JSON.") + ) + } + try self.encode(utf8, forKey: key) + } + +} + extension JobInfo { init?(dictionary: [String: Any]) { diff --git a/Sources/SwiftQueue/Utils.swift b/Sources/SwiftQueue/Utils.swift index d0e9c64d..fa41ece3 100644 --- a/Sources/SwiftQueue/Utils.swift +++ b/Sources/SwiftQueue/Utils.swift @@ -66,6 +66,29 @@ internal extension Limit { } +extension Limit: Codable { + + private enum CodingKeys: String, CodingKey { case value } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + let value = try values.decode(Double.self, forKey: .value) + self = value < 0 ? Limit.unlimited : Limit.limited(value) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + switch self { + case .unlimited: + try container.encode(-1, forKey: .value) + case .limited(let value): + assert(value >= 0) + try container.encode(value, forKey: .value) + } + } + +} + extension Limit: Equatable { public static func == (lhs: Limit, rhs: Limit) -> Bool {