Last active
October 19, 2018 09:37
-
-
Save esphynox/52743ce50f77c451b3babf4f2b2fdac8 to your computer and use it in GitHub Desktop.
Decoding heterogenous arrays with base protocol in Swift 4
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
import Foundation | |
protocol Item: Codable { | |
static var type: ItemType { get } | |
var commonProtocolString: String { get } | |
} | |
enum ItemType: String, Codable { | |
case numberItem | |
case stringItem | |
var metatype: Item.Type { | |
switch self { | |
case .numberItem: return NumberItem.self | |
case .stringItem: return StringItem.self | |
} | |
} | |
} | |
struct NumberItem: Item { | |
static var type = ItemType.numberItem | |
let commonProtocolString = "common string from protocol" | |
let numberUniqueToThisStruct = 42 | |
} | |
struct StringItem: Item { | |
static var type = ItemType.stringItem | |
let commonProtocolString = "protocol member string" | |
let stringUniqueToThisStruct = "a random string" | |
} | |
struct AnyItem: Codable { | |
var item: Item | |
init(_ item: Item) { | |
self.item = item | |
} | |
private enum CodingKeys : CodingKey { | |
case type | |
case item | |
} | |
func encode(to encoder: Encoder) throws { | |
var container = encoder.container(keyedBy: CodingKeys.self) | |
try container.encode(type(of: item).type, forKey: .type) | |
try item.encode(to: encoder) | |
} | |
init(from decoder: Decoder) throws { | |
let container = try decoder.container(keyedBy: CodingKeys.self) | |
let type = try container.decode(ItemType.self, forKey: .type) | |
self.item = try type.metatype.init(from: decoder) | |
} | |
} | |
struct Parent: Codable { | |
let title: String | |
let items: [Item] | |
init(title: String, items: [Item]) { | |
self.title = title | |
self.items = items | |
} | |
enum CodingKeys: String, CodingKey { | |
case title | |
case items | |
} | |
func encode(to encoder: Encoder) throws { | |
var container = encoder.container(keyedBy: CodingKeys.self) | |
try container.encode(title, forKey: .title) | |
try container.encode(items.map({ AnyItem($0) }), forKey: .items) | |
} | |
init(from decoder: Decoder) throws { | |
let container = try decoder.container(keyedBy: CodingKeys.self) | |
title = try container.decode(String.self, forKey: .title) | |
items = try container.decode([AnyItem].self, forKey: .items).map { $0.item } | |
} | |
} | |
func testEncoding(_ parent: Parent) -> Data { | |
let jsonEncoder = JSONEncoder() | |
jsonEncoder.outputFormatting = .prettyPrinted | |
let jsonData = try! jsonEncoder.encode(parent) | |
let jsonString = String(data: jsonData, encoding: .utf8)! | |
return jsonData | |
} | |
func testDecoding(_ jsonData: Data) -> Parent { | |
let jsonDecoder = JSONDecoder() | |
let decoded = try! jsonDecoder.decode(type(of: parent), from: jsonData) | |
return decoded | |
} | |
let parent = Parent(title: "Parent Struct", items: [StringItem(), NumberItem()]) | |
let jsonData = testEncoding(parent) | |
let jsonString = String(data: jsonData, encoding: .utf8)! | |
print("Encoded object to JSON:\n\(jsonString)") | |
let parentDecoded = testDecoding(jsonData) | |
print("Decoded object from JSON:\n\(parentDecoded)") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment