This commit is contained in:
panxi 2019-11-26 12:42:48 -05:00
commit 293449a48e
10 changed files with 299 additions and 13 deletions

View File

@ -45,7 +45,7 @@
946EE1B0237B5EF70036751F /* JSONHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 946EE1AF237B5EF70036751F /* JSONHelper.swift */; };
946EE1B2237B5F260036751F /* JSONValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 946EE1B1237B5F260036751F /* JSONValue.swift */; };
946EE1B4237B619D0036751F /* Encoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 946EE1B3237B619D0036751F /* Encoder.swift */; };
946EE1BC237B691A0036751F /* ActionMapModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 946EE1BB237B691A0036751F /* ActionMapModel.swift */; };
946EE1BC237B691A0036751F /* ActionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 946EE1BB237B691A0036751F /* ActionModel.swift */; };
A332F33F20C7231700DCD9D9 /* MVMCoreViewControllerAnimatedTransitioning.h in Headers */ = {isa = PBXBuildFile; fileRef = A332F33E20C7231600DCD9D9 /* MVMCoreViewControllerAnimatedTransitioning.h */; settings = {ATTRIBUTES = (Public, ); }; };
AF1201832108C9B400E2F592 /* MVMCoreViewManagerViewControllerProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = AF1201812108C9B400E2F592 /* MVMCoreViewManagerViewControllerProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
AF26DDAE1FCE6A37004E8F65 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = AF26DDB01FCE6A37004E8F65 /* Localizable.strings */; };
@ -181,7 +181,7 @@
946EE1AF237B5EF70036751F /* JSONHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONHelper.swift; sourceTree = "<group>"; };
946EE1B1237B5F260036751F /* JSONValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONValue.swift; sourceTree = "<group>"; };
946EE1B3237B619D0036751F /* Encoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Encoder.swift; sourceTree = "<group>"; };
946EE1BB237B691A0036751F /* ActionMapModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionMapModel.swift; sourceTree = "<group>"; };
946EE1BB237B691A0036751F /* ActionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionModel.swift; sourceTree = "<group>"; };
A332F33E20C7231600DCD9D9 /* MVMCoreViewControllerAnimatedTransitioning.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreViewControllerAnimatedTransitioning.h; sourceTree = "<group>"; };
AF1201812108C9B400E2F592 /* MVMCoreViewManagerViewControllerProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreViewManagerViewControllerProtocol.h; sourceTree = "<group>"; };
AF26DDAF1FCE6A37004E8F65 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
@ -419,7 +419,7 @@
946EE1B6237B66630036751F /* ActionType */ = {
isa = PBXGroup;
children = (
946EE1BB237B691A0036751F /* ActionMapModel.swift */,
946EE1BB237B691A0036751F /* ActionModel.swift */,
);
path = ActionType;
sourceTree = "<group>";
@ -845,7 +845,7 @@
AFBB965F1FBA3A570008D868 /* MFFreebeeOperation.m in Sources */,
AFBB96901FBA3A9A0008D868 /* MVMCoreNavigationObject.m in Sources */,
946EE1AB237B5C940036751F /* Decoder.swift in Sources */,
946EE1BC237B691A0036751F /* ActionMapModel.swift in Sources */,
946EE1BC237B691A0036751F /* ActionModel.swift in Sources */,
AFBB96A61FBA3A9A0008D868 /* MVMCoreTopAlertOperation.m in Sources */,
881D26931FCC9D180079C521 /* MVMCoreErrorObject.m in Sources */,
946EE1B0237B5EF70036751F /* JSONHelper.swift in Sources */,

View File

@ -0,0 +1,10 @@
import Foundation
public typealias JSONArray = [[String: Any]]
public typealias JSONObject = [String: Any]
public enum JSONError: Error {
case pathNotFound
case data(path: String)
case other(error: Error)
}

View File

@ -0,0 +1,122 @@
import Foundation
public typealias JSONValueArray = [[String: JSONValue]]
public typealias JSONValueObject = [String: JSONValue]
public enum JSONValueError: Error {
case encode
}
extension Optional {
func or(_ other: Optional) -> Optional {
switch self {
case .none: return other
case .some: return self
}
}
func resolve(with error: @autoclosure () -> Error) throws -> Wrapped {
switch self {
case .none: throw error()
case .some(let wrapped): return wrapped
}
}
}
public enum JSONValue: Codable, Equatable {
case string(String)
case int(Int)
case double(Double)
case bool(Bool)
case object([String: JSONValue])
case array([JSONValue])
case null
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let value = ((try? container.decode(String.self)).map(JSONValue.string))
.or((try? container.decode(Int.self)).map(JSONValue.int))
.or((try? container.decode(Double.self)).map(JSONValue.double))
.or((try? container.decode(Bool.self)).map(JSONValue.bool))
.or((try? container.decode([String: JSONValue].self)).map(JSONValue.object))
.or((try? container.decode([JSONValue].self)).map(JSONValue.array))
self = value ?? JSONValue.null
}
func decode<T: Decodable>() throws -> T {
let encoded = try JSONEncoder().encode(self)
return try JSONDecoder().decode(T.self, from: encoded)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .string(let string): try container.encode(string)
case .int(let int): try container.encode(int)
case .double(let double): try container.encode(double)
case .bool(let bool): try container.encode(bool)
case .object(let object): try container.encode(object)
case .array(let array): try container.encode(array)
case .null: break
}
}
}
public func ==(lhs: JSONValue, rhs: JSONValue) -> Bool {
let ld = try? lhs.encode()
let rd = try? lhs.encode()
return ld == rd
}
extension JSONValue: ExpressibleByStringLiteral {
public init(stringLiteral value: String) {
self = .string(value)
}
}
extension JSONValue: ExpressibleByIntegerLiteral {
public init(integerLiteral value: Int) {
self = .int(value)
}
}
extension JSONValue: ExpressibleByFloatLiteral {
public init(floatLiteral value: Double) {
self = .double(value)
}
}
extension JSONValue: ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = .bool(value)
}
}
extension JSONValue: ExpressibleByArrayLiteral {
public init(arrayLiteral elements: JSONValue...) {
self = .array(elements)
}
}
extension JSONValue: ExpressibleByDictionaryLiteral {
public init(dictionaryLiteral elements: (String, JSONValue)...) {
self = .object([String: JSONValue](uniqueKeysWithValues: elements))
}
}
extension Dictionary where Key == String, Value == Any {
public func toJSONValue() throws -> [String: JSONValue] {
let data = try JSONSerialization.data(withJSONObject: self, options: .prettyPrinted)
return try JSONDecoder().decode([String:JSONValue].self, from: data)
}
}
extension Dictionary where Key == String, Value == JSONValue {
public func toJSONObject() throws -> JSONObject {
let encoded = try JSONEncoder().encode(self)
guard let json = try JSONSerialization.jsonObject(with: encoded, options: .mutableContainers) as? JSONObject else {
throw JSONValueError.encode
}
return json
}
}

