Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Refactor to remove duplication #71

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions JSONCodable.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
BD885BBE1D17358E00CA767A /* EncodeNestingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD885BBD1D17358E00CA767A /* EncodeNestingTests.swift */; };
BD885BC01D173A0700CA767A /* PropertyItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD885BBF1D173A0700CA767A /* PropertyItem.swift */; };
BDD667CC1D1F3572003F94D7 /* Messages.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDD667CB1D1F3572003F94D7 /* Messages.swift */; };
C6FD31691DDDC31C00EF5141 /* DictionaryTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6FD31681DDDC31C00EF5141 /* DictionaryTest.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -80,6 +81,7 @@
BD885BBD1D17358E00CA767A /* EncodeNestingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EncodeNestingTests.swift; sourceTree = "<group>"; };
BD885BBF1D173A0700CA767A /* PropertyItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PropertyItem.swift; sourceTree = "<group>"; };
BDD667CB1D1F3572003F94D7 /* Messages.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Messages.swift; sourceTree = "<group>"; };
C6FD31681DDDC31C00EF5141 /* DictionaryTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DictionaryTest.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -138,6 +140,7 @@
9E455BF91BCE185B00070A4F /* EnumTests.swift */,
9ECF00BF1BCE251B008D557C /* TransformerTests.swift */,
A1B71C7D1D37E90B006DA33A /* MirrorTests.swift */,
C6FD31681DDDC31C00EF5141 /* DictionaryTest.swift */,
A10DFC4B1DF71BF400B7D6D7 /* ClassInheritanceTests.swift */,
);
name = Tests;
Expand Down Expand Up @@ -292,6 +295,7 @@
buildActionMask = 2147483647;
files = (
52E8F44F1C9087D200F40F7F /* UtilityTests.swift in Sources */,
C6FD31691DDDC31C00EF5141 /* DictionaryTest.swift in Sources */,
A1B71C801D37E982006DA33A /* ClassInheritance.swift in Sources */,
5211CD0A1CE2EBFB0097F255 /* NestItem.swift in Sources */,
A1B71C7E1D37E90B006DA33A /* MirrorTests.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO">
Expand Down
27 changes: 21 additions & 6 deletions JSONCodable/JSONCodable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,32 @@

// convenience protocol

public protocol JSONCodable: JSONEncodable, JSONDecodable {}
public protocol JSONCodable: JSONEncodable, JSONCompatible {}

// JSONCompatible - valid types in JSON

public protocol JSONCompatible: JSONEncodable {}

extension String: JSONCompatible {}
extension Double: JSONCompatible {}
extension Float: JSONCompatible {}
extension Bool: JSONCompatible {}
extension Int: JSONCompatible {}
extension String: JSONDecodable, JSONCompatible {
public init(object: JSONObject) throws {
fatalError()
}
}
extension Double: JSONDecodable, JSONCompatible {
public init(object: JSONObject) throws {
fatalError()
}
}
extension Bool: JSONDecodable, JSONCompatible {
public init(object: JSONObject) throws {
fatalError()
}
}
extension Int: JSONDecodable, JSONCompatible {
public init(object: JSONObject) throws {
fatalError()
}
}

extension JSONCompatible {
public func toJSON() throws -> Any {
Expand Down
239 changes: 63 additions & 176 deletions JSONCodable/JSONDecodable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ public enum JSONDecodableError: Error, CustomStringConvertible {

public protocol JSONDecodable {
init(object: JSONObject) throws
init(object: [JSONObject]) throws
}

public extension JSONDecodable {
Expand Down Expand Up @@ -87,7 +86,7 @@ public extension Array where Element: JSONDecodable {

// JSONDecoder - provides utility methods for decoding

public class JSONDecoder {
public final class JSONDecoder {
let object: JSONObject

public init(object: JSONObject) {
Expand Down Expand Up @@ -131,239 +130,127 @@ public class JSONDecoder {
}
return (result ?? object[key]).flatMap{$0 is NSNull ? nil : $0}
}

// JSONCompatible
public func decode<Compatible: JSONCompatible>(_ key: String) throws -> Compatible {
guard let value = get(key) else {
throw JSONDecodableError.missingTypeError(key: key)
}
guard let compatible = value as? Compatible else {
throw JSONDecodableError.incompatibleTypeError(key: key, elementType: type(of: value), expectedType: Compatible.self)
}
return compatible
}

// JSONCompatible?
public func decode<Compatible: JSONCompatible>(_ key: String) throws -> Compatible? {
return (get(key) ?? object[key] as Any) as? Compatible
}

// JSONDecodable
public func decode<Decodable: JSONDecodable>(_ key: String) throws -> Decodable {
guard let value = get(key) else {
throw JSONDecodableError.missingTypeError(key: key)
}
guard let object = value as? JSONObject else {
throw JSONDecodableError.dictionaryTypeExpectedError(key: key, elementType: type(of: value))
}
return try Decodable(object: object)
return try gettingTransforms(key: key, transform: Decodable.init)
}

// JSONDecodable?
public func decode<Decodable: JSONDecodable>(_ key: String) throws -> Decodable? {
guard let value = get(key) else {
return nil
}
guard let object = value as? JSONObject else {
throw JSONDecodableError.dictionaryTypeExpectedError(key: key, elementType: type(of: value))
}
return try Decodable(object: object)
return try gettingTransformsOptional(key: key, transform: Decodable.init)
}


// Enum
public func decode<Enum: RawRepresentable>(_ key: String) throws -> Enum {
guard let value = get(key) else {
throw JSONDecodableError.missingTypeError(key: key)
}
guard let raw = value as? Enum.RawValue else {
throw JSONDecodableError.incompatibleTypeError(key: key, elementType: type(of: value), expectedType: Enum.RawValue.self)
}
guard let result = Enum(rawValue: raw) else {
guard let result = try decode(key) as Enum? else {
throw JSONDecodableError.incompatibleTypeError(key: key, elementType: Enum.RawValue.self, expectedType: Enum.self)
}
return result
}

// Enum?
public func decode<Enum: RawRepresentable>(_ key: String) throws -> Enum? {
guard let value = get(key) else {
return nil
}
guard let raw = value as? Enum.RawValue else {
throw JSONDecodableError.incompatibleTypeError(key: key, elementType: type(of: value), expectedType: Enum.RawValue.self)
}
guard let result = Enum(rawValue: raw) else {
throw JSONDecodableError.incompatibleTypeError(key: key, elementType: Enum.RawValue.self, expectedType: Enum.self)
}
return result
}

// [JSONCompatible]
public func decode<Element: JSONCompatible>(_ key: String, filter: Bool = false) throws -> [Element] {
guard let value = get(key) else {
throw JSONDecodableError.missingTypeError(key: key)
}
guard let array = value as? [Element] else {
throw JSONDecodableError.incompatibleTypeError(key: key, elementType: type(of: value), expectedType: [Element].self)
}
return array
}

// [JSONCompatible]?
public func decode<Element: JSONCompatible>(_ key: String) throws -> [Element]? {
guard let value = get(key) else {
return nil
}
guard let array = value as? [Element] else {
throw JSONDecodableError.incompatibleTypeError(key: key, elementType: type(of: value), expectedType: [Element].self)
}
return array
return try gettingTransformsOptional(key: key, transform: Enum.init)
}

// [JSONDecodable]
public func decode<Element: JSONDecodable>(_ key: String, filter: Bool = false) throws -> [Element] {
guard let value = get(key) else {
throw JSONDecodableError.missingTypeError(key: key)
}
guard let array = value as? [JSONObject] else {
throw JSONDecodableError.arrayTypeExpectedError(key: key, elementType: type(of: value))
}
return try array.flatMap {
if filter {
return try? Element(object: $0)
} else {
return try Element(object: $0)
return try gettingTransforms(key: key, transform: { (preResult: [JSONObject]) in
return try preResult.flatMap {
if filter {
return try? Element(object: $0)
} else {
return try Element(object: $0)
}
}
}
})
}

// [JSONDecodable]?
public func decode<Element: JSONDecodable>(_ key: String, filter: Bool = false) throws -> [Element]? {
guard let value = get(key) else {
return nil
}
guard let array = value as? [JSONObject] else {
throw JSONDecodableError.arrayTypeExpectedError(key: key, elementType: type(of: value))
}
return try array.flatMap {
if filter {
return try? Element(object: $0)
} else {
return try Element(object: $0)
return try gettingTransformsOptional(key: key, transform: { (preResult: [JSONObject]) in
return try preResult.flatMap {
if filter {
return try? Element(object: $0)
} else {
return try Element(object: $0)
}
}
}
})
}

// [[JSONDecodable]]
public func decode<Element: JSONDecodable>(_ key: String, filter: Bool = false) throws -> [[Element]] {
guard let value = get(key) else {
throw JSONDecodableError.missingTypeError(key: key)
}
guard let array = value as? [[JSONObject]] else {
throw JSONDecodableError.arrayTypeExpectedError(key: key, elementType: type(of: value))
}
var res:[[Element]] = []

for x in array {
if filter {
let nested = x.flatMap { try? Element(object: $0)}
res.append(nested)
} else {
let nested = try x.flatMap { try Element(object: $0)}
res.append(nested)
}
}
return res
}

// [[JSONCompatible]]
public func decode<Element: JSONCompatible>(_ key: String) throws -> [[Element]] {
guard let value = get(key) else {
throw JSONDecodableError.missingTypeError(key: key)
}
guard let array = value as? [[Element]] else {
throw JSONDecodableError.incompatibleTypeError(key: key, elementType: type(of: value), expectedType: [Element].self)
}
var res:[[Element]] = []

for x in array {
res.append(x)
}
return res
return try gettingTransforms(key: key, transform: { (preResult: [[JSONObject]]) in
return try preResult.map({ x in
if filter {
return x.flatMap { try? Element(object: $0)}
} else {
return try x.flatMap { try Element(object: $0)}
}
})
})
}

// [Enum]
public func decode<Enum: RawRepresentable>(_ key: String) throws -> [Enum] {
guard let value = get(key) else {
throw JSONDecodableError.missingTypeError(key: key)
}
guard let array = value as? [Enum.RawValue] else {
throw JSONDecodableError.arrayTypeExpectedError(key: key, elementType: type(of: value))
}
return array.flatMap { Enum(rawValue: $0) }
return try gettingTransforms(key: key, transform: { (preResult: [Enum.RawValue]) in
return preResult.map { Enum(rawValue: $0)! }
})
}

// [Enum]?
public func decode<Enum: RawRepresentable>(_ key: String) throws -> [Enum]? {
guard let value = get(key) else {
return nil
}
guard let array = value as? [Enum.RawValue] else {
throw JSONDecodableError.arrayTypeExpectedError(key: key, elementType: type(of: value))
}
return array.flatMap { Enum(rawValue: $0) }
return try gettingTransformsOptional(key: key, transform: { (preResult: [Enum.RawValue]) in
return preResult.flatMap { Enum(rawValue: $0) }
})
}

// [String:JSONCompatible]
public func decode<Value: JSONCompatible>(_ key: String) throws -> [String: Value] {
guard let value = get(key) else {

private func gettingTransforms<T, PreResult>(key: String, transform: (PreResult) throws -> T) throws -> T {
guard let value = try gettingTransformsOptional(key: key, transform: transform) else {
throw JSONDecodableError.missingTypeError(key: key)
}
guard let dictionary = value as? [String: Value] else {
throw JSONDecodableError.incompatibleTypeError(key: key, elementType: type(of: value), expectedType: [String: Value].self)
}
return dictionary

return value
}

// [String:JSONCompatible]?
public func decode<Value: JSONCompatible>(_ key: String) throws -> [String: Value]? {
private func gettingTransformsOptional<T, PreResult>(key: String, transform: (PreResult) throws -> T?) throws -> T? {
guard let value = get(key) else {
return nil
}
guard let dictionary = value as? [String: Value] else {
throw JSONDecodableError.incompatibleTypeError(key: key, elementType: type(of: value), expectedType: [String: Value].self)
if let t = value as? T {
return t
}
return dictionary
guard let preResult = value as? PreResult else {
throw JSONDecodableError.incompatibleTypeError(key: key, elementType: type(of: value), expectedType: PreResult.self)
}
return try transform(preResult)
}

// [String:JSONDecodable]
public func decode<Element: JSONDecodable>(_ key: String) throws -> [String: Element] {
guard let value = get(key) else {
throw JSONDecodableError.missingTypeError(key: key)
}
guard let dictionary = value as? [String: JSONObject] else {
throw JSONDecodableError.incompatibleTypeError(key: key, elementType: type(of: value), expectedType: [String: Element].self)
}
var decoded = [String: Element]()
try dictionary.forEach {
decoded[$0] = try Element(object: $1)
}
return decoded
return try gettingTransforms(key: key, transform: { (preResult: [String: JSONObject]) in
var decoded = [String: Element]()
try preResult.forEach {
decoded[$0] = try Element(object: $1)
}
return decoded
})
}

// [String:JSONDecodable]?
public func decode<Element: JSONDecodable>(_ key: String) throws -> [String: Element]? {
guard let value = get(key) else {
return nil
}
guard let dictionary = value as? [String: JSONObject] else {
throw JSONDecodableError.incompatibleTypeError(key: key, elementType: type(of: value), expectedType: [String: Element].self)
}
var decoded = [String: Element]()
try dictionary.forEach {
decoded[$0] = try Element(object: $1)
}
return decoded
return try gettingTransformsOptional(key: key, transform: { (preResult: [String: JSONObject]) in
var decoded = [String: Element]()
try preResult.forEach {
decoded[$0] = try Element(object: $1)
}
return decoded
})
}

// JSONTransformable
Expand Down
Loading