spiegel-keyman/ios/engine/KMEI/KeymanEngine/Classes/Queries/Queries.swift
2020-08-07 11:48:08 +07:00

101 lines
3.1 KiB
Swift

//
// 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: Decodable> = (_: 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<T: Decodable>(resultType: T.Type, completionBlock: @escaping JSONQueryCompletionBlock<T>) -> 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))
}
}
}
}
}
}