View File

@ -1,5 +1,5 @@
//
// ActionMapModel.swift
// ActionModel.swift
// MVMCore
//
// Created by Suresh, Kamlesh on 10/3/19.
@ -8,12 +8,10 @@
import Foundation
@objcMembers public class ActionMapModel: Codable {
@objcMembers public class ActionModel: Codable {
public var actionType: String?
public var pageType: String?
public init(actionType: String?, pageType: String?) {
self.actionType = actionType
self.pageType = pageType
}
}

View File

@ -0,0 +1,45 @@
import Foundation
public protocol AnyDecoder {
func decode<T: Decodable>(_ type: T.Type, from data: Data) throws -> T
}
extension JSONDecoder: AnyDecoder {}
extension PropertyListDecoder: AnyDecoder {}
extension Data {
public func decode<T: Decodable>(using decoder: AnyDecoder = JSONDecoder()) throws -> T {
return try decoder.decode(T.self, from: self)
}
}
extension KeyedDecodingContainerProtocol {
public func decode<T: Decodable>(forKey key: Key) throws -> T {
return try decode(T.self, forKey: key)
}
public func decode<T: Decodable>(
forKey key: Key,
default defaultExpression: @autoclosure () -> T
) throws -> T {
return try decodeIfPresent(T.self, forKey: key) ?? defaultExpression()
}
}
extension Decodable {
public static func decode(fileName: String, bundle: Bundle = Bundle.main) throws -> Self {
guard let path = bundle.path(forResource: fileName, ofType: ".json") else {
throw JSONError.pathNotFound
}
guard let jsonData = try? Data(contentsOf: URL(fileURLWithPath: path)) else {
throw JSONError.data(path: path)
}
do {
return try jsonData.decode()
} catch {
throw JSONError.other(error: error)
}
}
}

View File

@ -0,0 +1,27 @@
import Foundation
public protocol AnyEncoder {
func encode<T: Encodable>(_ value: T) throws -> Data
}
extension JSONEncoder: AnyEncoder {}
extension PropertyListEncoder: AnyEncoder {}
extension Encodable {
public func encode(using encoder: AnyEncoder = JSONEncoder()) throws -> Data {
return try encoder.encode(self)
}
public func toJSON() -> JSONObject? {
guard let data = try? encode() else { return nil }
return (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)).flatMap { $0 as? JSONObject }
}
public func toJSONString() -> String? {
guard let data = try? encode(),
let string = String(data: data, encoding: .utf8) else {
return nil
}
return string
}
}

