diff --git a/README.md b/README.md index 8f2f24bc..dae64f16 100644 --- a/README.md +++ b/README.md @@ -269,6 +269,25 @@ extension IntOrString: Codable { This is described in more details in PR [\#119](https://github.com/MaxDesiatov/XMLCoder/pull/119) by [@jsbean](https://github.com/jsbean) and [@bwetherfield](https://github.com/bwetherfield). +## Integrating with [Combine](https://developer.apple.com/documentation/combine) + +When Apple's Combine framework is available, `XMLDecoder` conforms to the +`TopLevelDecoder` protocol, which allows it to be used with the +`decode(type:decoder:)` operator: + +```swift +import Combine +import Foundation +import XMLCoder + +func fetchBook(from url: URL) -> AnyPublisher { + return URLSession.shared.dataTaskPublisher(for: url) + .map(\.data) + .decode(type: Book.self, decoder: XMLDecoder()) + .eraseToAnyPublisher() +} +``` + ## Installation ### Requirements diff --git a/Sources/XMLCoder/Decoder/XMLDecoder.swift b/Sources/XMLCoder/Decoder/XMLDecoder.swift index 024b9935..11f1ead8 100644 --- a/Sources/XMLCoder/Decoder/XMLDecoder.swift +++ b/Sources/XMLCoder/Decoder/XMLDecoder.swift @@ -368,3 +368,11 @@ open class XMLDecoder { return try decoder.unbox(topLevel) } } + +// MARK: TopLevelDecoder + +#if canImport(Combine) +import protocol Combine.TopLevelDecoder + +extension XMLDecoder: TopLevelDecoder {} +#endif diff --git a/Tests/XMLCoderTests/CombineTests.swift b/Tests/XMLCoderTests/CombineTests.swift new file mode 100644 index 00000000..cdc0d7ea --- /dev/null +++ b/Tests/XMLCoderTests/CombineTests.swift @@ -0,0 +1,37 @@ +// +// CombineTests.swift +// XMLCoder +// +// Created by Adam Sharp on 9/29/19. +// + +#if canImport(Combine) && !os(macOS) +import Combine +import Foundation +import XCTest +import XMLCoder + +private let xml = """ + + + Foo + +""".data(using: .utf8)! + +private struct Foo: Decodable { + var name: String +} + +@available(iOS 13.0, macOS 10.15.0, tvOS 13.0, watchOS 6.0, *) +class CombineTests: XCTestCase { + func testDecodeFromXMLDecoder() { + let data = Just(xml) + var foo: Foo? + _ = data.decode(type: Foo.self, decoder: XMLDecoder()).sink( + receiveCompletion: { _ in }, + receiveValue: { foo = $0 } + ) + XCTAssertEqual(foo?.name, "Foo") + } +} +#endif diff --git a/XMLCoder.xcodeproj/project.pbxproj b/XMLCoder.xcodeproj/project.pbxproj index 287f49e6..6669133a 100644 --- a/XMLCoder.xcodeproj/project.pbxproj +++ b/XMLCoder.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 4A062D4F2341924E009BCAC1 /* CombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A062D4E2341924E009BCAC1 /* CombineTests.swift */; }; B5EA3BB6230F237800D8D69B /* NestedChoiceArrayTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EA3BB4230F235C00D8D69B /* NestedChoiceArrayTest.swift */; }; B5F74472233F74E400BBDB15 /* RootLevelAttributeTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F74471233F74E400BBDB15 /* RootLevelAttributeTest.swift */; }; OBJ_148 /* BoolBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* BoolBox.swift */; }; @@ -154,6 +155,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 4A062D4E2341924E009BCAC1 /* CombineTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CombineTests.swift; sourceTree = ""; }; B5EA3BB4230F235C00D8D69B /* NestedChoiceArrayTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NestedChoiceArrayTest.swift; sourceTree = ""; }; B5F74471233F74E400BBDB15 /* RootLevelAttributeTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootLevelAttributeTest.swift; sourceTree = ""; }; OBJ_100 /* DecimalTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecimalTests.swift; sourceTree = ""; }; @@ -428,6 +430,7 @@ OBJ_86 /* CDCatalog.swift */, OBJ_87 /* CDTest.swift */, OBJ_88 /* ClassTests.swift */, + 4A062D4E2341924E009BCAC1 /* CombineTests.swift */, OBJ_89 /* CompositeChoiceTests.swift */, OBJ_90 /* DecodingContainerTests.swift */, OBJ_91 /* DynamicNodeDecodingTest.swift */, @@ -742,6 +745,7 @@ OBJ_269 /* RJITest.swift in Sources */, OBJ_270 /* RelationshipsTest.swift in Sources */, OBJ_271 /* SimpleChoiceTests.swift in Sources */, + 4A062D4F2341924E009BCAC1 /* CombineTests.swift in Sources */, OBJ_272 /* SingleChildTests.swift in Sources */, OBJ_273 /* SpacePreserveTest.swift in Sources */, );