// // Queries.swift // KeymanEngine // // Created by Joshua Horton on 6/25/20. // Copyright © 2020 SIL International. All rights reserved. // import Foundation // The core `Queries` definition provides common utility methods, handlers, etc across our API queries. public class Queries { public enum FetchError: LocalizedError { case networkError(Error) case noData case parsingError(Error) case decodingError(String, String) var localizedDescription: String { switch self { case .noData: return engineBundle.localizedString(forKey: "error-query-no-data", value: nil, table: nil) case .networkError(_): return engineBundle.localizedString(forKey: "error-query-general", value: nil, table: nil) case .parsingError(_): return engineBundle.localizedString(forKey: "error-query-decoding", value: nil, table: nil) case .decodingError(_, _): return engineBundle.localizedString(forKey: "error-query-decoding", value: nil, table: nil) } } } public enum ResultError: LocalizedError { case unqueried var localizedDescription: String { switch self { case .unqueried: return "Query was not run against specified parameter" } } } struct IDCodingKey: CodingKey { /* Required by CodingKey, though we don't particularly need these */ public var intValue: Int? public init?(intValue: Int) { self.init(stringValue: "\(intValue)") self.intValue = intValue } /* End section */ var stringValue: String init?(stringValue: String) { self.stringValue = stringValue } } public typealias JSONQueryCompletionBlock = (_: T?, _: Queries.FetchError?) -> Void typealias DataTaskCompletionBlock = (_: Data?, _: URLResponse?, _: Error?) -> Void /** * Returns a closure that wraps closures expecting a decodable type, * performing standard error checking and decoding the query's results * on behalf of the wrapped closure. * * Returns either the decoded result when successful or the error that * occurred when attempting to run the query. */ internal static func jsonDataTaskCompletionAdapter(resultType: T.Type, completionBlock: @escaping JSONQueryCompletionBlock) -> DataTaskCompletionBlock { return { data, response, baseError in guard baseError == nil else { DispatchQueue.main.async { completionBlock(nil, .networkError(baseError!)) } return } guard let data = data else { DispatchQueue.main.async { completionBlock(nil, .noData) } return } let decoder = JSONDecoder() decoder.dateDecodingStrategy = .secondsSince1970 do { let result = try decoder.decode(resultType, from: data) DispatchQueue.main.async { completionBlock(result, nil) } } catch { DispatchQueue.main.async { if let error = error as? FetchError { completionBlock(nil, error) } else { completionBlock(nil, .parsingError(error)) } } } } } }