View File

@ -28,7 +28,7 @@ extension Encodable {
public func toJSONString() -> String? {
guard let json = self.toJSON(),
let data = try? JSONSerialization.data(withJSONObject: json, options: .prettyPrinted),
let string = String(data: data, encoding: .utf8) else{
let string = String(data: data, encoding: .utf8) else {
return nil
}
return string

View File

@ -0,0 +1,5 @@
import Foundation
public protocol Holdable: Codable {
static var identifier: String { get set }
}

View File

@ -0,0 +1,83 @@
import Foundation
public struct HolderManager {
public enum Error: Swift.Error {
case codingKeyNotFound
case encoderError
case decoderError
case decoderErrorObjectNotPresent
case decoderErrorModelNotMapped
}
fileprivate static var types: [String: Holdable.Type] = [:]
public static func register<A: Holdable>(_ type: A.Type) {
types[A.identifier] = type
}
}
extension KeyedDecodingContainer where Key : CodingKey {
public enum TypeCodingKey: String, CodingKey {
case actionType
}
//MARK: - Decode
public func decode<T>(codingKey: KeyedDecodingContainer<K>.Key, typeCodingKey: TypeCodingKey) throws -> T {
guard let holdable: Holdable = try decodeIfPresent(codingKey: codingKey, typeCodingKey: typeCodingKey) else {
throw HolderManager.Error.decoderErrorObjectNotPresent
}
guard let h = holdable as? T else {
throw HolderManager.Error.decoderError
}
return h
}
public func decode(codingKey: KeyedDecodingContainer<K>.Key) throws -> ActionTypeProtocol {
return try decode(codingKey: codingKey, typeCodingKey: .actionType)
}
//MARK: - DecodeIfPresent
public func decodeIfPresent<T>(codingKey: KeyedDecodingContainer<K>.Key, typeCodingKey: TypeCodingKey) throws -> T? {
//get the type
let meta = try self.nestedContainer(keyedBy: TypeCodingKey.self, forKey: codingKey)
guard let type = try meta.decodeIfPresent(String.self, forKey: typeCodingKey) else {
return nil
}
//get the type
guard let found = HolderManager.types[type] else {
throw HolderManager.Error.decoderErrorModelNotMapped
}
//get the decoder for the propertyKey
let decoder = try self.superDecoder(forKey: codingKey)
//decode the type using the decoder
let holdable = try found.init(from: decoder)
guard let h = holdable as? T else {
throw HolderManager.Error.decoderError
}
return h
}
public func decodeIfPresent(codingKey: KeyedDecodingContainer<K>.Key) throws -> ActionTypeProtocol? {
return try decodeIfPresent(codingKey: codingKey, typeCodingKey: .actionType)
}
}
extension KeyedEncodingContainer where Key : CodingKey {
public mutating func encodeIfPresent(_ value: Holdable?, forKey key: KeyedEncodingContainer<K>.Key) throws {
if let v = value {
let encoder = self.superEncoder(forKey: key)
try v.encode(to: encoder)
}
}
public mutating func encode(_ value: Holdable, forKey key: KeyedEncodingContainer<K>.Key) throws {
let encoder = self.superEncoder(forKey: key)
try value.encode(to: encoder)
}
}

View File

@ -25,10 +25,6 @@ public struct ModelRegistry {
}
extension KeyedDecodingContainer where Key: CodingKey {
private enum TypeCodingKey: String, CodingKey {
case actionMap
}
//MARK: - Decode
public func decode<T, C:CodingKey>(codingKey: KeyedDecodingContainer<K>.Key, typeCodingKey: C) throws -> T {