Skip to content
Permalink
Browse files

Add modules Decode, FromJson, and JsonValue

This is an already working version of the source code. I'd like to have
started this yesterday and have an history how I got into this cleaner
and sorted out version of the design and implementation. We will have
that from now on.
  • Loading branch information...
NunoAlexandre committed Feb 2, 2019
1 parent 011c058 commit d58f9bc980ad7905fd5ecbd63748835a7823a9c5
Binary file not shown.
@@ -10,6 +10,12 @@
BC2F4CA1220620C60019095B /* Stork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC2F4C97220620C60019095B /* Stork.framework */; };
BC2F4CA6220620C60019095B /* StorkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC2F4CA5220620C60019095B /* StorkTests.swift */; };
BC2F4CA8220620C60019095B /* Stork.h in Headers */ = {isa = PBXBuildFile; fileRef = BC2F4C9A220620C60019095B /* Stork.h */; settings = {ATTRIBUTES = (Public, ); }; };
BC6F1FC92206236D00254714 /* JsonValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC6F1FC52206236C00254714 /* JsonValue.swift */; };
BC6F1FCA2206236D00254714 /* JsonValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC6F1FC52206236C00254714 /* JsonValue.swift */; };
BC6F1FCD2206236D00254714 /* FromJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC6F1FC72206236C00254714 /* FromJson.swift */; };
BC6F1FCE2206236D00254714 /* FromJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC6F1FC72206236C00254714 /* FromJson.swift */; };
BC6F1FCF2206236D00254714 /* Decode.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC6F1FC82206236D00254714 /* Decode.swift */; };
BC6F1FD02206236D00254714 /* Decode.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC6F1FC82206236D00254714 /* Decode.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
@@ -29,6 +35,9 @@
BC2F4CA0220620C60019095B /* StorkTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StorkTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
BC2F4CA5220620C60019095B /* StorkTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorkTests.swift; sourceTree = "<group>"; };
BC2F4CA7220620C60019095B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
BC6F1FC52206236C00254714 /* JsonValue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JsonValue.swift; sourceTree = "<group>"; };
BC6F1FC72206236C00254714 /* FromJson.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FromJson.swift; sourceTree = "<group>"; };
BC6F1FC82206236D00254714 /* Decode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Decode.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
@@ -90,6 +99,9 @@
BC6F1FC42206226400254714 /* Source */ = {
isa = PBXGroup;
children = (
BC6F1FC82206236D00254714 /* Decode.swift */,
BC6F1FC72206236C00254714 /* FromJson.swift */,
BC6F1FC52206236C00254714 /* JsonValue.swift */,
);
path = Source;
sourceTree = "<group>";
@@ -156,6 +168,7 @@
TargetAttributes = {
BC2F4C96220620C60019095B = {
CreatedOnToolsVersion = 10.1;
LastSwiftMigration = 1010;
};
BC2F4C9F220620C60019095B = {
CreatedOnToolsVersion = 10.1;
@@ -202,14 +215,20 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
BC6F1FC92206236D00254714 /* JsonValue.swift in Sources */,
BC6F1FCD2206236D00254714 /* FromJson.swift in Sources */,
BC6F1FCF2206236D00254714 /* Decode.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
BC2F4C9C220620C60019095B /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
BC6F1FD02206236D00254714 /* Decode.swift in Sources */,
BC2F4CA6220620C60019095B /* StorkTests.swift in Sources */,
BC6F1FCA2206236D00254714 /* JsonValue.swift in Sources */,
BC6F1FCE2206236D00254714 /* FromJson.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -349,6 +368,7 @@
BC2F4CAC220620C60019095B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic;
DEFINES_MODULE = YES;
@@ -366,6 +386,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.nunoalexandre.Stork;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.2;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -374,6 +395,7 @@
BC2F4CAD220620C60019095B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic;
DEFINES_MODULE = YES;
@@ -399,6 +421,7 @@
BC2F4CAF220620C60019095B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = NR4448Q277;
INFOPLIST_FILE = StorkTests/Info.plist;
@@ -417,6 +440,7 @@
BC2F4CB0220620C60019095B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = NR4448Q277;
INFOPLIST_FILE = StorkTests/Info.plist;
@@ -0,0 +1,49 @@
/*
Stork > Decode
This module offers tools to:
- decode a value of type T from 'Any' [Top-Level]
- decode values of type T from a JSON object [Nested]
Such type 'T' is required, at compile time, to comply
to the 'FromJson' protocol.
*/

infix operator .? // Maybe get a scalar or custom type
infix operator .! // Same as .? but expects the value to be present
infix operator ..? // Maybe get an array of scalar or custom types
infix operator ..! // Same as ..? but expects the array to be present
func .?<T>(json: JSON, key: String) -> T? where T: FromJson {
return json[key].flatMap(decodeValue)
}

func .!<T>(json: JSON, key: String) -> T where T: FromJson {
return (json .? key)!
}

func ..?<T>(json: JSON, key: String) -> [T]? where T: FromJson {
return json[key]
.map { value in
(value as? Array<Any>)?.compactMap(decodeValue) ?? []
}
}

func ..!<T>(json: JSON, key: String) -> [T] where T: FromJson {
return (json ..? key)!
}

func decodeValue<T>(_ value: Any) -> T? where T: FromJson {
switch value {
case is Bool:
return T.from(value: .boolean(value as! Bool))
case is Int:
return T.from(value: .int(value as! Int))
case is String:
return T.from(value: .string(value as! String))
case is JSON:
return T.from(value: .object(value as! JSON))
default:
return nil
}
}
@@ -0,0 +1,42 @@
/*
Stork > FromJson
The 'FromJson' protocol requires a complying type to
offer a way of maybe being built from a 'JsonValue'.
*/
protocol FromJson {
static func from(value: JsonValue) -> Self?
}

extension FromJson {
static func from(json: JSON) -> Self? {
return Self.from(value: .object(json))
}
}

extension String: FromJson {
static func from(value: JsonValue) -> String? {
return value.stringValue()
}
}

extension Bool: FromJson {
static func from(value: JsonValue) -> Bool? {
return value.boolValue()
}
}

extension Int: FromJson {
static func from(value: JsonValue) -> Int? {
return value.intValue()
}
}

extension Array where Element: FromJson {
static func from(jsonList: [JSON]) -> [Element] {
return jsonList.compactMap(Element.from(json:))
}
}


typealias JSON = [String: Any]
@@ -0,0 +1,67 @@
/*
Stork > JsonValue
A 'JsonValue' is a type-safe capture of what any value in
a JSON object can be, following to the JSON specification.
*/
enum JsonValue {
case boolean(Bool)
case int(Int)
case double(Double)
case string(String)
case object(JSON)
case list([JsonValue])

func boolValue() -> Bool? {
return self.ifBool(id)
}

func intValue() -> Int? {
return self.ifInt(id)
}

func stringValue() -> String? {
return self.ifString(id)
}

func ifBool<T>(_ apply: (Bool) throws -> T) -> T? {
switch self {
case .boolean(let bool):
return try? apply(bool)
default:
return nil
}
}

func ifInt<T>(_ apply: (Int) throws -> T) -> T? {
switch self {
case .int(let int):
return try? apply(int)
default:
return nil
}
}

func ifObject<T>(_ apply: (JSON) throws -> T) -> T? {
switch self {
case .object(let json):
return try? apply(json)
default:
return nil
}
}

func ifString<T>(_ apply: (String) throws -> T) -> T? {
switch self {
case .string(let str):
return try? apply(str)
default:
return nil
}
}
}

func id<T>(_ value: T) -> T {
return value
}

0 comments on commit d58f9bc

Please sign in to comment.
You can’t perform that action at this time.