Skip to content

Commit c4b78cb

Browse files
sharpletMaxDesiatov
authored andcommitted
Conditionally conform to Combine.TopLevelDecoder (#132)
This enables use of `XMLDecoder` with Combine's `decode(_:decoder:)` operator by conditionally conforming to `TopLevelDecoder` when Combine is available. I explored doing the same for `TopLevelEncoder`, but `XMLEncoder` currently requires `rootKey:` to be specified at encoding time, so it's not possible to provide an implementation of `encode(_:)` without some design around how to dynamically determine the root key to use. * Conditionally conform to Combine.TopLevelDecoder * Add test and documentation for Combine integration * Remove macCatalyst from CombineTests This shouldn't be necessary because all versions of Mac Catalyst should have a version of Combine available. * Add CombineTests to the Xcode project * Disable CombineTests on macOS
1 parent 149aafe commit c4b78cb

File tree

4 files changed

+68
-0
lines changed

4 files changed

+68
-0
lines changed

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,25 @@ extension IntOrString: Codable {
269269
This is described in more details in PR [\#119](https://github.com/MaxDesiatov/XMLCoder/pull/119)
270270
by [@jsbean](https://github.com/jsbean) and [@bwetherfield](https://github.com/bwetherfield).
271271

272+
## Integrating with [Combine](https://developer.apple.com/documentation/combine)
273+
274+
When Apple's Combine framework is available, `XMLDecoder` conforms to the
275+
`TopLevelDecoder` protocol, which allows it to be used with the
276+
`decode(type:decoder:)` operator:
277+
278+
```swift
279+
import Combine
280+
import Foundation
281+
import XMLCoder
282+
283+
func fetchBook(from url: URL) -> AnyPublisher<Book, Error> {
284+
return URLSession.shared.dataTaskPublisher(for: url)
285+
.map(\.data)
286+
.decode(type: Book.self, decoder: XMLDecoder())
287+
.eraseToAnyPublisher()
288+
}
289+
```
290+
272291
## Installation
273292

274293
### Requirements

Sources/XMLCoder/Decoder/XMLDecoder.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,3 +368,11 @@ open class XMLDecoder {
368368
return try decoder.unbox(topLevel)
369369
}
370370
}
371+
372+
// MARK: TopLevelDecoder
373+
374+
#if canImport(Combine)
375+
import protocol Combine.TopLevelDecoder
376+
377+
extension XMLDecoder: TopLevelDecoder {}
378+
#endif
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//
2+
// CombineTests.swift
3+
// XMLCoder
4+
//
5+
// Created by Adam Sharp on 9/29/19.
6+
//
7+
8+
#if canImport(Combine) && !os(macOS)
9+
import Combine
10+
import Foundation
11+
import XCTest
12+
import XMLCoder
13+
14+
private let xml = """
15+
<?xml version="1.0" encoding="UTF-8"?>
16+
<foo>
17+
<name>Foo</name>
18+
</foo>
19+
""".data(using: .utf8)!
20+
21+
private struct Foo: Decodable {
22+
var name: String
23+
}
24+
25+
@available(iOS 13.0, macOS 10.15.0, tvOS 13.0, watchOS 6.0, *)
26+
class CombineTests: XCTestCase {
27+
func testDecodeFromXMLDecoder() {
28+
let data = Just(xml)
29+
var foo: Foo?
30+
_ = data.decode(type: Foo.self, decoder: XMLDecoder()).sink(
31+
receiveCompletion: { _ in },
32+
receiveValue: { foo = $0 }
33+
)
34+
XCTAssertEqual(foo?.name, "Foo")
35+
}
36+
}
37+
#endif

XMLCoder.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
/* End PBXAggregateTarget section */
2222

2323
/* Begin PBXBuildFile section */
24+
4A062D4F2341924E009BCAC1 /* CombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A062D4E2341924E009BCAC1 /* CombineTests.swift */; };
2425
B5EA3BB6230F237800D8D69B /* NestedChoiceArrayTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EA3BB4230F235C00D8D69B /* NestedChoiceArrayTest.swift */; };
2526
B5F74472233F74E400BBDB15 /* RootLevelAttributeTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F74471233F74E400BBDB15 /* RootLevelAttributeTest.swift */; };
2627
OBJ_148 /* BoolBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* BoolBox.swift */; };
@@ -154,6 +155,7 @@
154155
/* End PBXContainerItemProxy section */
155156

156157
/* Begin PBXFileReference section */
158+
4A062D4E2341924E009BCAC1 /* CombineTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CombineTests.swift; sourceTree = "<group>"; };
157159
B5EA3BB4230F235C00D8D69B /* NestedChoiceArrayTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NestedChoiceArrayTest.swift; sourceTree = "<group>"; };
158160
B5F74471233F74E400BBDB15 /* RootLevelAttributeTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootLevelAttributeTest.swift; sourceTree = "<group>"; };
159161
OBJ_100 /* DecimalTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecimalTests.swift; sourceTree = "<group>"; };
@@ -428,6 +430,7 @@
428430
OBJ_86 /* CDCatalog.swift */,
429431
OBJ_87 /* CDTest.swift */,
430432
OBJ_88 /* ClassTests.swift */,
433+
4A062D4E2341924E009BCAC1 /* CombineTests.swift */,
431434
OBJ_89 /* CompositeChoiceTests.swift */,
432435
OBJ_90 /* DecodingContainerTests.swift */,
433436
OBJ_91 /* DynamicNodeDecodingTest.swift */,
@@ -742,6 +745,7 @@
742745
OBJ_269 /* RJITest.swift in Sources */,
743746
OBJ_270 /* RelationshipsTest.swift in Sources */,
744747
OBJ_271 /* SimpleChoiceTests.swift in Sources */,
748+
4A062D4F2341924E009BCAC1 /* CombineTests.swift in Sources */,
745749
OBJ_272 /* SingleChildTests.swift in Sources */,
746750
OBJ_273 /* SpacePreserveTest.swift in Sources */,
747751
);

0 commit comments

Comments
 (0)