2020-03-05 14:49:21 -08:00
|
|
|
//
|
|
|
|
|
// CodableServerError.swift
|
|
|
|
|
// AltKit
|
|
|
|
|
//
|
|
|
|
|
// Created by Riley Testut on 3/5/20.
|
|
|
|
|
// Copyright © 2020 Riley Testut. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
import Foundation
|
|
|
|
|
|
|
|
|
|
// Can only automatically conform ALTServerError.Code to Codable, not ALTServerError itself
|
|
|
|
|
extension ALTServerError.Code: Codable {}
|
|
|
|
|
|
2020-06-05 14:19:40 -07:00
|
|
|
extension CodableServerError
|
|
|
|
|
{
|
|
|
|
|
enum UserInfoValue: Codable
|
|
|
|
|
{
|
|
|
|
|
case string(String)
|
|
|
|
|
case error(NSError)
|
|
|
|
|
|
|
|
|
|
public init(from decoder: Decoder) throws
|
|
|
|
|
{
|
|
|
|
|
let container = try decoder.singleValueContainer()
|
|
|
|
|
|
|
|
|
|
if
|
|
|
|
|
let data = try? container.decode(Data.self),
|
|
|
|
|
let error = try? NSKeyedUnarchiver.unarchivedObject(ofClass: NSError.self, from: data)
|
|
|
|
|
{
|
|
|
|
|
self = .error(error)
|
|
|
|
|
}
|
|
|
|
|
else if let string = try? container.decode(String.self)
|
|
|
|
|
{
|
|
|
|
|
self = .string(string)
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
throw DecodingError.dataCorruptedError(in: container, debugDescription: "UserInfoValue value cannot be decoded")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func encode(to encoder: Encoder) throws
|
|
|
|
|
{
|
|
|
|
|
var container = encoder.singleValueContainer()
|
|
|
|
|
|
|
|
|
|
switch self
|
|
|
|
|
{
|
|
|
|
|
case .string(let string): try container.encode(string)
|
|
|
|
|
case .error(let error):
|
|
|
|
|
guard let data = try? NSKeyedArchiver.archivedData(withRootObject: error, requiringSecureCoding: true) else {
|
|
|
|
|
let context = EncodingError.Context(codingPath: container.codingPath, debugDescription: "UserInfoValue value \(self) cannot be encoded")
|
|
|
|
|
throw EncodingError.invalidValue(self, context)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try container.encode(data)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-05 14:49:21 -08:00
|
|
|
struct CodableServerError: Codable
|
|
|
|
|
{
|
|
|
|
|
var error: ALTServerError {
|
|
|
|
|
return ALTServerError(self.errorCode, userInfo: self.userInfo ?? [:])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private var errorCode: ALTServerError.Code
|
2020-06-05 14:19:40 -07:00
|
|
|
private var userInfo: [String: Any]?
|
2020-03-05 14:49:21 -08:00
|
|
|
|
|
|
|
|
private enum CodingKeys: String, CodingKey
|
|
|
|
|
{
|
|
|
|
|
case errorCode
|
|
|
|
|
case userInfo
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
init(error: ALTServerError)
|
|
|
|
|
{
|
|
|
|
|
self.errorCode = error.code
|
|
|
|
|
|
2020-06-05 14:19:40 -07:00
|
|
|
var userInfo = error.userInfo
|
2020-05-21 21:00:05 -07:00
|
|
|
if let localizedRecoverySuggestion = (error as NSError).localizedRecoverySuggestion
|
|
|
|
|
{
|
|
|
|
|
userInfo[NSLocalizedRecoverySuggestionErrorKey] = localizedRecoverySuggestion
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-05 14:49:21 -08:00
|
|
|
if !userInfo.isEmpty
|
|
|
|
|
{
|
|
|
|
|
self.userInfo = userInfo
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
init(from decoder: Decoder) throws
|
|
|
|
|
{
|
|
|
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
|
|
|
|
|
|
|
|
let errorCode = try container.decode(Int.self, forKey: .errorCode)
|
|
|
|
|
self.errorCode = ALTServerError.Code(rawValue: errorCode) ?? .unknown
|
|
|
|
|
|
2020-06-05 14:19:40 -07:00
|
|
|
let rawUserInfo = try container.decodeIfPresent([String: UserInfoValue].self, forKey: .userInfo)
|
|
|
|
|
|
|
|
|
|
let userInfo = rawUserInfo?.mapValues { (value) -> Any in
|
|
|
|
|
switch value
|
|
|
|
|
{
|
|
|
|
|
case .string(let string): return string
|
|
|
|
|
case .error(let error): return error
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-05 14:49:21 -08:00
|
|
|
self.userInfo = userInfo
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func encode(to encoder: Encoder) throws
|
|
|
|
|
{
|
|
|
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
|
|
|
try container.encode(self.error.code.rawValue, forKey: .errorCode)
|
2020-06-05 14:19:40 -07:00
|
|
|
|
|
|
|
|
let rawUserInfo = self.userInfo?.compactMapValues { (value) -> UserInfoValue? in
|
|
|
|
|
switch value
|
|
|
|
|
{
|
|
|
|
|
case let string as String: return .string(string)
|
|
|
|
|
case let error as NSError: return .error(error)
|
|
|
|
|
default: return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
try container.encodeIfPresent(rawUserInfo, forKey: .userInfo)
|
2020-03-05 14:49:21 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|