-
-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
177 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
// | ||
// CodableAny.swift | ||
// MoreCodable | ||
// | ||
// Created by Tatsuya Tanaka on 20180909. | ||
// Copyright © 2018年 tattn. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
public struct CodableAny { | ||
public let value: Any | ||
|
||
public init(_ value: Any) { | ||
self.value = value | ||
} | ||
} | ||
|
||
extension CodableAny: Decodable { | ||
public init(from decoder: Decoder) throws { | ||
let container = try decoder.singleValueContainer() | ||
|
||
if container.decodeNil() { | ||
self.init(()) | ||
} else if let bool = try? container.decode(Bool.self) { | ||
self.init(bool) | ||
} else if let int = try? container.decode(Int.self) { | ||
self.init(int) | ||
} else if let uint = try? container.decode(UInt.self) { | ||
self.init(uint) | ||
} else if let double = try? container.decode(Double.self) { | ||
self.init(double) | ||
} else if let string = try? container.decode(String.self) { | ||
self.init(string) | ||
} else if let array = try? container.decode([CodableAny].self) { | ||
self.init(array.map { $0.value }) | ||
} else if let dictionary = try? container.decode([String: CodableAny].self) { | ||
self.init(dictionary.mapValues { $0.value }) | ||
} else { | ||
throw DecodingError.dataCorruptedError( | ||
in: container, | ||
debugDescription: "This type is not supported." | ||
) | ||
} | ||
} | ||
} | ||
|
||
extension CodableAny: Encodable { | ||
public func encode(to encoder: Encoder) throws { | ||
var container = encoder.singleValueContainer() | ||
|
||
switch value { | ||
case is Void, Optional<Any>.none: | ||
try container.encodeNil() | ||
case let bool as Bool: | ||
try container.encode(bool) | ||
case let int as Int: | ||
try container.encode(int) | ||
case let int8 as Int8: | ||
try container.encode(int8) | ||
case let int16 as Int16: | ||
try container.encode(int16) | ||
case let int32 as Int32: | ||
try container.encode(int32) | ||
case let int64 as Int64: | ||
try container.encode(int64) | ||
case let uint as UInt: | ||
try container.encode(uint) | ||
case let uint8 as UInt8: | ||
try container.encode(uint8) | ||
case let uint16 as UInt16: | ||
try container.encode(uint16) | ||
case let uint32 as UInt32: | ||
try container.encode(uint32) | ||
case let uint64 as UInt64: | ||
try container.encode(uint64) | ||
case let float as Float: | ||
try container.encode(float) | ||
case let double as Double: | ||
try container.encode(double) | ||
case let string as String: | ||
try container.encode(string) | ||
case let date as Date: | ||
try container.encode(date) | ||
case let url as URL: | ||
try container.encode(url) | ||
case let array as [Any]: | ||
try container.encode(array.map(CodableAny.init)) | ||
case let dictionary as [String: Any]: | ||
try container.encode(dictionary.mapValues(CodableAny.init)) | ||
default: | ||
throw EncodingError.invalidValue(value, EncodingError.Context( | ||
codingPath: container.codingPath, | ||
debugDescription: "\(type(of: value)) type is not supported." | ||
)) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
// | ||
// CodableAnyTests.swift | ||
// MoreCodableTests | ||
// | ||
// Created by Tatsuya Tanaka on 20180909. | ||
// Copyright © 2018年 tattn. All rights reserved. | ||
// | ||
|
||
import XCTest | ||
import MoreCodable | ||
|
||
class CodableAnyTests: XCTestCase { | ||
let jsonEncoder = JSONEncoder() | ||
let jsonDecoder = JSONDecoder() | ||
|
||
override func setUp() { | ||
super.setUp() | ||
} | ||
|
||
func testInt() { | ||
assertEncodingAndDecoding(["value": 1]) | ||
} | ||
|
||
func testStringIntDictionary() { | ||
assertEncodingAndDecoding(["value": ["key": 1]]) | ||
} | ||
|
||
func testDoubleArray() { | ||
assertEncodingAndDecoding([1.1, 2.2, 3.3]) | ||
|
||
let decodedValue = encodeAndDecode([1, 2, 3] as [Double]) | ||
XCTAssertEqual(decodedValue as! [Int], [1, 2, 3]) // double to int | ||
} | ||
|
||
func testNestedArray() { | ||
assertEncodingAndDecoding([[[["one", "two", "three"]]]]) | ||
} | ||
|
||
func testOptional() { | ||
let decodedValue = encodeAndDecode([nil, 1, nil]) | ||
let values = decodedValue as! [Any] | ||
XCTAssertNotNil(values[0]) | ||
XCTAssertTrue(values[0] is Void) | ||
XCTAssertEqual(values[1] as! Int, 1) | ||
XCTAssertNotNil(values[2]) | ||
XCTAssertTrue(values[2] is Void) | ||
} | ||
|
||
func testDate() { | ||
let date = Date() | ||
let decodedValue = encodeAndDecode(["key": date]) | ||
XCTAssertEqual(decodedValue as! [String: Double], ["key": date.timeIntervalSinceReferenceDate]) // date to double | ||
} | ||
|
||
func testURL() { | ||
let url = URL(string: "https://example.com")! | ||
let decodedValue = encodeAndDecode(["key": url]) | ||
XCTAssertEqual(decodedValue as! [String: String], ["key": url.absoluteString]) // url to string | ||
} | ||
|
||
private func encodeAndDecode(_ value: Any) -> Any { | ||
let _any = CodableAny(value) | ||
let data = try! jsonEncoder.encode(_any) | ||
return (try! jsonDecoder.decode(CodableAny.self, from: data)).value | ||
} | ||
|
||
private func assertEncodingAndDecoding<T: Equatable>(_ expectedValue: T) { | ||
let decodedValue = encodeAndDecode(expectedValue) | ||
XCTAssertEqual(decodedValue as! T, expectedValue) | ||
} | ||
} |