From 5477e9a4509b82e00be645465bbda5201bc4bc32 Mon Sep 17 00:00:00 2001 From: Matthew Cheok Date: Sun, 19 Jun 2016 11:54:28 -0700 Subject: [PATCH 1/4] Updated for Swift 3 --- JSONCodable.xcodeproj/project.pbxproj | 38 +- .../xcschemes/JSONCodable OSX.xcscheme | 2 +- .../xcschemes/JSONCodable iOS.xcscheme | 2 +- JSONCodable/JSONCodable.swift | 12 +- JSONCodable/JSONDecodable.swift | 164 +++--- JSONCodable/JSONEncodable.swift | 494 +++++++++--------- JSONCodable/JSONHelpers.swift | 50 +- JSONCodable/JSONString.swift | 148 +++--- JSONCodable/JSONTransformer.swift | 56 +- JSONCodableTests/Company.swift | 18 +- JSONCodableTests/EnumTests.swift | 74 +-- JSONCodableTests/Food.swift | 52 +- JSONCodableTests/Fruit.swift | 36 +- JSONCodableTests/HelperTests.swift | 62 +-- JSONCodableTests/ImageAsset.swift | 30 +- JSONCodableTests/NestItem.swift | 31 +- JSONCodableTests/RegularTests.swift | 198 +++---- JSONCodableTests/TransformerTests.swift | 50 +- JSONCodableTests/User.swift | 64 +-- JSONCodableTests/UtilityTests.swift | 30 +- 20 files changed, 797 insertions(+), 814 deletions(-) diff --git a/JSONCodable.xcodeproj/project.pbxproj b/JSONCodable.xcodeproj/project.pbxproj index f982c81..9e294c7 100644 --- a/JSONCodable.xcodeproj/project.pbxproj +++ b/JSONCodable.xcodeproj/project.pbxproj @@ -256,7 +256,7 @@ 9EDF80101B59CFCE00E4A2D6 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0710; + LastUpgradeCheck = 0800; TargetAttributes = { 9E455BF61BCE185B00070A4F = { CreatedOnToolsVersion = 7.0.1; @@ -266,6 +266,7 @@ }; 9EDB39221B59D01D00C63019 = { CreatedOnToolsVersion = 7.0; + LastSwiftMigration = 0800; }; }; }; @@ -453,6 +454,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.matthewcheok.JSONCodableTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; }; name = Release; }; @@ -558,6 +560,7 @@ PRODUCT_NAME = JSONCodable; SDKROOT = iphoneos; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; @@ -618,6 +621,7 @@ SDKROOT = macosx; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -668,6 +672,8 @@ PRODUCT_NAME = JSONCodable; SDKROOT = macosx; SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 3.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -676,7 +682,22 @@ 9EDF80141B59CFCE00E4A2D6 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MACOSX_DEPLOYMENT_TARGET = 10.10; ONLY_ACTIVE_ARCH = YES; @@ -686,6 +707,21 @@ 9EDF80151B59CFCE00E4A2D6 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MACOSX_DEPLOYMENT_TARGET = 10.10; }; diff --git a/JSONCodable.xcodeproj/xcshareddata/xcschemes/JSONCodable OSX.xcscheme b/JSONCodable.xcodeproj/xcshareddata/xcschemes/JSONCodable OSX.xcscheme index 9d9d6a7..6156403 100644 --- a/JSONCodable.xcodeproj/xcshareddata/xcschemes/JSONCodable OSX.xcscheme +++ b/JSONCodable.xcodeproj/xcshareddata/xcschemes/JSONCodable OSX.xcscheme @@ -1,6 +1,6 @@ AnyObject { - return self as! AnyObject - } + public func toJSON() throws -> AnyObject { + return self as! AnyObject + } } - -// Swift 2 Shims - -#if !swift(>=3.0) - typealias ErrorProtocol = ErrorType -#endif \ No newline at end of file diff --git a/JSONCodable/JSONDecodable.swift b/JSONCodable/JSONDecodable.swift index 1cb1c30..4ef39fe 100644 --- a/JSONCodable/JSONDecodable.swift +++ b/JSONCodable/JSONDecodable.swift @@ -9,37 +9,37 @@ // Decoding Errors public enum JSONDecodableError: ErrorProtocol, CustomStringConvertible { - case MissingTypeError( + case missingTypeError( key: String ) - case IncompatibleTypeError( + case incompatibleTypeError( key: String, elementType: Any.Type, expectedType: Any.Type ) - case ArrayTypeExpectedError( + case arrayTypeExpectedError( key: String, elementType: Any.Type ) - case DictionaryTypeExpectedError( + case dictionaryTypeExpectedError( key: String, elementType: Any.Type ) - case TransformerFailedError( + case transformerFailedError( key: String ) public var description: String { switch self { - case let .MissingTypeError(key: key): + case let .missingTypeError(key: key): return "JSONDecodableError: Missing value for key \(key)" - case let .IncompatibleTypeError(key: key, elementType: elementType, expectedType: expectedType): + case let .incompatibleTypeError(key: key, elementType: elementType, expectedType: expectedType): return "JSONDecodableError: Incompatible type for key \(key); Got \(elementType) instead of \(expectedType)" - case let .ArrayTypeExpectedError(key: key, elementType: elementType): + case let .arrayTypeExpectedError(key: key, elementType: elementType): return "JSONDecodableError: Got \(elementType) instead of an array for key \(key)" - case let .DictionaryTypeExpectedError(key: key, elementType: elementType): + case let .dictionaryTypeExpectedError(key: key, elementType: elementType): return "JSONDecodableError: Got \(elementType) instead of a dictionary for key \(key)" - case let .TransformerFailedError(key: key): + case let .transformerFailedError(key: key): return "JSONDecodableError: Transformer failed for key \(key)" } } @@ -65,7 +65,7 @@ public extension Array where Element: JSONDecodable { init(JSONArray: [AnyObject]) throws { self.init(try JSONArray.flatMap { guard let json = $0 as? [String : AnyObject] else { - throw JSONDecodableError.DictionaryTypeExpectedError(key: "n/a", elementType: $0.dynamicType) + throw JSONDecodableError.dictionaryTypeExpectedError(key: "n/a", elementType: $0.dynamicType) } return try Element(object: json) }) @@ -83,7 +83,7 @@ public class JSONDecoder { /// Get index from `"[0]"` formatted `String` /// returns `nil` if invalid format (i.e. no brackets or contents not an `Int`) - internal func parseArrayIndex(key:String) -> Int? { + internal func parseArrayIndex(_ key:String) -> Int? { var chars = key.characters let first = chars.popFirst() let last = chars.popLast() @@ -94,174 +94,168 @@ public class JSONDecoder { } } - private func get(key: String) -> AnyObject? { - #if !swift(>=3.0) - let keys = key.stringByReplacingOccurrencesOfString("[", withString: ".[") - .componentsSeparatedByString(".") - #else - let keys = key.replacingOccurrences(of: "[", with: ".[").componentsSeparated(by: ".") - #endif - + private func get(_ key: String) -> AnyObject? { + let keys = key.replacingOccurrences(of: "[", with: ".[").components(separatedBy: ".") let result = keys.reduce(object as AnyObject?) { value, key in switch value { - case let dict as [String: AnyObject]: - return dict[key] - - case let arr as [AnyObject]: - guard let index = parseArrayIndex(key) else { - return nil - } - guard (0..(key: String) throws -> Compatible { + public func decode(_ key: String) throws -> Compatible { guard let value = get(key) else { - throw JSONDecodableError.MissingTypeError(key: key) + throw JSONDecodableError.missingTypeError(key: key) } guard let compatible = value as? Compatible else { - throw JSONDecodableError.IncompatibleTypeError(key: key, elementType: value.dynamicType, expectedType: Compatible.self) + throw JSONDecodableError.incompatibleTypeError(key: key, elementType: value.dynamicType, expectedType: Compatible.self) } return compatible } // JSONCompatible? - public func decode(key: String) throws -> Compatible? { + public func decode(_ key: String) throws -> Compatible? { return (get(key) ?? object[key]) as? Compatible } // JSONDecodable - public func decode(key: String) throws -> Decodable { + public func decode(_ key: String) throws -> Decodable { guard let value = get(key) else { - throw JSONDecodableError.MissingTypeError(key: key) + throw JSONDecodableError.missingTypeError(key: key) } guard let object = value as? JSONObject else { - throw JSONDecodableError.DictionaryTypeExpectedError(key: key, elementType: value.dynamicType) + throw JSONDecodableError.dictionaryTypeExpectedError(key: key, elementType: value.dynamicType) } return try Decodable(object: object) } // JSONDecodable? - public func decode(key: String) throws -> Decodable? { + public func decode(_ 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: value.dynamicType) + throw JSONDecodableError.dictionaryTypeExpectedError(key: key, elementType: value.dynamicType) } return try Decodable(object: object) } // Enum - public func decode(key: String) throws -> Enum { + public func decode(_ key: String) throws -> Enum { guard let value = get(key) else { - throw JSONDecodableError.MissingTypeError(key: key) + throw JSONDecodableError.missingTypeError(key: key) } guard let raw = value as? Enum.RawValue else { - throw JSONDecodableError.IncompatibleTypeError(key: key, elementType: value.dynamicType, expectedType: Enum.RawValue.self) + throw JSONDecodableError.incompatibleTypeError(key: key, elementType: value.dynamicType, expectedType: Enum.RawValue.self) } guard let result = Enum(rawValue: raw) else { - throw JSONDecodableError.IncompatibleTypeError(key: key, elementType: Enum.RawValue.self, expectedType: Enum.self) + throw JSONDecodableError.incompatibleTypeError(key: key, elementType: Enum.RawValue.self, expectedType: Enum.self) } return result } // Enum? - public func decode(key: String) throws -> Enum? { + public func decode(_ 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: value.dynamicType, expectedType: Enum.RawValue.self) + throw JSONDecodableError.incompatibleTypeError(key: key, elementType: value.dynamicType, expectedType: Enum.RawValue.self) } guard let result = Enum(rawValue: raw) else { - throw JSONDecodableError.IncompatibleTypeError(key: key, elementType: Enum.RawValue.self, expectedType: Enum.self) + throw JSONDecodableError.incompatibleTypeError(key: key, elementType: Enum.RawValue.self, expectedType: Enum.self) } return result } // [JSONCompatible] - public func decode(key: String) throws -> [Element] { + public func decode(_ key: String) throws -> [Element] { guard let value = get(key) else { return [] } guard let array = value as? [Element] else { - throw JSONDecodableError.IncompatibleTypeError(key: key, elementType: value.dynamicType, expectedType: [Element].self) + throw JSONDecodableError.incompatibleTypeError(key: key, elementType: value.dynamicType, expectedType: [Element].self) } return array } // [JSONCompatible]? - public func decode(key: String) throws -> [Element]? { + public func decode(_ 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: value.dynamicType, expectedType: [Element].self) + throw JSONDecodableError.incompatibleTypeError(key: key, elementType: value.dynamicType, expectedType: [Element].self) } return array } // [JSONDecodable] - public func decode(key: String) throws -> [Element] { + public func decode(_ key: String) throws -> [Element] { guard let value = get(key) else { return [] } guard let array = value as? [JSONObject] else { - throw JSONDecodableError.ArrayTypeExpectedError(key: key, elementType: value.dynamicType) + throw JSONDecodableError.arrayTypeExpectedError(key: key, elementType: value.dynamicType) } return try array.flatMap { try Element(object: $0)} } // [JSONDecodable]? - public func decode(key: String) throws -> [Element]? { + public func decode(_ key: String) throws -> [Element]? { guard let value = get(key) else { return nil } guard let array = value as? [JSONObject] else { - throw JSONDecodableError.ArrayTypeExpectedError(key: key, elementType: value.dynamicType) + throw JSONDecodableError.arrayTypeExpectedError(key: key, elementType: value.dynamicType) } return try array.flatMap { try Element(object: $0)} } - + // [[JSONDecodable]] - public func decode(key: String) throws -> [[Element]] { + public func decode(_ key: String) throws -> [[Element]] { guard let value = get(key) else { return [] } guard let array = value as? [[JSONObject]] else { - throw JSONDecodableError.ArrayTypeExpectedError(key: key, elementType: value.dynamicType) + throw JSONDecodableError.arrayTypeExpectedError(key: key, elementType: value.dynamicType) } var res:[[Element]] = [] - + for x in array { let nested = try x.flatMap { try Element(object: $0)} res.append(nested) } return res } - + // [[JSONCompatible]] - public func decode(key: String) throws -> [[Element]] { + public func decode(_ key: String) throws -> [[Element]] { guard let value = get(key) else { return [] } guard let array = value as? [[Element]] else { - throw JSONDecodableError.IncompatibleTypeError(key: key, elementType: value.dynamicType, expectedType: [Element].self) + throw JSONDecodableError.incompatibleTypeError(key: key, elementType: value.dynamicType, expectedType: [Element].self) } var res:[[Element]] = [] - + for x in array { res.append(x) } @@ -269,56 +263,56 @@ public class JSONDecoder { } // [Enum] - public func decode(key: String) throws -> [Enum] { + public func decode(_ key: String) throws -> [Enum] { guard let value = get(key) else { return [] } guard let array = value as? [Enum.RawValue] else { - throw JSONDecodableError.ArrayTypeExpectedError(key: key, elementType: value.dynamicType) + throw JSONDecodableError.arrayTypeExpectedError(key: key, elementType: value.dynamicType) } return array.flatMap { Enum(rawValue: $0) } } // [Enum]? - public func decode(key: String) throws -> [Enum]? { + public func decode(_ 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: value.dynamicType) + throw JSONDecodableError.arrayTypeExpectedError(key: key, elementType: value.dynamicType) } return array.flatMap { Enum(rawValue: $0) } } // [String:JSONCompatible] - public func decode(key: String) throws -> [String: Value] { + public func decode(_ key: String) throws -> [String: Value] { guard let value = get(key) else { - throw JSONDecodableError.MissingTypeError(key: key) + throw JSONDecodableError.missingTypeError(key: key) } guard let dictionary = value as? [String: Value] else { - throw JSONDecodableError.IncompatibleTypeError(key: key, elementType: value.dynamicType, expectedType: [String: Value].self) + throw JSONDecodableError.incompatibleTypeError(key: key, elementType: value.dynamicType, expectedType: [String: Value].self) } return dictionary } // [String:JSONCompatible]? - public func decode(key: String) throws -> [String: Value]? { + public func decode(_ key: String) throws -> [String: Value]? { guard let value = get(key) else { return nil } guard let dictionary = value as? [String: Value] else { - throw JSONDecodableError.IncompatibleTypeError(key: key, elementType: value.dynamicType, expectedType: [String: Value].self) + throw JSONDecodableError.incompatibleTypeError(key: key, elementType: value.dynamicType, expectedType: [String: Value].self) } return dictionary } // [String:JSONDecodable] - public func decode(key: String) throws -> [String: Element] { + public func decode(_ key: String) throws -> [String: Element] { guard let value = get(key) else { - throw JSONDecodableError.MissingTypeError(key: key) + throw JSONDecodableError.missingTypeError(key: key) } guard let dictionary = value as? [String: JSONObject] else { - throw JSONDecodableError.IncompatibleTypeError(key: key, elementType: value.dynamicType, expectedType: [String: Element].self) + throw JSONDecodableError.incompatibleTypeError(key: key, elementType: value.dynamicType, expectedType: [String: Element].self) } var decoded = [String: Element]() try dictionary.forEach { @@ -328,12 +322,12 @@ public class JSONDecoder { } // [String:JSONDecodable]? - public func decode(key: String) throws -> [String: Element]? { + public func decode(_ 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: value.dynamicType, expectedType: [String: Element].self) + throw JSONDecodableError.incompatibleTypeError(key: key, elementType: value.dynamicType, expectedType: [String: Element].self) } var decoded = [String: Element]() try dictionary.forEach { @@ -343,29 +337,29 @@ public class JSONDecoder { } // JSONTransformable - public func decode(key: String, transformer: JSONTransformer) throws -> DecodedType { + public func decode(_ key: String, transformer: JSONTransformer) throws -> DecodedType { guard let value = get(key) else { - throw JSONDecodableError.MissingTypeError(key: key) + throw JSONDecodableError.missingTypeError(key: key) } guard let actual = value as? EncodedType else { - throw JSONDecodableError.IncompatibleTypeError(key: key, elementType: value.dynamicType, expectedType: EncodedType.self) + throw JSONDecodableError.incompatibleTypeError(key: key, elementType: value.dynamicType, expectedType: EncodedType.self) } guard let result = transformer.decoding(actual) else { - throw JSONDecodableError.TransformerFailedError(key: key) + throw JSONDecodableError.transformerFailedError(key: key) } return result } // JSONTransformable? - public func decode(key: String, transformer: JSONTransformer) throws -> DecodedType? { + public func decode(_ key: String, transformer: JSONTransformer) throws -> DecodedType? { guard let value = get(key) else { return nil } guard let actual = value as? EncodedType else { - throw JSONDecodableError.IncompatibleTypeError(key: key, elementType: value.dynamicType, expectedType: EncodedType.self) + throw JSONDecodableError.incompatibleTypeError(key: key, elementType: value.dynamicType, expectedType: EncodedType.self) } guard let result = transformer.decoding(actual) else { - throw JSONDecodableError.TransformerFailedError(key: key) + throw JSONDecodableError.transformerFailedError(key: key) } return result } diff --git a/JSONCodable/JSONEncodable.swift b/JSONCodable/JSONEncodable.swift index 5ddf955..8298542 100644 --- a/JSONCodable/JSONEncodable.swift +++ b/JSONCodable/JSONEncodable.swift @@ -9,300 +9,294 @@ // Encoding Errors public enum JSONEncodableError: ErrorProtocol, CustomStringConvertible { - case IncompatibleTypeError( - elementType: Any.Type - ) - case ArrayIncompatibleTypeError( - elementType: Any.Type - ) - case DictionaryIncompatibleTypeError( - elementType: Any.Type - ) - case ChildIncompatibleTypeError( - key: String, - elementType: Any.Type - ) - case TransformerFailedError( - key: String - ) - - public var description: String { - switch self { - case let .IncompatibleTypeError(elementType: elementType): - return "JSONEncodableError: Incompatible type \(elementType)" - case let .ArrayIncompatibleTypeError(elementType: elementType): - return "JSONEncodableError: Got an array of incompatible type \(elementType)" - case let .DictionaryIncompatibleTypeError(elementType: elementType): - return "JSONEncodableError: Got an dictionary of incompatible type \(elementType)" - case let .ChildIncompatibleTypeError(key: key, elementType: elementType): - return "JSONEncodableError: Got incompatible type \(elementType) for key \(key)" - case let .TransformerFailedError(key: key): - return "JSONEncodableError: Transformer failed for key \(key)" - } + case incompatibleTypeError( + elementType: Any.Type + ) + case arrayIncompatibleTypeError( + elementType: Any.Type + ) + case dictionaryIncompatibleTypeError( + elementType: Any.Type + ) + case childIncompatibleTypeError( + key: String, + elementType: Any.Type + ) + case transformerFailedError( + key: String + ) + + public var description: String { + switch self { + case let .incompatibleTypeError(elementType: elementType): + return "JSONEncodableError: Incompatible type \(elementType)" + case let .arrayIncompatibleTypeError(elementType: elementType): + return "JSONEncodableError: Got an array of incompatible type \(elementType)" + case let .dictionaryIncompatibleTypeError(elementType: elementType): + return "JSONEncodableError: Got an dictionary of incompatible type \(elementType)" + case let .childIncompatibleTypeError(key: key, elementType: elementType): + return "JSONEncodableError: Got incompatible type \(elementType) for key \(key)" + case let .transformerFailedError(key: key): + return "JSONEncodableError: Transformer failed for key \(key)" } + } } // Struct -> Dictionary public protocol JSONEncodable { - func toJSON() throws -> AnyObject + func toJSON() throws -> AnyObject } public extension JSONEncodable { - func toJSON() throws -> AnyObject { - let mirror = Mirror(reflecting: self) + func toJSON() throws -> AnyObject { + let mirror = Mirror(reflecting: self) + + guard let style = mirror.displayStyle where style == .`struct` || style == .`class` else { + throw JSONEncodableError.incompatibleTypeError(elementType: self.dynamicType) + } + + return try JSONEncoder.create({ (encoder) -> Void in + // loop through all properties (instance variables) + for (labelMaybe, valueMaybe) in mirror.children { + guard let label = labelMaybe else { + continue + } + + let value: Any - #if !swift(>=3.0) - guard let style = mirror.displayStyle where style == .Struct || style == .Class else { - throw JSONEncodableError.IncompatibleTypeError(elementType: self.dynamicType) + // unwrap optionals + if let v = valueMaybe as? JSONOptional { + guard let unwrapped = v.wrapped else { + continue + } + value = unwrapped } - #else - guard let style = mirror.displayStyle where style == .`struct` || style == .`class` else { - throw JSONEncodableError.IncompatibleTypeError(elementType: self.dynamicType) + else { + value = valueMaybe } - #endif - return try JSONEncoder.create({ (encoder) -> Void in - // loop through all properties (instance variables) - for (labelMaybe, valueMaybe) in mirror.children { - guard let label = labelMaybe else { - continue - } - - let value: Any - - // unwrap optionals - if let v = valueMaybe as? JSONOptional { - guard let unwrapped = v.wrapped else { - continue - } - value = unwrapped - } - else { - value = valueMaybe - } - - switch (value) { - case let value as JSONEncodable: - try encoder.encode(value, key: label) - case let value as JSONArray: - try encoder.encode(value, key: label) - case let value as JSONDictionary: - try encoder.encode(value, key: label) - default: - throw JSONEncodableError.ChildIncompatibleTypeError(key: label, elementType: value.dynamicType) - } - } - }) - } + switch (value) { + case let value as JSONEncodable: + try encoder.encode(value, key: label) + case let value as JSONArray: + try encoder.encode(value, key: label) + case let value as JSONDictionary: + try encoder.encode(value, key: label) + default: + throw JSONEncodableError.childIncompatibleTypeError(key: label, elementType: value.dynamicType) + } + } + }) + } } public extension Array {//where Element: JSONEncodable { - private var wrapped: [Any] { return self.map{$0} } - - public func toJSON() throws -> AnyObject { - var results: [AnyObject] = [] - for item in self.wrapped { - if let item = item as? JSONEncodable { - results.append(try item.toJSON()) - } - else { - throw JSONEncodableError.ArrayIncompatibleTypeError(elementType: item.dynamicType) - } - } - return results + private var wrapped: [Any] { return self.map{$0} } + + public func toJSON() throws -> AnyObject { + var results: [AnyObject] = [] + for item in self.wrapped { + if let item = item as? JSONEncodable { + results.append(try item.toJSON()) + } + else { + throw JSONEncodableError.arrayIncompatibleTypeError(elementType: item.dynamicType) + } } + return results + } } // Dictionary convenience methods public extension Dictionary {//where Key: String, Value: JSONEncodable { - public func toJSON() throws -> AnyObject { - var result: [String: AnyObject] = [:] - for (k, item) in self { - if let item = item as? JSONEncodable { - result[String(k)] = try item.toJSON() - } - else { - throw JSONEncodableError.DictionaryIncompatibleTypeError(elementType: item.dynamicType) - } - } - return result + public func toJSON() throws -> AnyObject { + var result: [String: AnyObject] = [:] + for (k, item) in self { + if let item = item as? JSONEncodable { + result[String(k)] = try item.toJSON() + } + else { + throw JSONEncodableError.dictionaryIncompatibleTypeError(elementType: item.dynamicType) + } } + return result + } } // JSONEncoder - provides utility methods for encoding public class JSONEncoder { - var object = JSONObject() - - public static func create(@noescape setup: (encoder: JSONEncoder) throws -> Void) rethrows -> JSONObject { - let encoder = JSONEncoder() - try setup(encoder: encoder) - return encoder.object + var object = JSONObject() + + public static func create(_ setup: @noescape (encoder: JSONEncoder) throws -> Void) rethrows -> JSONObject { + let encoder = JSONEncoder() + try setup(encoder: encoder) + return encoder.object + } + + /* + Note: + There is some duplication because methods with generic constraints need to + take a concrete type conforming to the constraint are unable to take a parameter + typed to the protocol. Hence we need non-generic versions so we can cast from + Any to JSONEncodable in the default implementation for toJSON(). + */ + + // JSONEncodable + public func encode(_ value: Encodable, key: String) throws { + let result = try value.toJSON() + object[key] = result + } + private func encode(_ value: JSONEncodable, key: String) throws { + let result = try value.toJSON() + object[key] = result + } + + // JSONEncodable? + public func encode(_ value: Encodable?, key: String) throws { + guard let actual = value else { + return } - - /* - Note: - There is some duplication because methods with generic constraints need to - take a concrete type conforming to the constraint are unable to take a parameter - typed to the protocol. Hence we need non-generic versions so we can cast from - Any to JSONEncodable in the default implementation for toJSON(). - */ - - // JSONEncodable - public func encode(value: Encodable, key: String) throws { - let result = try value.toJSON() - object[key] = result + let result = try actual.toJSON() + object[key] = result + } + + // Enum + public func encode(_ value: Enum, key: String) throws { + guard let compatible = value.rawValue as? JSONCompatible else { + return } - private func encode(value: JSONEncodable, key: String) throws { - let result = try value.toJSON() - object[key] = result + let result = try compatible.toJSON() + object[key] = result + } + + // Enum? + public func encode(_ value: Enum?, key: String) throws { + guard let actual = value else { + return } - - // JSONEncodable? - public func encode(value: Encodable?, key: String) throws { - guard let actual = value else { - return - } - let result = try actual.toJSON() - object[key] = result + guard let compatible = actual.rawValue as? JSONCompatible else { + return } - - // Enum - public func encode(value: Enum, key: String) throws { - guard let compatible = value.rawValue as? JSONCompatible else { - return - } - let result = try compatible.toJSON() - object[key] = result + let result = try compatible.toJSON() + object[key] = result + } + + // [JSONEncodable] + public func encode(_ array: [Encodable], key: String) throws { + guard array.count > 0 else { + return } - - // Enum? - public func encode(value: Enum?, key: String) throws { - guard let actual = value else { - return - } - guard let compatible = actual.rawValue as? JSONCompatible else { - return - } - let result = try compatible.toJSON() - object[key] = result + let result = try array.toJSON() + object[key] = result + } + public func encode(_ array: [JSONEncodable], key: String) throws { + guard array.count > 0 else { + return } - - // [JSONEncodable] - public func encode(array: [Encodable], key: String) throws { - guard array.count > 0 else { - return - } - let result = try array.toJSON() - object[key] = result + let result = try array.toJSON() + object[key] = result + } + private func encode(_ array: JSONArray, key: String) throws { + guard array.count > 0 && array.elementsAreJSONEncodable() else { + return } - public func encode(array: [JSONEncodable], key: String) throws { - guard array.count > 0 else { - return - } - let result = try array.toJSON() - object[key] = result + let encodable = array.elementsMadeJSONEncodable() + let result = try encodable.toJSON() + object[key] = result + } + + // [JSONEncodable]? + public func encode(_ value: [Encodable]?, key: String) throws { + guard let actual = value else { + return } - private func encode(array: JSONArray, key: String) throws { - guard array.count > 0 && array.elementsAreJSONEncodable() else { - return - } - let encodable = array.elementsMadeJSONEncodable() - let result = try encodable.toJSON() - object[key] = result + guard actual.count > 0 else { + return } - - // [JSONEncodable]? - public func encode(value: [Encodable]?, key: String) throws { - guard let actual = value else { - return - } - guard actual.count > 0 else { - return - } - let result = try actual.toJSON() - object[key] = result + let result = try actual.toJSON() + object[key] = result + } + + // [Enum] + public func encode(_ value: [Enum], key: String) throws { + guard value.count > 0 else { + return } - - // [Enum] - public func encode(value: [Enum], key: String) throws { - guard value.count > 0 else { - return - } - let result = try value.flatMap { - try ($0.rawValue as? JSONCompatible)?.toJSON() - } - object[key] = result + let result = try value.flatMap { + try ($0.rawValue as? JSONCompatible)?.toJSON() } - - // [Enum]? - public func encode(value: [Enum]?, key: String) throws { - guard let actual = value else { - return - } - guard actual.count > 0 else { - return - } - let result = try actual.flatMap { - try ($0.rawValue as? JSONCompatible)?.toJSON() - } - object[key] = result + object[key] = result + } + + // [Enum]? + public func encode(_ value: [Enum]?, key: String) throws { + guard let actual = value else { + return } - - // [String:JSONEncodable] - public func encode(dictionary: [String:Encodable], key: String) throws { - guard dictionary.count > 0 else { - return - } - let result = try dictionary.toJSON() - object[key] = result + guard actual.count > 0 else { + return } - public func encode(dictionary: [String:JSONEncodable], key: String) throws { - guard dictionary.count > 0 else { - return - } - let result = try dictionary.toJSON() - object[key] = result + let result = try actual.flatMap { + try ($0.rawValue as? JSONCompatible)?.toJSON() } - private func encode(dictionary: JSONDictionary, key: String) throws { - guard dictionary.count > 0 && dictionary.valuesAreJSONEncodable() else { - return - } - let encodable = dictionary.valuesMadeJSONEncodable() - let result = try encodable.toJSON() - object[key] = result + object[key] = result + } + + // [String:JSONEncodable] + public func encode(_ dictionary: [String:Encodable], key: String) throws { + guard dictionary.count > 0 else { + return } - - // [String:JSONEncodable]? - public func encode(value: [String:Encodable]?, key: String) throws { - guard let actual = value else { - return - } - guard actual.count > 0 else { - return - } - let result = try actual.toJSON() - object[key] = result + let result = try dictionary.toJSON() + object[key] = result + } + public func encode(_ dictionary: [String:JSONEncodable], key: String) throws { + guard dictionary.count > 0 else { + return } - - // JSONTransformable - public func encode(value: DecodedType, key: String, transformer: JSONTransformer) throws { - guard let result = transformer.encoding(value) else { - throw JSONEncodableError.TransformerFailedError(key: key) - } - object[key] = (result as! AnyObject) + let result = try dictionary.toJSON() + object[key] = result + } + private func encode(_ dictionary: JSONDictionary, key: String) throws { + guard dictionary.count > 0 && dictionary.valuesAreJSONEncodable() else { + return } - - // JSONTransformable? - public func encode(value: DecodedType?, key: String, transformer: JSONTransformer) throws { - guard let actual = value else { - return - } - guard let result = transformer.encoding(actual) else { - return - } - object[key] = (result as! AnyObject) + let encodable = dictionary.valuesMadeJSONEncodable() + let result = try encodable.toJSON() + object[key] = result + } + + // [String:JSONEncodable]? + public func encode(_ value: [String:Encodable]?, key: String) throws { + guard let actual = value else { + return + } + guard actual.count > 0 else { + return + } + let result = try actual.toJSON() + object[key] = result + } + + // JSONTransformable + public func encode(_ value: DecodedType, key: String, transformer: JSONTransformer) throws { + guard let result = transformer.encoding(value) else { + throw JSONEncodableError.transformerFailedError(key: key) + } + object[key] = (result as! AnyObject) + } + + // JSONTransformable? + public func encode(_ value: DecodedType?, key: String, transformer: JSONTransformer) throws { + guard let actual = value else { + return + } + guard let result = transformer.encoding(actual) else { + return } + object[key] = (result as! AnyObject) + } } diff --git a/JSONCodable/JSONHelpers.swift b/JSONCodable/JSONHelpers.swift index 1683d07..035e61b 100644 --- a/JSONCodable/JSONHelpers.swift +++ b/JSONCodable/JSONHelpers.swift @@ -13,49 +13,49 @@ public typealias JSONObject = [String: AnyObject] // Dictionary handling protocol JSONDictionary { - var count: Int { get } - func valuesAreJSONEncodable() -> Bool - func valuesMadeJSONEncodable() -> [String: JSONEncodable] + var count: Int { get } + func valuesAreJSONEncodable() -> Bool + func valuesMadeJSONEncodable() -> [String: JSONEncodable] } extension Dictionary : JSONDictionary { - func valuesAreJSONEncodable() -> Bool { - return Key.self is String.Type && (Value.self is JSONEncodable.Type || Value.self is JSONEncodable.Protocol) - } - - func valuesMadeJSONEncodable() -> [String: JSONEncodable] { - var dict: [String: JSONEncodable] = [:] - for (k, v) in self { - dict[String(k)] = v as? JSONEncodable - } - return dict + func valuesAreJSONEncodable() -> Bool { + return Key.self is String.Type && (Value.self is JSONEncodable.Type || Value.self is JSONEncodable.Protocol) + } + + func valuesMadeJSONEncodable() -> [String: JSONEncodable] { + var dict: [String: JSONEncodable] = [:] + for (k, v) in self { + dict[String(k)] = v as? JSONEncodable } + return dict + } } // Array handling protocol JSONArray { - var count: Int { get } - func elementsAreJSONEncodable() -> Bool - func elementsMadeJSONEncodable() -> [JSONEncodable] + var count: Int { get } + func elementsAreJSONEncodable() -> Bool + func elementsMadeJSONEncodable() -> [JSONEncodable] } extension Array: JSONArray { - func elementsAreJSONEncodable() -> Bool { - return Element.self is JSONEncodable.Type || Element.self is JSONEncodable.Protocol - } - - func elementsMadeJSONEncodable() -> [JSONEncodable] { - return self.map {$0 as! JSONEncodable} - } + func elementsAreJSONEncodable() -> Bool { + return Element.self is JSONEncodable.Type || Element.self is JSONEncodable.Protocol + } + + func elementsMadeJSONEncodable() -> [JSONEncodable] { + return self.map {$0 as! JSONEncodable} + } } // Optional handling protocol JSONOptional { - var wrapped: Any? { get } + var wrapped: Any? { get } } extension Optional: JSONOptional { - var wrapped: Any? { return self } + var wrapped: Any? { return self } } diff --git a/JSONCodable/JSONString.swift b/JSONCodable/JSONString.swift index fa0da39..53cea35 100644 --- a/JSONCodable/JSONString.swift +++ b/JSONCodable/JSONString.swift @@ -9,108 +9,80 @@ import Foundation public extension JSONEncodable { - public func toJSONString() throws -> String { - switch self { - case let str as String: - return escapeJSONString(str) - case is Bool, is Int, is Float, is Double: - return String(self) - default: - let json = try toJSON() - #if !swift(>=3.0) - let data = try NSJSONSerialization.dataWithJSONObject(json, options: NSJSONWritingOptions(rawValue: 0)) - #else - let data = try NSJSONSerialization.data(withJSONObject: json, options: NSJSONWritingOptions(rawValue: 0)) - #endif - guard let string = NSString(data: data, encoding: NSUTF8StringEncoding) else { - return "" - } - return string as String - } + public func toJSONString() throws -> String { + switch self { + case let str as String: + return escapeJSONString(str) + case is Bool, is Int, is Float, is Double: + return String(self) + default: + let json = try toJSON() + let data = try JSONSerialization.data(withJSONObject: json, options: []) + guard let string = String(data: data, encoding: String.Encoding.utf8) else { + return "" + } + return string } + } } -private func escapeJSONString(str: String) -> String { - var chars = String.CharacterView("\"") - for c in str.characters { - switch c { - case "\\": - chars.append("\\") - chars.append("\\") - case "\"": - chars.append("\\") - chars.append("\"") - default: - chars.append(c) - } +private func escapeJSONString(_ str: String) -> String { + var chars = String.CharacterView("\"") + for c in str.characters { + switch c { + case "\\": + chars.append("\\") + chars.append("\\") + case "\"": + chars.append("\\") + chars.append("\"") + default: + chars.append(c) } - chars.append("\"") - return String(chars) + } + chars.append("\"") + return String(chars) } public extension Optional where Wrapped: JSONEncodable { - public func toJSONString() throws -> String { - #if !swift(>=3.0) - switch self { - case let .Some(jsonEncodable): - return try jsonEncodable.toJSONString() - case nil: - return "null" - } - #else - switch self { - case let .some(jsonEncodable): - return try jsonEncodable.toJSONString() - case nil: - return "null" - } - #endif + public func toJSONString() throws -> String { + switch self { + case let .some(jsonEncodable): + return try jsonEncodable.toJSONString() + case nil: + return "null" } + } } public extension JSONDecodable { - init(JSONString: String) throws { - #if !swift(>=3.0) - guard let data = JSONString.dataUsingEncoding(NSUTF8StringEncoding) else { - throw JSONDecodableError.IncompatibleTypeError(key: "n/a", elementType: JSONString.dynamicType, expectedType: String.self) - } - - let result: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(rawValue: 0)) - #else - guard let data = JSONString.data(usingEncoding:NSUTF8StringEncoding) else { - throw JSONDecodableError.IncompatibleTypeError(key: "n/a", elementType: JSONString.dynamicType, expectedType: String.self) - } - - let result: AnyObject = try NSJSONSerialization.jsonObject(with: data, options: NSJSONReadingOptions(rawValue: 0)) - #endif - - guard let converted = result as? [String: AnyObject] else { - throw JSONDecodableError.DictionaryTypeExpectedError(key: "n/a", elementType: result.dynamicType) - } - - try self.init(object: converted) + init(JSONString: String) throws { + guard let data = JSONString.data(using:String.Encoding.utf8) else { + throw JSONDecodableError.incompatibleTypeError(key: "n/a", elementType: JSONString.dynamicType, expectedType: String.self) + } + + let result: AnyObject = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions(rawValue: 0)) + + guard let converted = result as? [String: AnyObject] else { + throw JSONDecodableError.dictionaryTypeExpectedError(key: "n/a", elementType: result.dynamicType) } + + try self.init(object: converted) + } } public extension Array where Element: JSONDecodable { - init(JSONString: String) throws { - #if !swift(>=3.0) - guard let data = JSONString.dataUsingEncoding(NSUTF8StringEncoding) else { - throw JSONDecodableError.IncompatibleTypeError(key: "n/a", elementType: JSONString.dynamicType, expectedType: String.self) - } - - let result: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(rawValue: 0)) - #else - guard let data = JSONString.data(usingEncoding: NSUTF8StringEncoding) else { - throw JSONDecodableError.IncompatibleTypeError(key: "n/a", elementType: JSONString.dynamicType, expectedType: String.self) - } - - let result: AnyObject = try NSJSONSerialization.jsonObject(with: data, options: NSJSONReadingOptions(rawValue: 0)) - #endif - guard let converted = result as? [AnyObject] else { - throw JSONDecodableError.ArrayTypeExpectedError(key: "n/a", elementType: result.dynamicType) - } - - try self.init(JSONArray: converted) + init(JSONString: String) throws { + guard let data = JSONString.data(using: String.Encoding.utf8) else { + throw JSONDecodableError.incompatibleTypeError(key: "n/a", elementType: JSONString.dynamicType, expectedType: String.self) + } + + let result: AnyObject = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions(rawValue: 0)) + + guard let converted = result as? [AnyObject] else { + throw JSONDecodableError.arrayTypeExpectedError(key: "n/a", elementType: result.dynamicType) } + + try self.init(JSONArray: converted) + } } diff --git a/JSONCodable/JSONTransformer.swift b/JSONCodable/JSONTransformer.swift index 68c9646..2cfaea5 100644 --- a/JSONCodable/JSONTransformer.swift +++ b/JSONCodable/JSONTransformer.swift @@ -9,41 +9,35 @@ // Converting between types public struct JSONTransformer: CustomStringConvertible { - let decoding: (EncodedType -> DecodedType?) - let encoding: (DecodedType -> EncodedType?) - - // needs public accessor - public init(decoding: (EncodedType -> DecodedType?), encoding: (DecodedType -> EncodedType?)) { - self.decoding = decoding - self.encoding = encoding - } - - public var description: String { - return "JSONTransformer \(EncodedType.self) <-> \(DecodedType.self)" - } + let decoding: ((EncodedType) -> DecodedType?) + let encoding: ((DecodedType) -> EncodedType?) + + // needs public accessor + public init(decoding: ((EncodedType) -> DecodedType?), encoding: ((DecodedType) -> EncodedType?)) { + self.decoding = decoding + self.encoding = encoding + } + + public var description: String { + return "JSONTransformer \(EncodedType.self) <-> \(DecodedType.self)" + } } import Foundation -private let dateTimeFormatter: NSDateFormatter = { - let formatter = NSDateFormatter() - formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" - formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0) - formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") - return formatter +private let dateTimeFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" + formatter.timeZone = TimeZone(forSecondsFromGMT: 0) + formatter.locale = Locale(localeIdentifier: "en_US_POSIX") + return formatter }() public struct JSONTransformers { - public static let StringToNSURL = JSONTransformer( - decoding: {NSURL(string: $0)}, - encoding: {$0.absoluteString}) - - #if !swift(>=3.0) - public static let StringToNSDate = JSONTransformer( - decoding: {dateTimeFormatter.dateFromString($0)}, - encoding: {dateTimeFormatter.stringFromDate($0)}) - #else - public static let StringToNSDate = JSONTransformer( - decoding: {dateTimeFormatter.date(from: $0)}, - encoding: {dateTimeFormatter.string(from: $0)}) - #endif + public static let StringToNSURL = JSONTransformer( + decoding: {URL(string: $0)}, + encoding: {$0.absoluteString}) + + public static let StringToNSDate = JSONTransformer( + decoding: {dateTimeFormatter.date(from: $0)}, + encoding: {dateTimeFormatter.string(from: $0)}) } diff --git a/JSONCodableTests/Company.swift b/JSONCodableTests/Company.swift index 6d040ed..d8ab537 100644 --- a/JSONCodableTests/Company.swift +++ b/JSONCodableTests/Company.swift @@ -10,20 +10,20 @@ import Foundation import JSONCodable struct Company: Equatable { - let name: String - var address: String? + let name: String + var address: String? } func ==(lhs: Company, rhs: Company) -> Bool { - return lhs.name == rhs.name && lhs.address == rhs.address + return lhs.name == rhs.name && lhs.address == rhs.address } extension Company: JSONEncodable {} extension Company: JSONDecodable { - init(object: JSONObject) throws { - let decoder = JSONDecoder(object: object) - name = try decoder.decode("name") - address = try decoder.decode("address") - } -} \ No newline at end of file + init(object: JSONObject) throws { + let decoder = JSONDecoder(object: object) + name = try decoder.decode("name") + address = try decoder.decode("address") + } +} diff --git a/JSONCodableTests/EnumTests.swift b/JSONCodableTests/EnumTests.swift index f8d0f4d..ddca888 100644 --- a/JSONCodableTests/EnumTests.swift +++ b/JSONCodableTests/EnumTests.swift @@ -9,48 +9,48 @@ import XCTest class EnumTests: XCTestCase { + + let encodedValue = ["name": "apple", "color": "Red"] + let decodedValue = Fruit(name: "apple", color: FruitColor.Red) + + let encodedValue2 = ["name": "Seaweed Pasta", "cuisines": ["Italian", "Japanese"]] + let decodedValue2 = Food(name: "Seaweed Pasta", cuisines: [.Italian, .Japanese]) + + func testDecodingEnum() { + guard let fruit = try? Fruit(object: encodedValue) else { + XCTFail() + return + } - let encodedValue = ["name": "apple", "color": "Red"] - let decodedValue = Fruit(name: "apple", color: FruitColor.Red) + XCTAssertEqual(fruit, decodedValue) - let encodedValue2 = ["name": "Seaweed Pasta", "cuisines": ["Italian", "Japanese"]] - let decodedValue2 = Food(name: "Seaweed Pasta", cuisines: [.Italian, .Japanese]) + guard let food = try? Food(object: encodedValue2) else { + XCTFail() + return + } - func testDecodingEnum() { - guard let fruit = try? Fruit(object: encodedValue) else { - XCTFail() - return - } - - XCTAssertEqual(fruit, decodedValue) - - guard let food = try? Food(object: encodedValue2) else { - XCTFail() - return - } - - XCTAssertEqual(food, decodedValue2) + XCTAssertEqual(food, decodedValue2) + } + + func testEncodingEnum() { + guard let json = try? decodedValue.toJSON() else { + XCTFail() + return } - func testEncodingEnum() { - guard let json = try? decodedValue.toJSON() else { - XCTFail() - return - } - - guard let castedJSON = json as? [String: String] else { - XCTFail() - return - } - - XCTAssertEqual(castedJSON, encodedValue) - - guard let json2 = try? decodedValue2.toJSON() else { - XCTFail() - return - } - - print(json2, encodedValue2) + guard let castedJSON = json as? [String: String] else { + XCTFail() + return + } + + XCTAssertEqual(castedJSON, encodedValue) + + guard let json2 = try? decodedValue2.toJSON() else { + XCTFail() + return } + print(json2, encodedValue2) + } + } diff --git a/JSONCodableTests/Food.swift b/JSONCodableTests/Food.swift index 14847d1..cad6d13 100644 --- a/JSONCodableTests/Food.swift +++ b/JSONCodableTests/Food.swift @@ -10,38 +10,38 @@ import Foundation import JSONCodable enum Cuisine: String { - case Mexican - case Italian - case German - case French - case Pizza - case Barbecue - case Chinese - case Japanese - case Korean - case Thai + case Mexican + case Italian + case German + case French + case Pizza + case Barbecue + case Chinese + case Japanese + case Korean + case Thai } struct Food: Equatable { - let name: String - let cuisines: [Cuisine] + let name: String + let cuisines: [Cuisine] } func ==(lhs: Food, rhs: Food) -> Bool { - return lhs.name == rhs.name && lhs.cuisines == rhs.cuisines + return lhs.name == rhs.name && lhs.cuisines == rhs.cuisines } extension Food: JSONCodable { - init(object: JSONObject) throws { - let decoder = JSONDecoder(object: object) - name = try decoder.decode("name") - cuisines = try decoder.decode("cuisines") - } - - func toJSON() throws -> AnyObject { - return try JSONEncoder.create({ (encoder) -> Void in - try encoder.encode(name, key: "name") - try encoder.encode(cuisines, key: "cuisines") - }) - } -} \ No newline at end of file + init(object: JSONObject) throws { + let decoder = JSONDecoder(object: object) + name = try decoder.decode("name") + cuisines = try decoder.decode("cuisines") + } + + func toJSON() throws -> AnyObject { + return try JSONEncoder.create({ (encoder) -> Void in + try encoder.encode(name, key: "name") + try encoder.encode(cuisines, key: "cuisines") + }) + } +} diff --git a/JSONCodableTests/Fruit.swift b/JSONCodableTests/Fruit.swift index 3d981bc..13ad398 100644 --- a/JSONCodableTests/Fruit.swift +++ b/JSONCodableTests/Fruit.swift @@ -10,30 +10,30 @@ import Foundation import JSONCodable enum FruitColor: String { - case Red - case Blue + case Red + case Blue } struct Fruit: Equatable { - let name: String - let color: FruitColor + let name: String + let color: FruitColor } func ==(lhs: Fruit, rhs: Fruit) -> Bool { - return lhs.name == rhs.name && lhs.color == rhs.color + return lhs.name == rhs.name && lhs.color == rhs.color } extension Fruit: JSONCodable { - init(object: JSONObject) throws { - let decoder = JSONDecoder(object: object) - name = try decoder.decode("name") - color = try decoder.decode("color") - } - - func toJSON() throws -> AnyObject { - return try JSONEncoder.create({ (encoder) -> Void in - try encoder.encode(name, key: "name") - try encoder.encode(color, key: "color") - }) - } -} \ No newline at end of file + init(object: JSONObject) throws { + let decoder = JSONDecoder(object: object) + name = try decoder.decode("name") + color = try decoder.decode("color") + } + + func toJSON() throws -> AnyObject { + return try JSONEncoder.create({ (encoder) -> Void in + try encoder.encode(name, key: "name") + try encoder.encode(color, key: "color") + }) + } +} diff --git a/JSONCodableTests/HelperTests.swift b/JSONCodableTests/HelperTests.swift index 5bfa0db..b27f399 100644 --- a/JSONCodableTests/HelperTests.swift +++ b/JSONCodableTests/HelperTests.swift @@ -13,37 +13,37 @@ struct NotEncodable { } class HelperTests: XCTestCase { + + func testArrayElementsAreEncodable() { + let intArray:[Int] = [1,2,3] + XCTAssert(intArray.elementsAreJSONEncodable(), "Array of type [Int] should be encodable") - func testArrayElementsAreEncodable() { - let intArray:[Int] = [1,2,3] - XCTAssert(intArray.elementsAreJSONEncodable(), "Array of type [Int] should be encodable") - - let encodableArray:[JSONEncodable] = [1,2,3] - XCTAssert(encodableArray.elementsAreJSONEncodable(), "Array of type [JSONEncodable] should be encodable") - - let notEncodableArray:[NotEncodable] = [NotEncodable()] - XCTAssert(!notEncodableArray.elementsAreJSONEncodable(), "Array of type [NotEncodable] should not be encodable") - - let _ = try? JSONEncoder.create({ (encoder) -> Void in - try encoder.encode(intArray, key: "intArray") - try encoder.encode(encodableArray, key: "encodableArray") - }) - } - - func testDictionaryIsEncodable() { - let intDict:[String:Int] = ["a":1,"b":2,"c":3] - XCTAssert(intDict.valuesAreJSONEncodable(), "Dictionary of type [String:Int] should be encodable") - - let encodableDict:[String:JSONEncodable] = ["a":1,"b":2,"c":3] - XCTAssert(encodableDict.valuesAreJSONEncodable(), "Dictionary of type [String:JSONEncodable] should be encodable") - - let notEncodableDict:[String:NotEncodable] = ["a":NotEncodable()] - XCTAssert(!notEncodableDict.valuesAreJSONEncodable(), "Dictionary of type [String:NotEncodable] should not be encodable") - - let _ = try? JSONEncoder.create({ (encoder) -> Void in - try encoder.encode(intDict, key: "intArray") - try encoder.encode(encodableDict, key: "encodableArray") - }) - } + let encodableArray:[JSONEncodable] = [1,2,3] + XCTAssert(encodableArray.elementsAreJSONEncodable(), "Array of type [JSONEncodable] should be encodable") + + let notEncodableArray:[NotEncodable] = [NotEncodable()] + XCTAssert(!notEncodableArray.elementsAreJSONEncodable(), "Array of type [NotEncodable] should not be encodable") + + let _ = try? JSONEncoder.create({ (encoder) -> Void in + try encoder.encode(intArray, key: "intArray") + try encoder.encode(encodableArray, key: "encodableArray") + }) + } + + func testDictionaryIsEncodable() { + let intDict:[String:Int] = ["a":1,"b":2,"c":3] + XCTAssert(intDict.valuesAreJSONEncodable(), "Dictionary of type [String:Int] should be encodable") + + let encodableDict:[String:JSONEncodable] = ["a":1,"b":2,"c":3] + XCTAssert(encodableDict.valuesAreJSONEncodable(), "Dictionary of type [String:JSONEncodable] should be encodable") + + let notEncodableDict:[String:NotEncodable] = ["a":NotEncodable()] + XCTAssert(!notEncodableDict.valuesAreJSONEncodable(), "Dictionary of type [String:NotEncodable] should not be encodable") + let _ = try? JSONEncoder.create({ (encoder) -> Void in + try encoder.encode(intDict, key: "intArray") + try encoder.encode(encodableDict, key: "encodableArray") + }) + } + } diff --git a/JSONCodableTests/ImageAsset.swift b/JSONCodableTests/ImageAsset.swift index 5561b6c..21ca445 100644 --- a/JSONCodableTests/ImageAsset.swift +++ b/JSONCodableTests/ImageAsset.swift @@ -10,27 +10,27 @@ import Foundation import JSONCodable struct ImageAsset: Equatable { - let name: String - var uri: NSURL? + let name: String + var uri: NSURL? } func ==(lhs: ImageAsset, rhs: ImageAsset) -> Bool { - return lhs.name == rhs.name && lhs.uri == rhs.uri + return lhs.name == rhs.name && lhs.uri == rhs.uri } extension ImageAsset: JSONEncodable { - func toJSON() throws -> AnyObject { - return try JSONEncoder.create({ (encoder) -> Void in - try encoder.encode(name, key: "name") - try encoder.encode(uri, key: "uri", transformer: JSONTransformers.StringToNSURL) - }) - } + func toJSON() throws -> AnyObject { + return try JSONEncoder.create({ (encoder) -> Void in + try encoder.encode(name, key: "name") + try encoder.encode(uri, key: "uri", transformer: JSONTransformers.StringToNSURL) + }) + } } extension ImageAsset: JSONDecodable { - init(object: JSONObject) throws { - let decoder = JSONDecoder(object: object) - name = try decoder.decode("name") - uri = try decoder.decode("uri", transformer: JSONTransformers.StringToNSURL) - } -} \ No newline at end of file + init(object: JSONObject) throws { + let decoder = JSONDecoder(object: object) + name = try decoder.decode("name") + uri = try decoder.decode("uri", transformer: JSONTransformers.StringToNSURL) + } +} diff --git a/JSONCodableTests/NestItem.swift b/JSONCodableTests/NestItem.swift index 40ff086..75ad42b 100644 --- a/JSONCodableTests/NestItem.swift +++ b/JSONCodableTests/NestItem.swift @@ -10,24 +10,23 @@ import Foundation import JSONCodable struct NestItem { - let areas: [[Float]] - var places: [[String]]? - var business: [[Company]] - var assets: [[ImageAsset]]? + let areas: [[Float]] + var places: [[String]]? + var business: [[Company]] + var assets: [[ImageAsset]]? } extension NestItem: JSONDecodable { - init(object: JSONObject) throws { - do { - let decoder = JSONDecoder(object: object) - areas = try decoder.decode("areas") - places = try decoder.decode("places") - business = try decoder.decode("business") - assets = try decoder.decode("assets") - }catch{ - fatalError("\(error)") - } + init(object: JSONObject) throws { + do { + let decoder = JSONDecoder(object: object) + areas = try decoder.decode("areas") + places = try decoder.decode("places") + business = try decoder.decode("business") + assets = try decoder.decode("assets") + }catch{ + fatalError("\(error)") } - + } + } - diff --git a/JSONCodableTests/RegularTests.swift b/JSONCodableTests/RegularTests.swift index 37e3b67..1399fde 100644 --- a/JSONCodableTests/RegularTests.swift +++ b/JSONCodableTests/RegularTests.swift @@ -9,111 +9,111 @@ import XCTest class RegularTests: XCTestCase { - - let nestedCodableArray = ["areas" : [[10.0,10.5,12.5]], - "places":[["Tokyo","New York", "El Cerrito"]], - "business" : [[ - [ "name": "Apple", - "address": "1 Infinite Loop, Cupertino, CA" - ], - [ "name": "Propeller", - "address": "1212 broadway, Oakland, CA" - ] + + let nestedCodableArray = ["areas" : [[10.0,10.5,12.5]], + "places":[["Tokyo","New York", "El Cerrito"]], + "business" : [[ + [ "name": "Apple", + "address": "1 Infinite Loop, Cupertino, CA" + ], + [ "name": "Propeller", + "address": "1212 broadway, Oakland, CA" + ] ]], - "assets": [[ - [ "name": "image-name", - "uri": "http://www.example.com/image.png" - ], - [ "name": "image2-name", - "uri": "http://www.example.com/image2.png" - ] - ]]] - - let encodedNestedArray = [ - "id": 99, - "full_name": "Jen Jackson", - "properties":[ - ["likes":5], - ["likes":15], - ["likes":25] - ] - ] - - let encodedValue = [ - "id": 24, - "full_name": "John Appleseed", - "email": "john@appleseed.com", - "company": [ - "name": "Apple", - "address": "1 Infinite Loop, Cupertino, CA" - ], - "friends": [ - ["id": 27, "full_name": "Bob Jefferson"], - ["id": 29, "full_name": "Jen Jackson"] - ], - "friendsLookup": ["Bob Jefferson": ["id": 27, "full_name": "Bob Jefferson"]] + "assets": [[ + [ "name": "image-name", + "uri": "http://www.example.com/image.png" + ], + [ "name": "image2-name", + "uri": "http://www.example.com/image2.png" + ] + ]]] + + let encodedNestedArray = [ + "id": 99, + "full_name": "Jen Jackson", + "properties":[ + ["likes":5], + ["likes":15], + ["likes":25] ] - let decodedValue = User( - id: 24, - likes:0, - name: "John Appleseed", - email: "john@appleseed.com", - company: Company(name: "Apple", address: "1 Infinite Loop, Cupertino, CA"), - friends: [ - User(id: 27, likes:0, name: "Bob Jefferson", email: nil, company: nil, friends: [], friendsLookup: nil), - User(id: 29, likes:0, name: "Jen Jackson", email: nil, company: nil, friends: [], friendsLookup: nil) - ], - friendsLookup: ["Bob Jefferson": User(id: 27, likes:0, name: "Bob Jefferson", email: nil, company: nil, friends: [], friendsLookup: nil)] - ) - - func testDecodeNestedCodableArray() { - guard let nested = try? NestItem(object: nestedCodableArray) else { - XCTFail() - return - } - print("nested=",nested) - let places = nested.places ?? [[]] - let areas = nested.areas - let business = nested.business - let assets = nested.assets ?? [[]] - XCTAssert(places == [["Tokyo","New York", "El Cerrito"]], "\(nestedCodableArray))") - XCTAssert(areas == [[10.0,10.5,12.5]], "\(nestedCodableArray))") - - XCTAssert(business.map{ $0.map{ $0.name } } == [[try! Company(object:["name": "Apple", - "address": "1 Infinite Loop, Cupertino, CA"]), - try! Company(object:[ "name": "Propeller", - "address": "1212 broadway, Oakland, CA"])].map{ $0.name }], - "\(nestedCodableArray))") - - XCTAssert(assets.map{ $0.map{ $0.name } } == [[try! ImageAsset(object:[ "name": "image-name", - "uri": "http://www.example.com/image.png"]), - try! ImageAsset(object: ["name": "image2-name", - "uri": "http://www.example.com/image2.png"])].map{ $0.name }], - "\(nestedCodableArray))") + ] + + let encodedValue = [ + "id": 24, + "full_name": "John Appleseed", + "email": "john@appleseed.com", + "company": [ + "name": "Apple", + "address": "1 Infinite Loop, Cupertino, CA" + ], + "friends": [ + ["id": 27, "full_name": "Bob Jefferson"], + ["id": 29, "full_name": "Jen Jackson"] + ], + "friendsLookup": ["Bob Jefferson": ["id": 27, "full_name": "Bob Jefferson"]] + ] + let decodedValue = User( + id: 24, + likes:0, + name: "John Appleseed", + email: "john@appleseed.com", + company: Company(name: "Apple", address: "1 Infinite Loop, Cupertino, CA"), + friends: [ + User(id: 27, likes:0, name: "Bob Jefferson", email: nil, company: nil, friends: [], friendsLookup: nil), + User(id: 29, likes:0, name: "Jen Jackson", email: nil, company: nil, friends: [], friendsLookup: nil) + ], + friendsLookup: ["Bob Jefferson": User(id: 27, likes:0, name: "Bob Jefferson", email: nil, company: nil, friends: [], friendsLookup: nil)] + ) + + func testDecodeNestedCodableArray() { + guard let nested = try? NestItem(object: nestedCodableArray) else { + XCTFail() + return } + print("nested=",nested) + let places = nested.places ?? [[]] + let areas = nested.areas + let business = nested.business + let assets = nested.assets ?? [[]] + XCTAssert(places == [["Tokyo","New York", "El Cerrito"]], "\(nestedCodableArray))") + XCTAssert(areas == [[10.0,10.5,12.5]], "\(nestedCodableArray))") - func testDecodingNestedArray() { - guard let user = try? User(object: encodedNestedArray) else { - XCTFail() - return - } - XCTAssert(user.likes != nil, "\(encodedNestedArray))") - } + XCTAssert(business.map{ $0.map{ $0.name } } == [[try! Company(object:["name": "Apple", + "address": "1 Infinite Loop, Cupertino, CA"]), + try! Company(object:[ "name": "Propeller", + "address": "1212 broadway, Oakland, CA"])].map{ $0.name }], + "\(nestedCodableArray))") - func testDecodingRegular() { - guard let user = try? User(object: encodedValue) else { - XCTFail() - return - } - XCTAssertEqual(user, decodedValue) + XCTAssert(assets.map{ $0.map{ $0.name } } == [[try! ImageAsset(object:[ "name": "image-name", + "uri": "http://www.example.com/image.png"]), + try! ImageAsset(object: ["name": "image2-name", + "uri": "http://www.example.com/image2.png"])].map{ $0.name }], + "\(nestedCodableArray))") + } + + func testDecodingNestedArray() { + guard let user = try? User(object: encodedNestedArray) else { + XCTFail() + return + } + XCTAssert(user.likes != nil, "\(encodedNestedArray))") + } + + func testDecodingRegular() { + guard let user = try? User(object: encodedValue) else { + XCTFail() + return } + XCTAssertEqual(user, decodedValue) + } - func testEncodingRegular() { - guard let json = try? decodedValue.toJSON() else { - XCTFail() - return - } - - XCTAssertEqual(json as! [String : NSObject], encodedValue) + func testEncodingRegular() { + guard let json = try? decodedValue.toJSON() else { + XCTFail() + return } + + XCTAssertEqual(json as! [String : NSObject], encodedValue) + } } diff --git a/JSONCodableTests/TransformerTests.swift b/JSONCodableTests/TransformerTests.swift index f323923..11cfead 100644 --- a/JSONCodableTests/TransformerTests.swift +++ b/JSONCodableTests/TransformerTests.swift @@ -9,32 +9,32 @@ import XCTest class TransformerTests: XCTestCase { - - let encodedValue = [ - "name": "image-name", - "uri": "http://www.example.com/image.png" - ] - let decodedValue = ImageAsset( - name: "image-name", - uri: NSURL(string: "http://www.example.com/image.png") - ) - - func testDecodingTransformer() { - guard let asset = try? ImageAsset(object: encodedValue) else { - XCTFail() - return - } - - XCTAssertEqual(asset, decodedValue) + + let encodedValue = [ + "name": "image-name", + "uri": "http://www.example.com/image.png" + ] + let decodedValue = ImageAsset( + name: "image-name", + uri: NSURL(string: "http://www.example.com/image.png") + ) + + func testDecodingTransformer() { + guard let asset = try? ImageAsset(object: encodedValue) else { + XCTFail() + return } - func testEncodingTransformer() { - guard let json = try? decodedValue.toJSON() else { - XCTFail() - return - } - - XCTAssertEqual(json as! [String : NSObject], encodedValue) + XCTAssertEqual(asset, decodedValue) + } + + func testEncodingTransformer() { + guard let json = try? decodedValue.toJSON() else { + XCTFail() + return } -} \ No newline at end of file + XCTAssertEqual(json as! [String : NSObject], encodedValue) + } + +} diff --git a/JSONCodableTests/User.swift b/JSONCodableTests/User.swift index b2dafaf..f31cff9 100644 --- a/JSONCodableTests/User.swift +++ b/JSONCodableTests/User.swift @@ -10,45 +10,45 @@ import Foundation import JSONCodable struct User: Equatable { - let id: Int - var likes: Int? - let name: String - var email: String? - var company: Company? - var friends: [User] = [] - var friendsLookup: [String: User]? + let id: Int + var likes: Int? + let name: String + var email: String? + var company: Company? + var friends: [User] = [] + var friendsLookup: [String: User]? } func ==(lhs: User, rhs: User) -> Bool { - return lhs.id == rhs.id && - lhs.name == rhs.name && - lhs.email == rhs.email && - lhs.company == rhs.company && - lhs.friends == rhs.friends + return lhs.id == rhs.id && + lhs.name == rhs.name && + lhs.email == rhs.email && + lhs.company == rhs.company && + lhs.friends == rhs.friends } extension User: JSONEncodable { - func toJSON() throws -> AnyObject { - return try JSONEncoder.create({ (encoder) -> Void in - try encoder.encode(id, key: "id") - try encoder.encode(name, key: "full_name") - try encoder.encode(email, key: "email") - try encoder.encode(company, key: "company") - try encoder.encode(friends, key: "friends") - try encoder.encode(friendsLookup, key: "friendsLookup") - }) + func toJSON() throws -> AnyObject { + return try JSONEncoder.create { (encoder) -> Void in + try encoder.encode(id, key: "id") + try encoder.encode(name, key: "full_name") + try encoder.encode(email, key: "email") + try encoder.encode(company, key: "company") + try encoder.encode(friends, key: "friends") + try encoder.encode(friendsLookup, key: "friendsLookup") } + } } extension User: JSONDecodable { - init(object: JSONObject) throws { - let decoder = JSONDecoder(object: object) - id = try decoder.decode("id") - likes = try decoder.decode("properties[0].likes") - name = try decoder.decode("full_name") - email = try decoder.decode("email") - company = try decoder.decode("company") - friends = try decoder.decode("friends") - friendsLookup = try decoder.decode("friendsLookup") - } -} \ No newline at end of file + init(object: JSONObject) throws { + let decoder = JSONDecoder(object: object) + id = try decoder.decode("id") + likes = try decoder.decode("properties[0].likes") + name = try decoder.decode("full_name") + email = try decoder.decode("email") + company = try decoder.decode("company") + friends = try decoder.decode("friends") + friendsLookup = try decoder.decode("friendsLookup") + } +} diff --git a/JSONCodableTests/UtilityTests.swift b/JSONCodableTests/UtilityTests.swift index ef63fd4..480011b 100644 --- a/JSONCodableTests/UtilityTests.swift +++ b/JSONCodableTests/UtilityTests.swift @@ -10,18 +10,18 @@ import XCTest @testable import JSONCodable class UtilityTests: XCTestCase { - func testArrayIndexDecoding() { - let jsonObj = ["test":"test"] - let decoder = JSONDecoder(object: jsonObj) - XCTAssert(decoder.parseArrayIndex("[0]") == 0) - XCTAssert(decoder.parseArrayIndex("[1]") == 1) - XCTAssert(decoder.parseArrayIndex("[10]") == 10) - XCTAssert(decoder.parseArrayIndex("[202]") == 202) - XCTAssert(decoder.parseArrayIndex("[1111111]") == 1111111) - XCTAssert(decoder.parseArrayIndex("[zero]") == nil) - XCTAssert(decoder.parseArrayIndex("[1A]") == nil) - XCTAssert(decoder.parseArrayIndex("[^]") == nil) - XCTAssert(decoder.parseArrayIndex("HAHA") == nil) - XCTAssert(decoder.parseArrayIndex("[-1]") == -1) - } -} \ No newline at end of file + func testArrayIndexDecoding() { + let jsonObj = ["test":"test"] + let decoder = JSONDecoder(object: jsonObj) + XCTAssert(decoder.parseArrayIndex("[0]") == 0) + XCTAssert(decoder.parseArrayIndex("[1]") == 1) + XCTAssert(decoder.parseArrayIndex("[10]") == 10) + XCTAssert(decoder.parseArrayIndex("[202]") == 202) + XCTAssert(decoder.parseArrayIndex("[1111111]") == 1111111) + XCTAssert(decoder.parseArrayIndex("[zero]") == nil) + XCTAssert(decoder.parseArrayIndex("[1A]") == nil) + XCTAssert(decoder.parseArrayIndex("[^]") == nil) + XCTAssert(decoder.parseArrayIndex("HAHA") == nil) + XCTAssert(decoder.parseArrayIndex("[-1]") == -1) + } +} From bed971434c458590061d7d1a3188a17a359f3854 Mon Sep 17 00:00:00 2001 From: Matthew Cheok Date: Sun, 19 Jun 2016 12:05:44 -0700 Subject: [PATCH 2/4] Fixed tests --- JSONCodable/JSONTransformer.swift | 4 ++-- JSONCodableTests/ImageAsset.swift | 10 +++++----- JSONCodableTests/TransformerTests.swift | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/JSONCodable/JSONTransformer.swift b/JSONCodable/JSONTransformer.swift index 2cfaea5..8c78598 100644 --- a/JSONCodable/JSONTransformer.swift +++ b/JSONCodable/JSONTransformer.swift @@ -33,11 +33,11 @@ private let dateTimeFormatter: DateFormatter = { }() public struct JSONTransformers { - public static let StringToNSURL = JSONTransformer( + public static let StringToURL = JSONTransformer( decoding: {URL(string: $0)}, encoding: {$0.absoluteString}) - public static let StringToNSDate = JSONTransformer( + public static let StringToDate = JSONTransformer( decoding: {dateTimeFormatter.date(from: $0)}, encoding: {dateTimeFormatter.string(from: $0)}) } diff --git a/JSONCodableTests/ImageAsset.swift b/JSONCodableTests/ImageAsset.swift index 21ca445..9e6a569 100644 --- a/JSONCodableTests/ImageAsset.swift +++ b/JSONCodableTests/ImageAsset.swift @@ -11,7 +11,7 @@ import JSONCodable struct ImageAsset: Equatable { let name: String - var uri: NSURL? + var uri: URL? } func ==(lhs: ImageAsset, rhs: ImageAsset) -> Bool { @@ -20,10 +20,10 @@ func ==(lhs: ImageAsset, rhs: ImageAsset) -> Bool { extension ImageAsset: JSONEncodable { func toJSON() throws -> AnyObject { - return try JSONEncoder.create({ (encoder) -> Void in + return try JSONEncoder.create{ (encoder) -> Void in try encoder.encode(name, key: "name") - try encoder.encode(uri, key: "uri", transformer: JSONTransformers.StringToNSURL) - }) + try encoder.encode(uri, key: "uri", transformer: JSONTransformers.StringToURL) + } } } @@ -31,6 +31,6 @@ extension ImageAsset: JSONDecodable { init(object: JSONObject) throws { let decoder = JSONDecoder(object: object) name = try decoder.decode("name") - uri = try decoder.decode("uri", transformer: JSONTransformers.StringToNSURL) + uri = try decoder.decode("uri", transformer: JSONTransformers.StringToURL) } } diff --git a/JSONCodableTests/TransformerTests.swift b/JSONCodableTests/TransformerTests.swift index 11cfead..bcccbee 100644 --- a/JSONCodableTests/TransformerTests.swift +++ b/JSONCodableTests/TransformerTests.swift @@ -16,7 +16,7 @@ class TransformerTests: XCTestCase { ] let decodedValue = ImageAsset( name: "image-name", - uri: NSURL(string: "http://www.example.com/image.png") + uri: URL(string: "http://www.example.com/image.png") ) func testDecodingTransformer() { From 9782c0fc77679c92fcd5d9a8a082305db121e2d1 Mon Sep 17 00:00:00 2001 From: Matthew Cheok Date: Sun, 19 Jun 2016 13:05:21 -0700 Subject: [PATCH 3/4] Add support exporting nested JSON objects --- JSONCodable/JSONEncodable.swift | 47 ++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/JSONCodable/JSONEncodable.swift b/JSONCodable/JSONEncodable.swift index 8298542..77c01c2 100644 --- a/JSONCodable/JSONEncodable.swift +++ b/JSONCodable/JSONEncodable.swift @@ -136,6 +136,21 @@ public class JSONEncoder { return encoder.object } + private func update(object: JSONObject, keys: [String], value: AnyObject) -> JSONObject + { + var newObject = object + var newKeys = keys + + let firstKey = newKeys.removeFirst() + if newKeys.count > 0 { + let innerObject = object[firstKey] as? JSONObject ?? JSONObject() + newObject[firstKey] = update(object: innerObject, keys: newKeys, value: value) + } else { + newObject[firstKey] = value + } + return newObject + } + /* Note: There is some duplication because methods with generic constraints need to @@ -147,11 +162,11 @@ public class JSONEncoder { // JSONEncodable public func encode(_ value: Encodable, key: String) throws { let result = try value.toJSON() - object[key] = result + object = update(object: object, keys: key.components(separatedBy: "."), value: result) } private func encode(_ value: JSONEncodable, key: String) throws { let result = try value.toJSON() - object[key] = result + object = update(object: object, keys: key.components(separatedBy: "."), value: result) } // JSONEncodable? @@ -160,7 +175,7 @@ public class JSONEncoder { return } let result = try actual.toJSON() - object[key] = result + object = update(object: object, keys: key.components(separatedBy: "."), value: result) } // Enum @@ -169,7 +184,7 @@ public class JSONEncoder { return } let result = try compatible.toJSON() - object[key] = result + object = update(object: object, keys: key.components(separatedBy: "."), value: result) } // Enum? @@ -181,7 +196,7 @@ public class JSONEncoder { return } let result = try compatible.toJSON() - object[key] = result + object = update(object: object, keys: key.components(separatedBy: "."), value: result) } // [JSONEncodable] @@ -190,14 +205,14 @@ public class JSONEncoder { return } let result = try array.toJSON() - object[key] = result + object = update(object: object, keys: key.components(separatedBy: "."), value: result) } public func encode(_ array: [JSONEncodable], key: String) throws { guard array.count > 0 else { return } let result = try array.toJSON() - object[key] = result + object = update(object: object, keys: key.components(separatedBy: "."), value: result) } private func encode(_ array: JSONArray, key: String) throws { guard array.count > 0 && array.elementsAreJSONEncodable() else { @@ -205,7 +220,7 @@ public class JSONEncoder { } let encodable = array.elementsMadeJSONEncodable() let result = try encodable.toJSON() - object[key] = result + object = update(object: object, keys: key.components(separatedBy: "."), value: result) } // [JSONEncodable]? @@ -217,7 +232,7 @@ public class JSONEncoder { return } let result = try actual.toJSON() - object[key] = result + object = update(object: object, keys: key.components(separatedBy: "."), value: result) } // [Enum] @@ -228,7 +243,7 @@ public class JSONEncoder { let result = try value.flatMap { try ($0.rawValue as? JSONCompatible)?.toJSON() } - object[key] = result + object = update(object: object, keys: key.components(separatedBy: "."), value: result) } // [Enum]? @@ -242,7 +257,7 @@ public class JSONEncoder { let result = try actual.flatMap { try ($0.rawValue as? JSONCompatible)?.toJSON() } - object[key] = result + object = update(object: object, keys: key.components(separatedBy: "."), value: result) } // [String:JSONEncodable] @@ -251,7 +266,7 @@ public class JSONEncoder { return } let result = try dictionary.toJSON() - object[key] = result + object = update(object: object, keys: key.components(separatedBy: "."), value: result) } public func encode(_ dictionary: [String:JSONEncodable], key: String) throws { guard dictionary.count > 0 else { @@ -266,7 +281,7 @@ public class JSONEncoder { } let encodable = dictionary.valuesMadeJSONEncodable() let result = try encodable.toJSON() - object[key] = result + object = update(object: object, keys: key.components(separatedBy: "."), value: result) } // [String:JSONEncodable]? @@ -278,7 +293,7 @@ public class JSONEncoder { return } let result = try actual.toJSON() - object[key] = result + object = update(object: object, keys: key.components(separatedBy: "."), value: result) } // JSONTransformable @@ -286,7 +301,7 @@ public class JSONEncoder { guard let result = transformer.encoding(value) else { throw JSONEncodableError.transformerFailedError(key: key) } - object[key] = (result as! AnyObject) + object = update(object: object, keys: key.components(separatedBy: "."), value: result as! AnyObject) } // JSONTransformable? @@ -297,6 +312,6 @@ public class JSONEncoder { guard let result = transformer.encoding(actual) else { return } - object[key] = (result as! AnyObject) + object = update(object: object, keys: key.components(separatedBy: "."), value: result as! AnyObject) } } From 794ee4315f0aebf9d5e67a2c749d09c3723ac4ff Mon Sep 17 00:00:00 2001 From: Nadohs Date: Sun, 19 Jun 2016 13:42:02 -0700 Subject: [PATCH 4/4] test for encoding nested JSON objects and remove unneeded imports. --- JSONCodable.xcodeproj/project.pbxproj | 8 +++++ JSONCodableTests/Company.swift | 1 - JSONCodableTests/EncodeNestingTests.swift | 36 +++++++++++++++++++ JSONCodableTests/Food.swift | 1 - JSONCodableTests/Fruit.swift | 1 - JSONCodableTests/ImageAsset.swift | 1 - JSONCodableTests/NestItem.swift | 2 -- JSONCodableTests/PropertyItem.swift | 44 +++++++++++++++++++++++ JSONCodableTests/User.swift | 1 - 9 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 JSONCodableTests/EncodeNestingTests.swift create mode 100644 JSONCodableTests/PropertyItem.swift diff --git a/JSONCodable.xcodeproj/project.pbxproj b/JSONCodable.xcodeproj/project.pbxproj index 9e294c7..52f8545 100644 --- a/JSONCodable.xcodeproj/project.pbxproj +++ b/JSONCodable.xcodeproj/project.pbxproj @@ -33,6 +33,8 @@ 9EDB394F1B59D0AF00C63019 /* JSONTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EDB39421B59D0AF00C63019 /* JSONTransformer.swift */; }; 9EDB39501B59D0AF00C63019 /* JSONTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EDB39421B59D0AF00C63019 /* JSONTransformer.swift */; }; 9EDB39511B59D15400C63019 /* JSONCodable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9EDB39091B59D00B00C63019 /* JSONCodable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BD885BBE1D17358E00CA767A /* EncodeNestingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD885BBD1D17358E00CA767A /* EncodeNestingTests.swift */; }; + BD885BC01D173A0700CA767A /* PropertyItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD885BBF1D173A0700CA767A /* PropertyItem.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -69,6 +71,8 @@ 9EDB393F1B59D0AF00C63019 /* JSONHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONHelpers.swift; sourceTree = ""; }; 9EDB39411B59D0AF00C63019 /* JSONString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONString.swift; sourceTree = ""; }; 9EDB39421B59D0AF00C63019 /* JSONTransformer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONTransformer.swift; sourceTree = ""; }; + BD885BBD1D17358E00CA767A /* EncodeNestingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EncodeNestingTests.swift; sourceTree = ""; }; + BD885BBF1D173A0700CA767A /* PropertyItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PropertyItem.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -111,6 +115,7 @@ isa = PBXGroup; children = ( 5211CD091CE2EBFB0097F255 /* NestItem.swift */, + BD885BBF1D173A0700CA767A /* PropertyItem.swift */, 9E455C021BCE1C1E00070A4F /* Fruit.swift */, 9E8E07231BD3F15800F98421 /* Food.swift */, 9E455C041BCE1D0700070A4F /* User.swift */, @@ -126,6 +131,7 @@ 9ECF00C31BCF82F5008D557C /* HelperTests.swift */, 52E8F44E1C9087D200F40F7F /* UtilityTests.swift */, 9E455C0A1BCE1F0100070A4F /* RegularTests.swift */, + BD885BBD1D17358E00CA767A /* EncodeNestingTests.swift */, 9E455BF91BCE185B00070A4F /* EnumTests.swift */, 9ECF00BF1BCE251B008D557C /* TransformerTests.swift */, ); @@ -326,8 +332,10 @@ 9ECF00C01BCE251B008D557C /* TransformerTests.swift in Sources */, 9E455BFA1BCE185B00070A4F /* EnumTests.swift in Sources */, 9E8E07241BD3F15800F98421 /* Food.swift in Sources */, + BD885BBE1D17358E00CA767A /* EncodeNestingTests.swift in Sources */, 9E455C0B1BCE1F0100070A4F /* RegularTests.swift in Sources */, 9E455C051BCE1D0700070A4F /* User.swift in Sources */, + BD885BC01D173A0700CA767A /* PropertyItem.swift in Sources */, 9E455C091BCE1DE100070A4F /* Company.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/JSONCodableTests/Company.swift b/JSONCodableTests/Company.swift index d8ab537..8d6ef55 100644 --- a/JSONCodableTests/Company.swift +++ b/JSONCodableTests/Company.swift @@ -6,7 +6,6 @@ // // -import Foundation import JSONCodable struct Company: Equatable { diff --git a/JSONCodableTests/EncodeNestingTests.swift b/JSONCodableTests/EncodeNestingTests.swift new file mode 100644 index 0000000..ee56a10 --- /dev/null +++ b/JSONCodableTests/EncodeNestingTests.swift @@ -0,0 +1,36 @@ +// +// EncodeNestingTests.swift +// JSONCodable +// +// Created by Richard Fox on 6/19/16. +// +// + +import XCTest +import JSONCodable + +class EncodeNestingTests: XCTestCase { + + let propertyItemArray: JSONObject = [ + "class": "propertyType", + "rel": "propertyType", + "properties" : + [ "name" : "person", + "location" : [ "coord" : [ + "lat" : 37.790770, + "long" : -122.402015 + ]]]] + + func testEncodeNestedPropertyItem() { + guard let pItem = try? PropertyItem(object: propertyItemArray), + json = try? pItem.toJSON(), + json1 = json as? JSONObject else { + XCTFail() + return + } + print(String(json1)) + print("\n\n") + print(String(propertyItemArray)) + XCTAssert(String(json1) == String(propertyItemArray), "failed to convert to \(propertyItemArray)") + } +} diff --git a/JSONCodableTests/Food.swift b/JSONCodableTests/Food.swift index cad6d13..94861e0 100644 --- a/JSONCodableTests/Food.swift +++ b/JSONCodableTests/Food.swift @@ -6,7 +6,6 @@ // // -import Foundation import JSONCodable enum Cuisine: String { diff --git a/JSONCodableTests/Fruit.swift b/JSONCodableTests/Fruit.swift index 13ad398..e032b04 100644 --- a/JSONCodableTests/Fruit.swift +++ b/JSONCodableTests/Fruit.swift @@ -6,7 +6,6 @@ // // -import Foundation import JSONCodable enum FruitColor: String { diff --git a/JSONCodableTests/ImageAsset.swift b/JSONCodableTests/ImageAsset.swift index 9e6a569..88f63df 100644 --- a/JSONCodableTests/ImageAsset.swift +++ b/JSONCodableTests/ImageAsset.swift @@ -6,7 +6,6 @@ // // -import Foundation import JSONCodable struct ImageAsset: Equatable { diff --git a/JSONCodableTests/NestItem.swift b/JSONCodableTests/NestItem.swift index 75ad42b..61b821b 100644 --- a/JSONCodableTests/NestItem.swift +++ b/JSONCodableTests/NestItem.swift @@ -6,7 +6,6 @@ // // -import Foundation import JSONCodable struct NestItem { @@ -28,5 +27,4 @@ extension NestItem: JSONDecodable { fatalError("\(error)") } } - } diff --git a/JSONCodableTests/PropertyItem.swift b/JSONCodableTests/PropertyItem.swift new file mode 100644 index 0000000..cd45ff0 --- /dev/null +++ b/JSONCodableTests/PropertyItem.swift @@ -0,0 +1,44 @@ +// +// PropertyItem.swift +// JSONCodable +// +// Created by Richard Fox on 6/19/16. +// +// + +import JSONCodable + +struct PropertyItem { + let name: String + let long: Double + let lat: Double + let rel: String + let type: String +} + +extension PropertyItem: JSONDecodable { + init(object: JSONObject) throws { + do { + let decoder = JSONDecoder(object: object) + rel = try decoder.decode("rel") + type = try decoder.decode("class") + name = try decoder.decode("properties.name") + long = try decoder.decode("properties.location.coord.long") + lat = try decoder.decode("properties.location.coord.lat") + }catch{ + fatalError("\(error)") + } + } +} + +extension PropertyItem: JSONEncodable { + func toJSON() throws -> AnyObject { + return try JSONEncoder.create { (encoder) -> Void in + try encoder.encode(rel, key: "rel") + try encoder.encode(type, key: "class") + try encoder.encode(name, key: "properties.name") + try encoder.encode(long, key: "properties.location.coord.long") + try encoder.encode(lat, key: "properties.location.coord.lat") + } + } +} diff --git a/JSONCodableTests/User.swift b/JSONCodableTests/User.swift index f31cff9..63609e2 100644 --- a/JSONCodableTests/User.swift +++ b/JSONCodableTests/User.swift @@ -6,7 +6,6 @@ // // -import Foundation import JSONCodable struct User: Equatable {