Skip to content

Commit

Permalink
Add ObjectMerger
Browse files Browse the repository at this point in the history
  • Loading branch information
tattn committed Mar 1, 2018
1 parent 8fc325b commit 7e1dcac
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 16 deletions.
8 changes: 8 additions & 0 deletions MoreCodable.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
objects = {

/* Begin PBXBuildFile section */
24028DD0204856B400721297 /* ObjectMerger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24028DCF204856B400721297 /* ObjectMerger.swift */; };
24028DD2204859A400721297 /* ObjectMergerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24028DD1204859A400721297 /* ObjectMergerTests.swift */; };
242C3E262030CBE500AAA577 /* URLQueryItemsEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 242C3E252030CBE500AAA577 /* URLQueryItemsEncoder.swift */; };
242C3E292030D70300AAA577 /* URLQueryItemsEncoderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 242C3E272030D68900AAA577 /* URLQueryItemsEncoderTests.swift */; };
242C3E2B2030D83600AAA577 /* URLQueryItemsDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 242C3E2A2030D83600AAA577 /* URLQueryItemsDecoder.swift */; };
Expand Down Expand Up @@ -40,6 +42,8 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
24028DCF204856B400721297 /* ObjectMerger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectMerger.swift; sourceTree = "<group>"; };
24028DD1204859A400721297 /* ObjectMergerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectMergerTests.swift; sourceTree = "<group>"; };
242C3E252030CBE500AAA577 /* URLQueryItemsEncoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLQueryItemsEncoder.swift; sourceTree = "<group>"; };
242C3E272030D68900AAA577 /* URLQueryItemsEncoderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLQueryItemsEncoderTests.swift; sourceTree = "<group>"; };
242C3E2A2030D83600AAA577 /* URLQueryItemsDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLQueryItemsDecoder.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -115,6 +119,7 @@
242C3E252030CBE500AAA577 /* URLQueryItemsEncoder.swift */,
242C3E2A2030D83600AAA577 /* URLQueryItemsDecoder.swift */,
242C3E2C2030DDDE00AAA577 /* URLQueryItem+.swift */,
24028DCF204856B400721297 /* ObjectMerger.swift */,
2491406C2039DF1D00D3E4CD /* Failable.swift */,
249140702039E27F00D3E4CD /* StringTo.swift */,
2491407E203C85A500D3E4CD /* RuleBasedCodingKey.swift */,
Expand All @@ -132,6 +137,7 @@
2491406E2039DF7B00D3E4CD /* FailableTests.swift */,
2491407C203C7D5200D3E4CD /* StringToTests.swift */,
24914080203C89D000D3E4CD /* RuleBasedCodingKeyTests.swift */,
24028DD1204859A400721297 /* ObjectMergerTests.swift */,
24A4FF4020302322001618E1 /* Products */,
24A4FF5820302490001618E1 /* Info.plist */,
);
Expand Down Expand Up @@ -255,6 +261,7 @@
249140712039E27F00D3E4CD /* StringTo.swift in Sources */,
242C3E2D2030DDDE00AAA577 /* URLQueryItem+.swift in Sources */,
24A4FF4D20302407001618E1 /* AnyCodingKey.swift in Sources */,
24028DD0204856B400721297 /* ObjectMerger.swift in Sources */,
242C3E2B2030D83600AAA577 /* URLQueryItemsDecoder.swift in Sources */,
242C3E262030CBE500AAA577 /* URLQueryItemsEncoder.swift in Sources */,
24A4FF4B203023E6001618E1 /* Storage.swift in Sources */,
Expand All @@ -273,6 +280,7 @@
242C3E292030D70300AAA577 /* URLQueryItemsEncoderTests.swift in Sources */,
242C3E2F2030E28500AAA577 /* URLQueryItemsDecoderTests.swift in Sources */,
24A4FF5720302490001618E1 /* DictionaryEncoderTests.swift in Sources */,
24028DD2204859A400721297 /* ObjectMergerTests.swift in Sources */,
2491407D203C7D5200D3E4CD /* StringToTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
68 changes: 52 additions & 16 deletions Sources/DictionaryEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ open class DictionaryEncoder: Encoder {
}

open func singleValueContainer() -> SingleValueEncodingContainer {
return UnkeyedContanier(encoder: self, codingPath: codingPath)
return SingleValueContanier(encoder: self, codingPath: codingPath)
}

private func box<T: Encodable>(_ value: T) throws -> Any {
Expand Down Expand Up @@ -116,11 +116,11 @@ extension DictionaryEncoder {
}
}

private class UnkeyedContanier: UnkeyedEncodingContainer, SingleValueEncodingContainer {
private class UnkeyedContanier: UnkeyedEncodingContainer {
var encoder: DictionaryEncoder
private(set) var codingPath: [CodingKey]
private var storage: Storage
var count: Int { return (storage.last as? [Any])?.count ?? 0 }
var count: Int { return storage.count }

init(encoder: DictionaryEncoder, codingPath: [CodingKey]) {
self.encoder = encoder
Expand All @@ -145,19 +145,19 @@ extension DictionaryEncoder {

func encodeNil() throws {}
func encode(_ value: Bool) throws {}
func encode(_ value: Int) throws { push(value) }
func encode(_ value: Int8) throws { push(value) }
func encode(_ value: Int16) throws { push(value) }
func encode(_ value: Int32) throws { push(value) }
func encode(_ value: Int64) throws { push(value) }
func encode(_ value: UInt) throws { push(value) }
func encode(_ value: UInt8) throws { push(value) }
func encode(_ value: UInt16) throws { push(value) }
func encode(_ value: UInt32) throws { push(value) }
func encode(_ value: UInt64) throws { push(value) }
func encode(_ value: Float) throws { push(value) }
func encode(_ value: Double) throws { push(value) }
func encode(_ value: String) throws { push(value) }
func encode(_ value: Int) throws { push(try encoder.box(value)) }
func encode(_ value: Int8) throws { push(try encoder.box(value)) }
func encode(_ value: Int16) throws { push(try encoder.box(value)) }
func encode(_ value: Int32) throws { push(try encoder.box(value)) }
func encode(_ value: Int64) throws { push(try encoder.box(value)) }
func encode(_ value: UInt) throws { push(try encoder.box(value)) }
func encode(_ value: UInt8) throws { push(try encoder.box(value)) }
func encode(_ value: UInt16) throws { push(try encoder.box(value)) }
func encode(_ value: UInt32) throws { push(try encoder.box(value)) }
func encode(_ value: UInt64) throws { push(try encoder.box(value)) }
func encode(_ value: Float) throws { push(try encoder.box(value)) }
func encode(_ value: Double) throws { push(try encoder.box(value)) }
func encode(_ value: String) throws { push(try encoder.box(value)) }
func encode<T: Encodable>(_ value: T) throws {
encoder.codingPath.append(AnyCodingKey(index: count))
defer { encoder.codingPath.removeLast() }
Expand All @@ -181,4 +181,40 @@ extension DictionaryEncoder {
return encoder
}
}

private class SingleValueContanier: SingleValueEncodingContainer {
var encoder: DictionaryEncoder
private(set) var codingPath: [CodingKey]
private var storage: Storage
var count: Int { return storage.count }

init(encoder: DictionaryEncoder, codingPath: [CodingKey]) {
self.encoder = encoder
self.codingPath = codingPath
self.storage = encoder.storage
}

private func push(_ value: Any) {
guard var array = storage.popContainer() as? [Any] else { assertionFailure(); return }
array.append(value)
storage.push(container: array)
}

func encodeNil() throws {}
func encode(_ value: Bool) throws { storage.push(container: value) }
func encode(_ value: Int) throws { storage.push(container: value) }
func encode(_ value: Int8) throws { storage.push(container: value) }
func encode(_ value: Int16) throws { storage.push(container: value) }
func encode(_ value: Int32) throws { storage.push(container: value) }
func encode(_ value: Int64) throws { storage.push(container: value) }
func encode(_ value: UInt) throws { storage.push(container: value) }
func encode(_ value: UInt8) throws { storage.push(container: value) }
func encode(_ value: UInt16) throws { storage.push(container: value) }
func encode(_ value: UInt32) throws { storage.push(container: value) }
func encode(_ value: UInt64) throws { storage.push(container: value) }
func encode(_ value: Float) throws { storage.push(container: value) }
func encode(_ value: Double) throws { storage.push(container: value) }
func encode(_ value: String) throws { storage.push(container: value) }
func encode<T: Encodable>(_ value: T) throws { storage.push(container: try encoder.box(value)) }
}
}
38 changes: 38 additions & 0 deletions Sources/ObjectMerger.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// ObjectMerger.swift
// MoreCodable
//
// Created by Tatsuya Tanaka on 20180302.
// Copyright © 2018年 tattn. All rights reserved.
//

import Foundation

public struct ObjectMerger {
private let encoder = DictionaryEncoder()
private let decoder = DictionaryDecoder()

public init() {}

public func merge<T: Decodable, A: Encodable, B: Encodable>(_ type: T.Type = T.self, _ aObject: A, _ bObject: B) throws -> T {
print(try encoder.encode(bObject))
let dictionary = try encoder.encode(aObject)
.merging(try encoder.encode(bObject)) { left, _ in left }
return try decoder.decode(T.self, from: dictionary)
}

public func merge<T: Decodable, A: Encodable, B: Encodable, C: Encodable>(_ type: T.Type = T.self, _ aObject: A, _ bObject: B, _ cObject: C) throws -> T {
let dictionary = try encoder.encode(aObject)
.merging(try encoder.encode(bObject)) { left, _ in left }
.merging(try encoder.encode(cObject)) { left, _ in left }
return try decoder.decode(T.self, from: dictionary)
}

public func merge<T: Decodable, A: Encodable, B: Encodable, C: Encodable, D: Encodable>(_ type: T.Type = T.self, _ aObject: A, _ bObject: B, _ cObject: C, _ dObject: D) throws -> T {
let dictionary = try encoder.encode(aObject)
.merging(try encoder.encode(bObject)) { left, _ in left }
.merging(try encoder.encode(cObject)) { left, _ in left }
.merging(try encoder.encode(dObject)) { left, _ in left }
return try decoder.decode(T.self, from: dictionary)
}
}
47 changes: 47 additions & 0 deletions Tests/ObjectMergerTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// ObjectMergerTests.swift
// MoreCodableTests
//
// Created by Tatsuya Tanaka on 20180302.
// Copyright © 2018年 tattn. All rights reserved.
//

import XCTest
import MoreCodable

class ObjectMergerTests: XCTestCase {

override func setUp() {
super.setUp()
}

override func tearDown() {
super.tearDown()
}

func testSimpleCase() throws {
struct APIResponse: Encodable {
let id: Int
let title: String
let foo: String
}

struct APIResponse2: Encodable {
let tags: [String]
}

struct Model: Decodable {
let id: Int
let title: String
let tags: [String]
}

let response = APIResponse(id: 0, title: "Awesome article", foo: "bar")
let response2 = APIResponse2(tags: ["swift", "ios", "macos"])
let model = try ObjectMerger().merge(Model.self, response, response2)

XCTAssertEqual(model.id, response.id)
XCTAssertEqual(model.title, response.title)
XCTAssertEqual(model.tags, response2.tags)
}
}

0 comments on commit 7e1dcac

Please sign in to comment.