├── ResponseDetective ├── include │ ├── ResponseDetective.h │ ├── RDTBodyDeserializer.h │ ├── RDTHTMLBodyDeserializer.h │ └── RDTXMLBodyDeserializer.h ├── Configuration │ ├── Tests-iOS.xcconfig │ ├── Tests-macOS.xcconfig │ ├── Tests-tvOS.xcconfig │ ├── Framework-iOS.xcconfig │ ├── Common-Debug.xcconfig │ ├── Framework-tvOS.xcconfig │ ├── Common-Release.xcconfig │ ├── Framework-macOS.xcconfig │ ├── Vendor │ │ ├── Targets │ │ │ ├── Tests.xcconfig │ │ │ ├── Extension.xcconfig │ │ │ ├── Application.xcconfig │ │ │ └── Framework.xcconfig │ │ ├── Common │ │ │ ├── Carthage.xcconfig │ │ │ ├── Variables.xcconfig │ │ │ └── Common.xcconfig │ │ ├── Platforms │ │ │ ├── tvOS.xcconfig │ │ │ ├── macOS.xcconfig │ │ │ ├── watchOS.xcconfig │ │ │ └── iOS.xcconfig │ │ └── Configurations │ │ │ ├── Release.xcconfig │ │ │ └── Debug.xcconfig │ ├── Tests-Base.xcconfig │ ├── Common-Base.xcconfig │ └── Framework-Base.xcconfig ├── Resources │ ├── Framework.modulemap │ ├── Framework-Info.plist │ └── Tests-Info.plist ├── Sources │ ├── RDTXMLBodyDeserializer.h │ ├── RDTHTMLBodyDeserializer.h │ ├── ResponseDetective.h │ ├── PlaintextBodyDeserializer.swift │ ├── Dictionary.swift │ ├── JSONBodyDeserializer.swift │ ├── ImageBodyDeserializer.swift │ ├── RDTBodyDeserializer.h │ ├── RDTHTMLBodyDeserializer.m │ ├── RDTXMLBodyDeserializer.m │ ├── OutputFacility.swift │ ├── URLEncodedBodyDeserializer.swift │ ├── BufferOutputFacility.swift │ ├── ErrorRepresentation.swift │ ├── RequestRepresentation.swift │ ├── ResponseRepresentation.swift │ ├── ConsoleOutputFacility.swift │ ├── ResponseDetective.swift │ └── URLProtocol.swift └── Tests │ ├── Specs │ ├── PlaintextBodyDeserializerSpec.swift │ ├── XMLBodyDeserializerSpec.swift │ ├── HTMLBodyDeserializerSpec.swift │ ├── JSONBodyDeserializerSpec.swift │ ├── URLEncodedBodyDeserializerSpec.swift │ ├── ResponseRepresentationSpec.swift │ ├── BufferOutputFacilitySpec.swift │ ├── RequestRepresentationSpec.swift │ ├── ImageBodyDeserializerSpec.swift │ ├── ErrorRepresentationSpec.swift │ ├── ConsoleOutputFacilitySpec.swift │ └── ResponseDetectiveSpec.swift │ └── Additions │ └── TestBodyDeserializer.swift ├── Images └── Header.png ├── Cartfile.resolved ├── .gitattributes ├── ResponseDetective.playground ├── playground.xcworkspace │ └── contents.xcworkspacedata ├── contents.xcplayground └── Contents.swift ├── ResponseDetective.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcshareddata │ └── xcschemes │ │ ├── ResponseDetective-iOS.xcscheme │ │ ├── ResponseDetective-tvOS.xcscheme │ │ └── ResponseDetective-macOS.xcscheme └── project.pbxproj ├── Cartfile.private ├── .gitignore ├── LICENSE.md ├── carthage.sh ├── ResponseDetective.podspec ├── Package.resolved ├── bitrise.yml ├── Package.swift └── README.md /ResponseDetective/include/ResponseDetective.h: -------------------------------------------------------------------------------- 1 | ../Sources/ResponseDetective.h -------------------------------------------------------------------------------- /ResponseDetective/include/RDTBodyDeserializer.h: -------------------------------------------------------------------------------- 1 | ../Sources/RDTBodyDeserializer.h -------------------------------------------------------------------------------- /ResponseDetective/include/RDTHTMLBodyDeserializer.h: -------------------------------------------------------------------------------- 1 | ../Sources/RDTHTMLBodyDeserializer.h -------------------------------------------------------------------------------- /ResponseDetective/include/RDTXMLBodyDeserializer.h: -------------------------------------------------------------------------------- 1 | ../Sources/RDTXMLBodyDeserializer.h -------------------------------------------------------------------------------- /Images/Header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netguru/ResponseDetective/HEAD/Images/Header.png -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "AliSoftware/OHHTTPStubs" "9.1.0" 2 | github "Quick/Nimble" "v9.0.0" 3 | github "Quick/Quick" "v3.0.0" 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # .gitattributes 3 | # 4 | # Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | # Licensed under the MIT License. 6 | # 7 | 8 | .pbxproj merge=union 9 | *.strings text diff 10 | -------------------------------------------------------------------------------- /ResponseDetective.playground/playground.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ResponseDetective.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ResponseDetective.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Tests-iOS.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Tests-iOS.xcconfig 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | // Imports 9 | 10 | #include "Tests-Base.xcconfig" 11 | #include "Vendor/Platforms/iOS.xcconfig" 12 | -------------------------------------------------------------------------------- /ResponseDetective/Resources/Framework.modulemap: -------------------------------------------------------------------------------- 1 | // 2 | // Framework.modulemap 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | framework module ResponseDetective { 9 | umbrella header "ResponseDetective.h" 10 | export * 11 | } 12 | -------------------------------------------------------------------------------- /Cartfile.private: -------------------------------------------------------------------------------- 1 | # 2 | # Cartfile.private 3 | # 4 | # Copyright © 2017-2020 Netguru S.A. All rights reserved. 5 | # Licensed under the MIT License. 6 | # 7 | 8 | # Test dependencies 9 | 10 | github "Quick/Quick" ~> 3.0.0 11 | github "Quick/Nimble" ~> 9.0.0 12 | github "AliSoftware/OHHTTPStubs" ~> 9.1.0 13 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Tests-macOS.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Tests-macOS.xcconfig 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | // Imports 9 | 10 | #include "Tests-Base.xcconfig" 11 | #include "Vendor/Platforms/macOS.xcconfig" 12 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Tests-tvOS.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Tests-tvOS.xcconfig 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | // Imports 9 | 10 | #include "Tests-Base.xcconfig" 11 | #include "Vendor/Platforms/tvOS.xcconfig" 12 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Framework-iOS.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Framework-iOS.xcconfig 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | // Imports 9 | 10 | #include "Framework-Base.xcconfig" 11 | #include "Vendor/Platforms/iOS.xcconfig" 12 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Common-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Common-Debug.xcconfig 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | // Imports 9 | 10 | #include "Common-Base.xcconfig" 11 | #include "Vendor/Configurations/Debug.xcconfig" 12 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Framework-tvOS.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Framework-tvOS.xcconfig 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | // Imports 9 | 10 | #include "Framework-Base.xcconfig" 11 | #include "Vendor/Platforms/tvOS.xcconfig" 12 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Common-Release.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Common-Release.xcconfig 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | // Imports 9 | 10 | #include "Common-Base.xcconfig" 11 | #include "Vendor/Configurations/Release.xcconfig" 12 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Framework-macOS.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Framework-macOS.xcconfig 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | // Imports 9 | 10 | #include "Framework-Base.xcconfig" 11 | #include "Vendor/Platforms/macOS.xcconfig" 12 | -------------------------------------------------------------------------------- /ResponseDetective.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Vendor/Targets/Tests.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Targets/Tests.xcconfig 3 | // 4 | // Copyright © 2017 Netguru Sp. z o.o. All rights reserved. 5 | // Licensed under MIT License. 6 | // 7 | // This file contains build settings specific to test bundle targets. 8 | // 9 | 10 | // Inherit from Application target. 11 | #include "Application.xcconfig" 12 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Vendor/Targets/Extension.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Targets/Extension.xcconfig 3 | // 4 | // Copyright © 2017 Netguru Sp. z o.o. All rights reserved. 5 | // Licensed under MIT License. 6 | // 7 | // This file contains build settings specific to extension targets. 8 | // 9 | 10 | // Inherit from Application target. 11 | #include "Application.xcconfig" 12 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Vendor/Targets/Application.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Targets/Application.xcconfig 3 | // 4 | // Copyright © 2017 Netguru Sp. z o.o. All rights reserved. 5 | // Licensed under MIT License. 6 | // 7 | // This file contains build settings specific to application targets. 8 | // 9 | 10 | // MARK: Packaging 11 | 12 | // Define LLVM module. 13 | DEFINES_MODULE = YES 14 | -------------------------------------------------------------------------------- /ResponseDetective/Sources/RDTXMLBodyDeserializer.h: -------------------------------------------------------------------------------- 1 | // 2 | // RDTXMLBodyDeserializer.h 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | @import Foundation; 9 | #import "RDTBodyDeserializer.h" 10 | 11 | /// Deserializes XML bodies. 12 | NS_SWIFT_NAME(XMLBodyDeserializer) @interface RDTXMLBodyDeserializer : NSObject 13 | 14 | // empty by design 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /ResponseDetective/Sources/RDTHTMLBodyDeserializer.h: -------------------------------------------------------------------------------- 1 | // 2 | // RDTHTMLBodyDeserializer.h 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | @import Foundation; 9 | #import "RDTBodyDeserializer.h" 10 | 11 | /// Deserializes HTML bodies. 12 | NS_SWIFT_NAME(HTMLBodyDeserializer) @interface RDTHTMLBodyDeserializer : NSObject 13 | 14 | // empty by design 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Tests-Base.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Tests-Base.xcconfig 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | // Imports 9 | 10 | #include "Vendor/Targets/Application.xcconfig" 11 | 12 | // Bundle 13 | 14 | _BUNDLE_NAME = ResponseDetectiveTests 15 | _BUNDLE_IDENTIFIER = co.netguru.lib.responsedetective.tests 16 | _BUNDLE_INFOPLIST_PATH = $(SRCROOT)/ResponseDetective/Resources/Tests-Info.plist 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # .gitignore 3 | # 4 | # Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | # Licensed under the MIT License. 6 | # 7 | 8 | .DS_Store 9 | .localized 10 | 11 | xcuserdata 12 | profile 13 | build 14 | output 15 | DerivedData 16 | *.mode1v3 17 | *.mode2v3 18 | *.perspectivev3 19 | *.pbxuser 20 | *.xccheckout 21 | *.moved-aside 22 | *.hmap 23 | *.ipa 24 | *.o 25 | *.LinkFileList 26 | *.hmap 27 | *~.nib 28 | *.swp 29 | *.dat 30 | *.dep 31 | 32 | .idea 33 | *.iml 34 | 35 | Pods 36 | Carthage 37 | .build 38 | .swiftpm 39 | -------------------------------------------------------------------------------- /ResponseDetective/Sources/ResponseDetective.h: -------------------------------------------------------------------------------- 1 | // 2 | // ResponseDetective.h 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | /// Project version number for ResponseDetective. 9 | extern double ResponseDetectiveVersionNumber; 10 | 11 | /// Project version string for ResponseDetective. 12 | extern const unsigned char ResponseDetectiveVersionString[]; 13 | 14 | @import Foundation; 15 | 16 | #import "RDTBodyDeserializer.h" 17 | #import "RDTXMLBodyDeserializer.h" 18 | #import "RDTHTMLBodyDeserializer.h" 19 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Common-Base.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Common-Base.xcconfig 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | // Imports 9 | 10 | #include "Vendor/Common/Common.xcconfig" 11 | #include "Vendor/Common/Carthage.xcconfig" 12 | 13 | // MARK: Build 14 | 15 | _BUILD_VERSION = 1.5.0 16 | 17 | // MARK: Deployment 18 | 19 | _DEPLOYMENT_TARGET_IOS = 9.0 20 | _DEPLOYMENT_TARGET_MACOS = 10.10 21 | _DEPLOYMENT_TARGET_TVOS = 9.0 22 | 23 | // MARK: Compiler 24 | 25 | _COMPILER_SWIFT_VERSION = 5.2 26 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Framework-Base.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Framework-Base.xcconfig 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | // Imports 9 | 10 | #include "Vendor/Targets/Framework.xcconfig" 11 | 12 | // Bundle 13 | 14 | _BUNDLE_NAME = ResponseDetective 15 | _BUNDLE_IDENTIFIER = co.netguru.lib.responsedetective 16 | _BUNDLE_INFOPLIST_PATH = $(SRCROOT)/ResponseDetective/Resources/Framework-Info.plist 17 | 18 | // Compiler 19 | 20 | _COMPILER_OBJC_HEADER_SEARCH_PATHS = $(SDKROOT)/usr/include/libxml2 21 | _COMPILER_OBJC_LINKER_FLAGS = -lxml2 22 | -------------------------------------------------------------------------------- /ResponseDetective/Sources/PlaintextBodyDeserializer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlaintextBodyDeserializer.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | #if SWIFT_PACKAGE 10 | import ResponseDetectiveObjC 11 | #endif 12 | 13 | /// Deserializes plaintext bodies. 14 | @objc(RDTPlaintextBodyDeserializer) public final class PlaintextBodyDeserializer: NSObject, BodyDeserializer { 15 | 16 | // MARK: BodyDeserializer 17 | 18 | /// Deserializes plaintext data into a string. 19 | public func deserialize(body: Data) -> String? { 20 | return NSString(data: body, encoding: String.Encoding.utf8.rawValue) as String? 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Vendor/Common/Carthage.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Common/Carthage.xcconfig 3 | // 4 | // Copyright © 2017 Netguru Sp. z o.o. All rights reserved. 5 | // Licensed under MIT License. 6 | // 7 | // This file contains base build settings for targets using Carthage. 8 | // 9 | 10 | // Path to top-level Carthage directory. 11 | _CARTHAGE_PATH = $(PROJECT_DIR)/Carthage 12 | 13 | // Path to platform-specific Carthage/Build directory. 14 | _CARTHAGE_BUILD_PATH[sdk=iphone*] = $(_CARTHAGE_PATH)/Build/iOS 15 | _CARTHAGE_BUILD_PATH[sdk=macosx*] = $(_CARTHAGE_PATH)/Build/Mac 16 | _CARTHAGE_BUILD_PATH[sdk=appletv*] = $(_CARTHAGE_PATH)/Build/tvOS 17 | _CARTHAGE_BUILD_PATH[sdk=watch*] = $(_CARTHAGE_PATH)/Build/watchOS 18 | -------------------------------------------------------------------------------- /ResponseDetective/Tests/Specs/PlaintextBodyDeserializerSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlaintextBodyDeserializerSpec.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | import Nimble 10 | import ResponseDetective 11 | import Quick 12 | 13 | internal final class PlaintextBodyDeserializerSpec: QuickSpec { 14 | 15 | override func spec() { 16 | 17 | describe("PlaintextBodyDeserializer") { 18 | 19 | let sut = PlaintextBodyDeserializer() 20 | 21 | it("should correctly deserialize plaintext data") { 22 | let source = "foo bar\nbaz qux" 23 | let data = source.data(using: .utf8)! 24 | let expected = source 25 | expect { sut.deserialize(body: data) }.to(equal(expected)) 26 | } 27 | 28 | } 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /ResponseDetective/Sources/Dictionary.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Dictionary.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | extension Dictionary { 9 | 10 | /// Appends elements of other `dictionary` and returns a new one. 11 | /// 12 | /// - Parameters: 13 | /// - dictionary: The dictionary whose elements to append. Values 14 | /// under existing keys will replace values in `self`. 15 | /// 16 | /// - Returns: A `Dictionary` containing elements of `self` merged with 17 | /// elements of other `dictionary. 18 | internal func appending(elementsOf dictionary: [Key: Value]) -> [Key: Value] { 19 | var mutableSelf = self 20 | for (key, value) in dictionary { 21 | mutableSelf[key] = value 22 | } 23 | return mutableSelf 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Vendor/Platforms/tvOS.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Platforms/tvOS.xcconfig 3 | // 4 | // Copyright © 2017 Netguru Sp. z o.o. All rights reserved. 5 | // Licensed under MIT License. 6 | // 7 | // This file contains build settings specific to tvOS platform. 8 | // 9 | 10 | // MARK: Architecture 11 | 12 | // SDK root of tvOS. 13 | SDKROOT = appletvos 14 | 15 | // Supported platforms for tvOS. 16 | SUPPORTED_PLATFORMS = appletvos appletvsimulator 17 | 18 | // MARK: Deployment 19 | 20 | // Deployment target for tvOS. 21 | TVOS_DEPLOYMENT_TARGET = $(_DEPLOYMENT_TARGET_TVOS) 22 | 23 | // Supported device families for tvOS. 24 | TARGETED_DEVICE_FAMILY = $(_DEPLOYMENT_DEVICES_TVOS) 25 | 26 | // MARK: Linker 27 | 28 | // Where to find embedded frameworks for tvOS. 29 | LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @loader_path/Frameworks 30 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Vendor/Platforms/macOS.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Platforms/macOS.xcconfig 3 | // 4 | // Copyright © 2017 Netguru Sp. z o.o. All rights reserved. 5 | // Licensed under MIT License. 6 | // 7 | // This file contains build settings specific to macOS platform. 8 | // 9 | 10 | // MARK: Architecture 11 | 12 | // SDK root of macOS. 13 | SDKROOT = macosx 14 | 15 | // Supported platforms for macOS. 16 | SUPPORTED_PLATFORMS = macosx 17 | 18 | // MARK: Deployment 19 | 20 | // Deployment target for macOS. 21 | MACOSX_DEPLOYMENT_TARGET = $(_DEPLOYMENT_TARGET_MACOS) 22 | 23 | // MARK: Compiler 24 | 25 | // Whether to combine multiple image resolutions into a TIFF. 26 | COMBINE_HIDPI_IMAGES = YES 27 | 28 | // MARK: Linker 29 | 30 | // Where to find embedded frameworks for macOS. 31 | LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/../Frameworks @loader_path/../Frameworks 32 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Vendor/Platforms/watchOS.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Platforms/watchOS.xcconfig 3 | // 4 | // Copyright © 2017 Netguru Sp. z o.o. All rights reserved. 5 | // Licensed under MIT License. 6 | // 7 | // This file contains build settings specific to watchOS platform. 8 | // 9 | 10 | // MARK: Architecture 11 | 12 | // SDK root of tvOS. 13 | SDKROOT = watchos 14 | 15 | // Supported platforms for watchOS. 16 | SUPPORTED_PLATFORMS = watchos watchsimulator 17 | 18 | // MARK: Deployment 19 | 20 | // Deployment target for watchOS. 21 | WATCHOS_DEPLOYMENT_TARGET = $(_DEPLOYMENT_TARGET_WATCHOS) 22 | 23 | // Supported device families for watchOS. 24 | TARGETED_DEVICE_FAMILY = $(_DEPLOYMENT_DEVICES_WATCHOS) 25 | 26 | // MARK: Linker 27 | 28 | // Where to find embedded frameworks for watchOS. 29 | LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @loader_path/Frameworks 30 | -------------------------------------------------------------------------------- /ResponseDetective/Sources/JSONBodyDeserializer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSONBodyDeserializer.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | #if SWIFT_PACKAGE 10 | import ResponseDetectiveObjC 11 | #endif 12 | 13 | /// Deserializes JSON bodies. 14 | @objc(RDTJSONBodyDeserializer) public final class JSONBodyDeserializer: NSObject, BodyDeserializer { 15 | 16 | // MARK: BodyDeserializer 17 | 18 | /// Deserializes JSON data into a pretty-printed string. 19 | public func deserialize(body: Data) -> String? { 20 | do { 21 | let object = try JSONSerialization.jsonObject(with: body, options: []) 22 | let data = try JSONSerialization.data(withJSONObject: object, options: [.prettyPrinted]) 23 | return String(data: data, encoding: .utf8) 24 | } catch { 25 | return nil 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /ResponseDetective/Resources/Framework-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en_US 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | $(PRODUCT_BUNDLE_VERSION_STRING) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(PRODUCT_BUNDLE_VERSION) 23 | 24 | 25 | -------------------------------------------------------------------------------- /ResponseDetective/Resources/Tests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en_US 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | $(PRODUCT_BUNDLE_VERSION_STRING) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(PRODUCT_BUNDLE_VERSION) 23 | 24 | 25 | -------------------------------------------------------------------------------- /ResponseDetective/Tests/Specs/XMLBodyDeserializerSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XMLBodyDeserializerSpec.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | import Nimble 10 | import ResponseDetective 11 | #if SWIFT_PACKAGE 12 | import ResponseDetectiveObjC 13 | #endif 14 | import Quick 15 | 16 | internal final class XMLBodyDeserializerSpec: QuickSpec { 17 | 18 | override func spec() { 19 | 20 | describe("XMLBodyDeserializer") { 21 | 22 | let sut = XMLBodyDeserializer() 23 | 24 | it("should correctly deserialize XML data") { 25 | let source = "" 26 | let data = source.data(using: .utf8)! 27 | let expected = "\n\n \n" 28 | expect { sut.deserialize(body: data) }.to(equal(expected)) 29 | } 30 | 31 | } 32 | 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Vendor/Targets/Framework.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Targets/Framework.xcconfig 3 | // 4 | // Copyright © 2017 Netguru Sp. z o.o. All rights reserved. 5 | // Licensed under MIT License. 6 | // 7 | // This file contains build settings specific to framework targets. 8 | // 9 | 10 | // MARK: Build options 11 | 12 | // Disallow use of APIs that are not available to app extensions and linking to 13 | // frameworks that have not been built with this setting enabled. 14 | APPLICATION_EXTENSION_API_ONLY = YES 15 | 16 | // MARK: Deployment 17 | 18 | // Install in run path. 19 | INSTALL_PATH = @rpath 20 | 21 | // Skip installation. 22 | SKIP_INSTALL = YES 23 | 24 | // MARK: Linking 25 | 26 | // Enable framework to be included from any location as long as the run path 27 | // includes it. 28 | LD_DYLIB_INSTALL_NAME = @rpath/$(PRODUCT_NAME).$(WRAPPER_EXTENSION)/$(PRODUCT_NAME) 29 | 30 | // MARK: Packaging 31 | 32 | // Define LLVM module. 33 | DEFINES_MODULE = YES 34 | -------------------------------------------------------------------------------- /ResponseDetective/Sources/ImageBodyDeserializer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageBodyDeserializer.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | #if os(iOS) || os(tvOS) || os(watchOS) 9 | import UIKit 10 | #elseif os(OSX) 11 | import AppKit 12 | #endif 13 | #if SWIFT_PACKAGE 14 | import ResponseDetectiveObjC 15 | #endif 16 | 17 | /// Deserializes image bodies. 18 | @objc(RDTImageBodyDeserializer) public final class ImageBodyDeserializer: NSObject, BodyDeserializer { 19 | 20 | // MARK: Nested types 21 | 22 | #if os(iOS) || os(tvOS) || os(watchOS) 23 | private typealias Image = UIImage 24 | #elseif os(OSX) 25 | private typealias Image = NSImage 26 | #endif 27 | 28 | // MARK: BodyDeserializer 29 | 30 | /// Deserializes image data into a pretty-printed string. 31 | public func deserialize(body: Data) -> String? { 32 | return Image(data: body).map { "\(Int($0.size.width))px × \(Int($0.size.height))px image" } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /ResponseDetective/Sources/RDTBodyDeserializer.h: -------------------------------------------------------------------------------- 1 | // 2 | // RDTBodyDeserializer.h 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | // This has been implemented as native Objective-C protocol instead of a Swift 8 | // protocol bridged to Objective-C because Swift protocol definition cannot be 9 | // imported in Objective-C header since it causes include cycle in the umbrella 10 | // header. 11 | 12 | @import Foundation; 13 | 14 | /// Represents a body deserializer which is able to deserialize raw body data 15 | /// into a human-readable string which will be logged as the request's or 16 | /// response's body. 17 | NS_SWIFT_NAME(BodyDeserializer) @protocol RDTBodyDeserializer 18 | 19 | @required 20 | 21 | /// Deserializes the body. 22 | /// 23 | /// @param body The HTTP body. 24 | /// 25 | /// @return A deserialized representation of the body. 26 | - (nullable NSString *)deserializeBody:(nonnull NSData *)body NS_SWIFT_NAME(deserialize(body:)); 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /ResponseDetective/Tests/Specs/HTMLBodyDeserializerSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HTMLBodyDeserializerSpec.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | import Nimble 10 | import ResponseDetective 11 | #if SWIFT_PACKAGE 12 | import ResponseDetectiveObjC 13 | #endif 14 | import Quick 15 | 16 | internal final class HTMLBodyDeserializerSpec: QuickSpec { 17 | 18 | override func spec() { 19 | 20 | describe("HTMLBodyDeserializer") { 21 | 22 | let sut = HTMLBodyDeserializer() 23 | 24 | it("should correctly deserialize HTML data") { 25 | let source = "

lorem
ipsum

" 26 | let data = source.data(using: .utf8)! 27 | let expected = "\n\n\n

lorem
ipsum

\n" 28 | expect { sut.deserialize(body: data) }.to(equal(expected)) 29 | } 30 | 31 | } 32 | 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Vendor/Platforms/iOS.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Platforms/iOS.xcconfig 3 | // 4 | // Copyright © 2017 Netguru Sp. z o.o. All rights reserved. 5 | // Licensed under MIT License. 6 | // 7 | // This file contains build settings specific to iOS platform. 8 | // 9 | 10 | // MARK: Architecture 11 | 12 | // SDK root of iOS. 13 | SDKROOT = iphoneos 14 | 15 | // Supported platforms for iOS. 16 | SUPPORTED_PLATFORMS = iphoneos iphonesimulator 17 | 18 | // MARK: Deployment 19 | 20 | // Deployment target for iOS. 21 | IPHONEOS_DEPLOYMENT_TARGET = $(_DEPLOYMENT_TARGET_IOS) 22 | 23 | // Supported device families for iOS. 24 | TARGETED_DEVICE_FAMILY = $(_DEPLOYMENT_DEVICES_IOS) 25 | 26 | // MARK: Linking 27 | 28 | // Where to find embedded frameworks for iOS. 29 | LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @loader_path/Frameworks 30 | 31 | // MARK: Search paths 32 | 33 | // Xcode needs this to find archived headers if `SKIP_INSTALL` is set. 34 | HEADER_SEARCH_PATHS = $(inherited) $(OBJROOT)/UninstalledProducts/include 35 | -------------------------------------------------------------------------------- /ResponseDetective/Sources/RDTHTMLBodyDeserializer.m: -------------------------------------------------------------------------------- 1 | // 2 | // RDTHTMLBodyDeserializer.m 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | #import 9 | #import "RDTHTMLBodyDeserializer.h" 10 | 11 | @implementation RDTHTMLBodyDeserializer 12 | 13 | // MARK: RDTBodyDeserializer 14 | 15 | - (nullable NSString *)deserializeBody:(nonnull NSData *)body { 16 | NSString *string = [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]; 17 | const char *memory = string.UTF8String; 18 | htmlDocPtr document = htmlReadMemory(memory, ((int)(strlen(memory))), NULL, NULL, HTML_PARSE_NOBLANKS); 19 | xmlChar *buffer = NULL; 20 | int bufferLength = 0; 21 | htmlDocDumpMemoryFormat(document, &buffer, &bufferLength, 1); 22 | NSString *result = [[NSString alloc] initWithBytes:buffer length:bufferLength encoding:NSUTF8StringEncoding]; 23 | xmlFree(buffer); 24 | return [result stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /ResponseDetective/Tests/Specs/JSONBodyDeserializerSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSONBodyDeserializerSpec.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | import Nimble 10 | import ResponseDetective 11 | import Quick 12 | 13 | internal final class JSONBodyDeserializerSpec: QuickSpec { 14 | 15 | override func spec() { 16 | 17 | describe("JSONBodyDeserializer") { 18 | 19 | let sut = JSONBodyDeserializer() 20 | 21 | it("should correctly deserialize JSON data") { 22 | let source: [String: Any] = ["foo": "", "bar": 0, "baz": false, "qux": [AnyObject](), "corge": [String: Any]()] 23 | let data = try! JSONSerialization.data(withJSONObject: source, options: []) 24 | let actualData = sut.deserialize(body: data)!.data(using: .utf8)! 25 | let actualJSON = try! JSONSerialization.jsonObject(with: actualData, options: []) as! [String: Any] 26 | 27 | expect { (actualJSON as NSDictionary) }.to(equal(source as NSDictionary)) 28 | } 29 | 30 | } 31 | 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /ResponseDetective.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResponseDetective.playground 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | import PlaygroundSupport 10 | import ResponseDetective 11 | 12 | // Enable indefinite execution so that requests complete before the playground 13 | // is killed. 14 | 15 | PlaygroundPage.current.needsIndefiniteExecution = true 16 | 17 | //: --- 18 | 19 | // Start by creating a session configuration that includes ResponseDetective's 20 | // interception mechanisms. 21 | 22 | let configuration = URLSessionConfiguration.default 23 | ResponseDetective.enable(inConfiguration: configuration) 24 | 25 | // And use the above configuration to create your session instance. 26 | 27 | let session = URLSession(configuration: configuration) 28 | 29 | //: --- 30 | 31 | // Now, just create a request and resume its session data task. 32 | 33 | let request = URLRequest(url: URL(string: "https://httpbin.org/get")!) 34 | session.dataTask(with: request).resume() 35 | 36 | // Watch the console! 🎉 37 | -------------------------------------------------------------------------------- /ResponseDetective/Sources/RDTXMLBodyDeserializer.m: -------------------------------------------------------------------------------- 1 | // 2 | // RDTXMLBodyDeserializer.m 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | #import 9 | #import 10 | #import "RDTXMLBodyDeserializer.h" 11 | 12 | @implementation RDTXMLBodyDeserializer 13 | 14 | // MARK: RDTBodyDeserializer 15 | 16 | - (nullable NSString *)deserializeBody:(nonnull NSData *)body { 17 | NSString *string = [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]; 18 | const char *memory = string.UTF8String; 19 | xmlDocPtr document = xmlReadMemory(memory, ((int)(strlen(memory))), NULL, NULL, XML_PARSE_NOCDATA | XML_PARSE_NOBLANKS); 20 | xmlChar *buffer = NULL; 21 | int bufferLength = 0; 22 | xmlDocDumpFormatMemory(document, &buffer, &bufferLength, 1); 23 | NSString *result = [[NSString alloc] initWithBytes:buffer length:bufferLength encoding:NSUTF8StringEncoding]; 24 | xmlFree(buffer); 25 | return [result stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright © 2017-2020 Netguru S.A. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ResponseDetective/Sources/OutputFacility.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OutputFacility.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Represents an output facility which is capable of displaying requests, 11 | /// responses and errors. 12 | @objc(RDTOutputFacility) public protocol OutputFacility { 13 | 14 | /// Outputs a request representation. 15 | /// 16 | /// - Parameters: 17 | /// - request: The request representation to output. 18 | @objc(outputRequestRepresentation:) func output(requestRepresentation request: RequestRepresentation) 19 | 20 | /// Outputs a response representation. 21 | /// 22 | /// - Parameters: 23 | /// - response: The response representation to output. 24 | @objc(outputResponseRepresentation:) func output(responseRepresentation response: ResponseRepresentation) 25 | 26 | /// Outputs an error representation. 27 | /// 28 | /// - Parameters: 29 | /// - error: The error representation to output. 30 | @objc(outputErrorRepresentation:) func output(errorRepresentation error: ErrorRepresentation) 31 | 32 | } 33 | -------------------------------------------------------------------------------- /carthage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Usage example: ./carthage.sh build --platform iOS 4 | # 5 | # This script fixes the issue with carthage being unable to produce fat 6 | # frameworks. This is needed until the following issue is fixed: 7 | # https://github.com/Carthage/Carthage/issues/3019 8 | 9 | # Forward inner failures. 10 | set -euo pipefail 11 | 12 | # Create a temporary xcconfig file for building the dependencies. 13 | xcconfig=$(mktemp /tmp/static.xcconfig.XXXXXX) 14 | trap 'rm -f "$xcconfig"' INT TERM HUP EXIT 15 | 16 | # For Xcode 12 make sure EXCLUDED_ARCHS is set to arm architectures otherwise 17 | # the build will fail on lipo due to duplicate architectures. 18 | echo 'EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200 = arm64 arm64e armv7 armv7s armv6 armv8' >> $xcconfig 19 | echo 'EXCLUDED_ARCHS = $(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT)__XCODE_$(XCODE_VERSION_MAJOR))' >> $xcconfig 20 | 21 | # Run carthage with the temporary xcconfig file and forwarded arguments. 22 | export XCODE_XCCONFIG_FILE="$xcconfig" 23 | carthage "$@" 24 | -------------------------------------------------------------------------------- /ResponseDetective/Sources/URLEncodedBodyDeserializer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLEncodedBodyDeserializer.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | #if SWIFT_PACKAGE 10 | import ResponseDetectiveObjC 11 | #endif 12 | 13 | /// Deserializes URL-encoded bodies. 14 | @objc(RDTURLEncodedBodyDeserializer) public final class URLEncodedBodyDeserializer: NSObject, BodyDeserializer { 15 | 16 | // MARK: BodyDeserializer 17 | 18 | /// Deserializes JSON data into a pretty-printed string. 19 | public func deserialize(body: Data) -> String? { 20 | 21 | guard let string = String(data: body, encoding: .utf8) else { 22 | return nil 23 | } 24 | 25 | if #available(macOS 10.10, *) { 26 | 27 | let stringFromQueryItems: ([URLQueryItem]) -> String = { 28 | $0.map { "\($0.name): \($0.value ?? "nil")" }.joined(separator: "\n") 29 | } 30 | 31 | if let items = URLComponents(string: string)?.queryItems { 32 | return stringFromQueryItems(items) 33 | } 34 | 35 | if let items = URLComponents(string: "?\(string)")?.queryItems { 36 | return stringFromQueryItems(items) 37 | } 38 | 39 | } 40 | 41 | return string 42 | 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Vendor/Configurations/Release.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Configurations/Release.xcconfig 3 | // 4 | // Copyright © 2017 Netguru Sp. z o.o. All rights reserved. 5 | // Licensed under MIT License. 6 | // 7 | // This file contains build settings specific to release configuration. 8 | // 9 | 10 | // MARK: Environment 11 | 12 | // Set release environments. 13 | _ENVIRONMENTS = ENV_RELEASE 14 | 15 | // MARK: Architecture 16 | 17 | // Build for all architectures. 18 | ONLY_ACTIVE_ARCH = NO 19 | 20 | // MARK: Build options 21 | 22 | // Disallow `@testable` imports. 23 | ENABLE_TESTABILITY = NO 24 | 25 | // MARK: Deployment 26 | 27 | // Strip debugging symbols when copying resources. 28 | COPY_PHASE_STRIP = YES 29 | 30 | // Strip debugging symbols when copying the built product to its final 31 | // installation location. 32 | STRIP_INSTALLED_PRODUCT = YES 33 | 34 | // MARK: LLVM compiler 35 | 36 | // Enable GCC optimization. 37 | GCC_OPTIMIZATION_LEVEL = s 38 | 39 | // MARK: Asset compiler 40 | 41 | // Optimize assets for space. 42 | ASSETCATALOG_COMPILER_OPTIMIZATION = space 43 | 44 | // MARK: Swift compiler 45 | 46 | // Enable whole-module compilation for Swift. 47 | SWIFT_COMPILATION_MODE = wholemodule 48 | 49 | // Enable whole-module optimization for Swift. 50 | SWIFT_OPTIMIZATION_LEVEL = -Owholemodule 51 | -------------------------------------------------------------------------------- /ResponseDetective.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # ResponseDetective.podspec 3 | # 4 | # Copyright © 2017-2020 Netguru S.A. All rights reserved. 5 | # Licensed under the MIT License. 6 | # 7 | 8 | Pod::Spec.new do |spec| 9 | 10 | # Description 11 | 12 | spec.name = 'ResponseDetective' 13 | spec.version = '1.5.0' 14 | spec.summary = 'Sherlock Holmes of the networking layer' 15 | spec.homepage = 'https://github.com/netguru/ResponseDetective' 16 | 17 | # License 18 | 19 | spec.license = { 20 | type: 'MIT', 21 | file: 'LICENSE.md' 22 | } 23 | 24 | spec.authors = { 25 | 'Adrian Kashivskyy' => 'adrian.kashivskyy@netguru.co', 26 | 'Aleksander Popko' => 'aleksander.popko@netguru.co' 27 | } 28 | 29 | # Source 30 | 31 | spec.source = { 32 | git: 'https://github.com/netguru/ResponseDetective.git', 33 | tag: spec.version.to_s 34 | } 35 | 36 | spec.source_files = 'ResponseDetective/Sources' 37 | spec.module_map = 'ResponseDetective/Resources/Framework.modulemap' 38 | 39 | # Linking 40 | 41 | spec.frameworks = 'Foundation' 42 | spec.libraries = 'xml2' 43 | 44 | spec.ios.frameworks = 'UIKit' 45 | spec.osx.frameworks = 'AppKit' 46 | 47 | # Settings 48 | 49 | spec.swift_version = '5.2' 50 | 51 | spec.requires_arc = true 52 | 53 | spec.ios.deployment_target = '8.0' 54 | spec.osx.deployment_target = '10.10' 55 | spec.tvos.deployment_target = '9.0' 56 | 57 | spec.pod_target_xcconfig = { 58 | 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2' 59 | } 60 | 61 | end 62 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Vendor/Configurations/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Configurations/Debug.xcconfig 3 | // 4 | // Copyright © 2017 Netguru Sp. z o.o. All rights reserved. 5 | // Licensed under MIT License. 6 | // 7 | // This file contains build settings specific to debug configuration. 8 | // 9 | 10 | // MARK: Environment 11 | 12 | // Set debug environments. 13 | _ENVIRONMENTS = ENV_DEBUG 14 | 15 | // MARK: Architecture 16 | 17 | // Build only the active architecture. 18 | ONLY_ACTIVE_ARCH = YES 19 | 20 | // MARK: Build options 21 | 22 | // Allow `@testable` imports. 23 | ENABLE_TESTABILITY = $(_BUNDLE_TESTABLE) 24 | 25 | // MARK: Deployment 26 | 27 | // Do not strip debugging symbols when copying resources. 28 | COPY_PHASE_STRIP = NO 29 | 30 | // Do not strip debugging symbols when copying the built product to its final 31 | // installation location. 32 | STRIP_INSTALLED_PRODUCT = NO 33 | 34 | // MARK: Signing 35 | 36 | // Disable Developer ID timestamping. 37 | OTHER_CODE_SIGN_FLAGS = --timestamp=none 38 | 39 | // MARK: LLVM compiler 40 | 41 | // Disable GCC optimization. 42 | GCC_OPTIMIZATION_LEVEL = 0 43 | 44 | // Catch errors in integer arithmetic. 45 | OTHER_CFLAGS = -ftrapv 46 | 47 | // MARK: Asset compiler 48 | 49 | // Optimize assets for time. 50 | ASSETCATALOG_COMPILER_OPTIMIZATION = time 51 | 52 | // MARK: Swift compiler 53 | 54 | // Disable whole-module compilation for Swift. 55 | SWIFT_COMPILATION_MODE = singlefile 56 | 57 | // Disable optimizations for Swift. 58 | SWIFT_OPTIMIZATION_LEVEL = -Onone 59 | -------------------------------------------------------------------------------- /ResponseDetective/Tests/Additions/TestBodyDeserializer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestBodyDeserializer.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import ResponseDetective 9 | #if SWIFT_PACKAGE 10 | import ResponseDetectiveObjC 11 | #endif 12 | 13 | /// A test body deserializer. 14 | internal final class TestBodyDeserializer: NSObject, BodyDeserializer { 15 | 16 | /// The closure for deserializing bodies. 17 | let deserializationClosure: @convention(block) (Data) -> String? 18 | 19 | /// Creates a general deserializer with given deserialization closure. 20 | /// 21 | /// - Parameter deserializationClosure: Implementation of `deserializeBody`. 22 | internal init(deserializationClosure: @escaping @convention(block) (Data) -> String?) { 23 | self.deserializationClosure = deserializationClosure 24 | } 25 | 26 | /// Creates a deserializer with fixed deserialized body. 27 | /// 28 | /// - Parameter fixedDeserializedBody: A fixed deserialized body. 29 | internal convenience init(fixedDeserializedBody: String?) { 30 | self.init { _ in fixedDeserializedBody } 31 | } 32 | 33 | /// Creates a deserializer which deserializes data into plaintext. 34 | internal convenience override init() { 35 | self.init { String(data: $0, encoding: .utf8) as String? } 36 | } 37 | 38 | // MARK: Implementation 39 | 40 | /// - SeeAlso: BodyDeserializer.deserializeBody(_:) 41 | internal func deserialize(body data: Data) -> String? { 42 | return deserializationClosure(data) 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /ResponseDetective/Tests/Specs/URLEncodedBodyDeserializerSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLEncodedBodyDeserializerSpec.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | import Nimble 10 | import ResponseDetective 11 | import Quick 12 | 13 | internal final class URLEncodedBodyDeserializerSpec: QuickSpec { 14 | 15 | override func spec() { 16 | 17 | describe("URLEncodedBodyDeserializer") { 18 | 19 | let sut = URLEncodedBodyDeserializer() 20 | 21 | it("should correctly deserialize URL-encoded data") { 22 | 23 | if #available(macOS 10.10, *) { 24 | 25 | let items = [URLQueryItem(name: "foo", value: nil), URLQueryItem(name: "bar", value: "baz")] 26 | let components = URLComponents(queryItems: items) 27 | let data = components.url!.relativeString.data(using: .utf8)! 28 | 29 | let actualString = sut.deserialize(body: data) 30 | let expectedString = "foo: nil\nbar: baz" 31 | 32 | expect(actualString).to(equal(expectedString)) 33 | 34 | } else { 35 | 36 | let expectedString = "foo&bar=baz" 37 | let actualString = sut.deserialize(body: expectedString.data(using: .utf8)!) 38 | 39 | expect(actualString).to(equal(expectedString)) 40 | 41 | } 42 | 43 | } 44 | 45 | } 46 | 47 | } 48 | 49 | } 50 | 51 | // MARK: - 52 | 53 | @available(macOS 10.10, *) extension URLComponents { 54 | 55 | fileprivate init(queryItems: [URLQueryItem]) { 56 | self.init() 57 | self.queryItems = queryItems 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "CwlCatchException", 6 | "repositoryURL": "https://github.com/mattgallagher/CwlCatchException.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "682841464136f8c66e04afe5dbd01ab51a3a56f2", 10 | "version": "2.1.0" 11 | } 12 | }, 13 | { 14 | "package": "CwlPreconditionTesting", 15 | "repositoryURL": "https://github.com/mattgallagher/CwlPreconditionTesting.git", 16 | "state": { 17 | "branch": null, 18 | "revision": "02b7a39a99c4da27abe03cab2053a9034379639f", 19 | "version": "2.0.0" 20 | } 21 | }, 22 | { 23 | "package": "Nimble", 24 | "repositoryURL": "https://github.com/Quick/Nimble.git", 25 | "state": { 26 | "branch": null, 27 | "revision": "e491a6731307bb23783bf664d003be9b2fa59ab5", 28 | "version": "9.0.0" 29 | } 30 | }, 31 | { 32 | "package": "OHHTTPStubs", 33 | "repositoryURL": "https://github.com/AliSoftware/OHHTTPStubs.git", 34 | "state": { 35 | "branch": null, 36 | "revision": "12f19662426d0434d6c330c6974d53e2eb10ecd9", 37 | "version": "9.1.0" 38 | } 39 | }, 40 | { 41 | "package": "Quick", 42 | "repositoryURL": "https://github.com/Quick/Quick.git", 43 | "state": { 44 | "branch": null, 45 | "revision": "0038bcbab4292f3b028632556507c124e5ba69f3", 46 | "version": "3.0.0" 47 | } 48 | } 49 | ] 50 | }, 51 | "version": 1 52 | } 53 | -------------------------------------------------------------------------------- /ResponseDetective/Sources/BufferOutputFacility.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BufferOutputFacility.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | 10 | /// An output facility that adds received representations to array buffers. 11 | @objc(RDTBufferOutputFacility) public final class BufferOutputFacility: NSObject, OutputFacility { 12 | 13 | // MARK: Properties 14 | 15 | /// A buffer of request representations. 16 | public private(set) var requestRepresentations: [RequestRepresentation] = [] 17 | 18 | /// A buffer of request representations. 19 | public private(set) var responseRepresentations: [ResponseRepresentation] = [] 20 | 21 | /// A buffer of request representations. 22 | public private(set) var errorRepresentations: [ErrorRepresentation] = [] 23 | 24 | // MARK: OutputFacility 25 | 26 | /// Adds the request representation to the buffer. 27 | /// 28 | /// - SeeAlso: OutputFacility.output(requestRepresentation:) 29 | public func output(requestRepresentation request: RequestRepresentation) { 30 | requestRepresentations.append(request) 31 | } 32 | 33 | /// Adds the response representation to the buffer. 34 | /// 35 | /// - SeeAlso: OutputFacility.output(responseRepresentation:) 36 | public func output(responseRepresentation response: ResponseRepresentation) { 37 | responseRepresentations.append(response) 38 | } 39 | 40 | /// Adds the error representation to the buffer. 41 | /// 42 | /// - SeeAlso: OutputFacility.output(errorRepresentation:) 43 | public func output(errorRepresentation error: ErrorRepresentation) { 44 | errorRepresentations.append(error) 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /ResponseDetective/Tests/Specs/ResponseRepresentationSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResponseRepresentationSpec.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | import Nimble 10 | import ResponseDetective 11 | import Quick 12 | 13 | internal final class ResponseRepresentationSpec: QuickSpec { 14 | 15 | override func spec() { 16 | 17 | describe("ResponseRepresentation") { 18 | 19 | context("after initializing with a response") { 20 | 21 | let fixtureResponse = HTTPURLResponse( 22 | url: URL(string: "https://httpbin.org/post")!, 23 | statusCode: 200, 24 | httpVersion: nil, 25 | headerFields: [ 26 | "Content-Type": "application/json", 27 | "X-Foo": "bar" 28 | ] 29 | )! 30 | 31 | let fixtureBody = try! JSONSerialization.data(withJSONObject: ["foo": "bar"], options: []) 32 | let fixtureIdentifier = "1" 33 | 34 | var sut: ResponseRepresentation! 35 | 36 | beforeEach { 37 | sut = ResponseRepresentation(requestIdentifier: fixtureIdentifier, response: fixtureResponse, body: fixtureBody, deserializedBody: nil) 38 | } 39 | 40 | it("should have a correct identifier") { 41 | expect(sut.requestIdentifier).to(equal(fixtureIdentifier)) 42 | } 43 | 44 | it("should have a correct URLString") { 45 | expect(sut.urlString).to(equal(fixtureResponse.url!.absoluteString)) 46 | } 47 | 48 | it("should have a correct method") { 49 | expect(sut.statusCode).to(equal(fixtureResponse.statusCode)) 50 | } 51 | 52 | it("should have correct headers") { 53 | expect(sut.headers).to(equal(fixtureResponse.allHeaderFields as? [String: String])) 54 | } 55 | 56 | it("should have a correct body") { 57 | expect(sut.body).to(equal(fixtureBody)) 58 | } 59 | 60 | } 61 | 62 | } 63 | 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /ResponseDetective/Tests/Specs/BufferOutputFacilitySpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BufferOutputFacilitySpec.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | import Nimble 10 | import ResponseDetective 11 | import Quick 12 | 13 | internal final class BufferOutputFacilitySpec: QuickSpec { 14 | 15 | override func spec() { 16 | 17 | describe("BufferOutputFacility") { 18 | 19 | let sut = BufferOutputFacility() 20 | 21 | it("should buffer request representations") { 22 | let request = RequestRepresentation( 23 | identifier: "0", 24 | method: "GET", 25 | urlString: "http://foo.bar", 26 | headers: [ 27 | "X-Foo": "bar", 28 | "X-Baz": "qux", 29 | ], 30 | body: nil, 31 | deserializedBody: "lorem ipsum" 32 | ) 33 | sut.output(requestRepresentation: request) 34 | expect(sut.requestRepresentations.count).to(beGreaterThanOrEqualTo(1)) 35 | } 36 | 37 | it("should buffer response representation") { 38 | let response = ResponseRepresentation( 39 | requestIdentifier: "0", 40 | statusCode: 200, 41 | urlString: "http://foo.bar", 42 | headers: [ 43 | "X-Bar": "foo", 44 | "X-Qux": "baz", 45 | ], 46 | body: nil, 47 | deserializedBody: "dolor sit amet" 48 | ) 49 | sut.output(responseRepresentation: response) 50 | expect(sut.responseRepresentations.count).to(beGreaterThanOrEqualTo(1)) 51 | } 52 | 53 | it("should buffer error representations") { 54 | let error = ErrorRepresentation( 55 | requestIdentifier: "0", 56 | response: nil, 57 | domain: "foo.bar.error", 58 | code: 1234, 59 | reason: "just because", 60 | userInfo: [ 61 | "foo": "bar", 62 | "baz": "qux", 63 | ] 64 | ) 65 | sut.output(errorRepresentation: error) 66 | expect(sut.errorRepresentations.count).to(beGreaterThanOrEqualTo(1)) 67 | } 68 | 69 | } 70 | 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /ResponseDetective/Tests/Specs/RequestRepresentationSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestRepresentationSpec.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | import Nimble 10 | import ResponseDetective 11 | import Quick 12 | 13 | internal final class RequestRepresentationSpec: QuickSpec { 14 | 15 | override func spec() { 16 | 17 | describe("RequestRepresentation") { 18 | 19 | context("after initializing with a request") { 20 | 21 | let fixtureRequest = NSMutableURLRequest( 22 | URL: URL(string: "https://httpbin.org/post")!, 23 | HTTPMethod: "POST", 24 | headerFields: [ 25 | "Content-Type": "application/json", 26 | "X-Foo": "bar", 27 | ], 28 | HTTPBody: try! JSONSerialization.data(withJSONObject: ["foo": "bar"], options: []) 29 | ) 30 | 31 | let fixtureIdentifier = "1" 32 | 33 | var sut: RequestRepresentation! 34 | 35 | beforeEach { 36 | sut = RequestRepresentation(identifier: fixtureIdentifier, request: fixtureRequest as URLRequest, deserializedBody: nil) 37 | } 38 | 39 | it("should have a correct identifier") { 40 | expect(sut.identifier).to(equal(fixtureIdentifier)) 41 | } 42 | 43 | it("should have a correct URLString") { 44 | expect(sut.urlString).to(equal(fixtureRequest.url!.absoluteString)) 45 | } 46 | 47 | it("should have a correct method") { 48 | expect(sut.method).to(equal(fixtureRequest.httpMethod)) 49 | } 50 | 51 | it("should have correct headers") { 52 | expect(sut.headers).to(equal(fixtureRequest.allHTTPHeaderFields)) 53 | } 54 | 55 | it("should have a correct body") { 56 | expect(sut.body).to(equal(fixtureRequest.httpBody)) 57 | } 58 | 59 | } 60 | 61 | } 62 | 63 | } 64 | 65 | } 66 | 67 | private extension NSMutableURLRequest { 68 | 69 | convenience init(URL: Foundation.URL, HTTPMethod: String, headerFields: [String: String], HTTPBody: Data?) { 70 | self.init(url: URL) 71 | self.httpMethod = HTTPMethod 72 | self.allHTTPHeaderFields = headerFields 73 | self.httpBody = HTTPBody 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /ResponseDetective/Tests/Specs/ImageBodyDeserializerSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageBodyDeserializerSpec.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | import Nimble 10 | import ResponseDetective 11 | import Quick 12 | 13 | internal final class ImageBodyDeserializerSpec: QuickSpec { 14 | 15 | override func spec() { 16 | 17 | describe("ImageBodyDeserializer") { 18 | 19 | let sut = ImageBodyDeserializer() 20 | 21 | it("should correctly deserialize image data") { 22 | let source = swatchImage(size: (width: 100, height: 200)) 23 | let data = imageData(image: source) 24 | let expected = "100px × 200px image" 25 | expect { sut.deserialize(body: data) }.to(equal(expected)) 26 | } 27 | 28 | } 29 | 30 | } 31 | 32 | } 33 | 34 | // MARK: - 35 | 36 | #if os(iOS) || os(tvOS) || os(watchOS) 37 | import UIKit 38 | private typealias Image = UIImage 39 | #elseif os(OSX) 40 | import AppKit 41 | private typealias Image = NSImage 42 | #endif 43 | 44 | private func swatchImage(size: (width: Int, height: Int)) -> Image { 45 | #if os(iOS) || os(tvOS) || os(watchOS) 46 | let rect = CGRect(x: 0, y: 0, width: CGFloat(size.width), height: CGFloat(size.height)) 47 | UIGraphicsBeginImageContext(rect.size) 48 | let context = UIGraphicsGetCurrentContext()! 49 | context.setFillColor(UIColor.black.cgColor) 50 | context.fill(rect) 51 | let image = UIGraphicsGetImageFromCurrentImageContext()! 52 | UIGraphicsEndImageContext() 53 | return image.withRenderingMode(.alwaysOriginal) 54 | #elseif os(OSX) 55 | let rect = NSMakeRect(0, 0, CGFloat(size.width), CGFloat(size.height)) 56 | let image = NSImage(size: rect.size) 57 | image.lockFocus() 58 | NSColor.black.drawSwatch(in: rect) 59 | image.unlockFocus() 60 | return image 61 | #endif 62 | } 63 | 64 | private func imageData(image: Image) -> Data { 65 | #if os(iOS) || os(tvOS) || os(watchOS) 66 | return image.jpegData(compressionQuality: 1)! 67 | #elseif os(OSX) 68 | return NSBitmapImageRep(data: image.tiffRepresentation!)!.representation(using: .jpeg, properties: [.compressionFactor: 1])! 69 | #endif 70 | } 71 | -------------------------------------------------------------------------------- /ResponseDetective/Tests/Specs/ErrorRepresentationSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorRepresentationSpec.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | import Nimble 10 | import ResponseDetective 11 | import Quick 12 | 13 | internal final class ErrorRepresentationSpec: QuickSpec { 14 | 15 | override func spec() { 16 | 17 | describe("ErrorRepresentation") { 18 | 19 | context("after initializing with an error") { 20 | 21 | let fixtureIdentifier = "1" 22 | 23 | let fixtureError = NSError(domain: NSURLErrorDomain, code: NSURLErrorNotConnectedToInternet, userInfo: [ 24 | NSLocalizedDescriptionKey: "The connection failed because the device is not connected to the internet.", 25 | NSURLErrorKey: NSURL(string: "https://httpbin.org/post")! 26 | ]) 27 | 28 | let fixtureResponse = ResponseRepresentation( 29 | requestIdentifier: fixtureIdentifier, 30 | response: HTTPURLResponse( 31 | url: URL(string: "https://httpbin.org/post")!, 32 | statusCode: 200, 33 | httpVersion: nil, 34 | headerFields: [ 35 | "Content-Type": "application/json", 36 | "X-Foo": "bar" 37 | ] 38 | )!, 39 | body: nil, 40 | deserializedBody: nil 41 | ) 42 | 43 | var sut: ErrorRepresentation! 44 | 45 | beforeEach { 46 | sut = ErrorRepresentation(requestIdentifier: fixtureIdentifier, error: fixtureError, response: fixtureResponse) 47 | } 48 | 49 | it("should have a correct identifier") { 50 | expect(sut.requestIdentifier).to(equal(fixtureIdentifier)) 51 | } 52 | 53 | it("should have a correct response") { 54 | expect(sut.response).to(beIdenticalTo(fixtureResponse)) 55 | } 56 | 57 | it("should have a correct domain") { 58 | expect(sut.domain).to(equal(fixtureError.domain)) 59 | } 60 | 61 | it("should have a correct code") { 62 | expect(sut.code).to(equal(fixtureError.code)) 63 | } 64 | 65 | it("should have a correct reason") { 66 | expect(sut.reason).to(equal(fixtureError.localizedDescription)) 67 | } 68 | 69 | it("should have correct user info") { 70 | expect(sut.userInfo as? [String: NSObject]).to(equal(fixtureError.userInfo as? [String: NSObject])) 71 | } 72 | 73 | } 74 | 75 | } 76 | 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /ResponseDetective/Tests/Specs/ConsoleOutputFacilitySpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConsoleOutputFacilitySpec.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | import Nimble 10 | import ResponseDetective 11 | import Quick 12 | 13 | internal final class ConsoleOutputFacilitySpec: QuickSpec { 14 | 15 | override func spec() { 16 | 17 | describe("ConsoleOutputFacility") { 18 | 19 | var buffer = [String]() 20 | let sut = ConsoleOutputFacility(printClosure: { buffer.append($0) }) 21 | 22 | it("should produce correct request output") { 23 | let request = RequestRepresentation( 24 | identifier: "0", 25 | method: "GET", 26 | urlString: "http://foo.bar", 27 | headers: ["X-Foo": "bar"], 28 | body: nil, 29 | deserializedBody: "lorem ipsum" 30 | ) 31 | let expected = "<0> [REQUEST] GET http://foo.bar\n" + 32 | " ├─ Headers\n" + 33 | " │ X-Foo: bar\n" + 34 | " ├─ Body\n" + 35 | " │ lorem ipsum\n" 36 | sut.output(requestRepresentation: request) 37 | expect(buffer.last).to(equal(expected)) 38 | } 39 | 40 | it("should produce correct response output") { 41 | let response = ResponseRepresentation( 42 | requestIdentifier: "0", 43 | statusCode: 200, 44 | urlString: "http://foo.bar", 45 | headers: ["X-Bar": "foo"], 46 | body: nil, 47 | deserializedBody: "dolor sit amet" 48 | ) 49 | let expected = "<0> [RESPONSE] 200 (NO ERROR) http://foo.bar\n" + 50 | " ├─ Headers\n" + 51 | " │ X-Bar: foo\n" + 52 | " ├─ Body\n" + 53 | " │ dolor sit amet\n" 54 | sut.output(responseRepresentation: response) 55 | expect(buffer.last).to(equal(expected)) 56 | } 57 | 58 | it("should produce correct error ourput") { 59 | let error = ErrorRepresentation( 60 | requestIdentifier: "0", 61 | response: nil, 62 | domain: "foo.bar.error", 63 | code: 1234, 64 | reason: "just because", 65 | userInfo: ["foo": "bar"] 66 | ) 67 | let expected = "<0> [ERROR] foo.bar.error 1234\n" + 68 | " ├─ User Info\n" + 69 | " │ foo: bar\n" 70 | sut.output(errorRepresentation: error) 71 | expect(buffer.last).to(equal(expected)) 72 | } 73 | 74 | } 75 | 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /ResponseDetective/Sources/ErrorRepresentation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorRepresentation.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Represents an instance of `NSError`. 11 | @objc(RDTErrorRepresentation) public final class ErrorRepresentation: NSObject { 12 | 13 | // MARK: Initializers 14 | 15 | /// Initializes the receiver. 16 | /// 17 | /// - Parameters: 18 | /// - requestIdentifier: The request's unique identifier. 19 | /// - response: The HTTP URL response representation, if any. 20 | /// - domain: The error domain. 21 | /// - code: The error code. 22 | /// - reason: The error reason. 23 | /// - userInfo: The error user info. 24 | public init(requestIdentifier: String, response: ResponseRepresentation?, domain: String, code: Int, reason: String, userInfo: [String: Any]) { 25 | self.requestIdentifier = requestIdentifier 26 | self.response = response 27 | self.domain = domain 28 | self.code = code 29 | self.reason = reason 30 | self.userInfo = userInfo 31 | } 32 | 33 | /// Initializes the receiver. 34 | /// 35 | /// - Parameters: 36 | /// - requestIdentifier: The unique identifier of assocciated request. 37 | /// - error: The error that came with the response. 38 | /// - response: The HTTP URL response representation, if any. 39 | public convenience init(requestIdentifier: String, error: NSError, response: ResponseRepresentation?) { 40 | self.init( 41 | requestIdentifier: requestIdentifier, 42 | response: response, 43 | domain: error.domain, 44 | code: error.code, 45 | reason: error.localizedDescription, 46 | userInfo: error.userInfo 47 | ) 48 | } 49 | 50 | // MARK: Properties 51 | 52 | /// The request's unique identifier. 53 | public let requestIdentifier: String 54 | 55 | /// The response representation that came along with the error. 56 | public let response: ResponseRepresentation? 57 | 58 | /// The error domain. 59 | public let domain: String 60 | 61 | /// The error code. 62 | public let code: Int 63 | 64 | /// The error reason. 65 | public let reason: String 66 | 67 | /// The error user info. 68 | public let userInfo: [String: Any] 69 | 70 | // MARK: Unavailable 71 | 72 | /// An unavailable initializer. 73 | @available(*, unavailable) public override init() { 74 | fatalError("\(#function) is not implemented."); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /ResponseDetective/Sources/RequestRepresentation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestRepresentation.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Represents an instance of `URLRequest`. 11 | @objc(RDTRequestRepresentation) public final class RequestRepresentation: NSObject { 12 | 13 | // MARK: Initializers 14 | 15 | /// Initializes the receiver. 16 | /// 17 | /// - Parameters: 18 | /// - identifier: A unique identifier of the request. 19 | /// - method: he HTTP method of the request. 20 | /// - urlString: The resolved URL string of the request. 21 | /// - headers: The HTTP headers of the request. 22 | /// - body: The raw body data of the request. 23 | /// - deserializedBody: The parsed body of the request. 24 | public init(identifier: String, method: String, urlString: String, headers: [String: String], body: Data?, deserializedBody: String?) { 25 | self.identifier = identifier 26 | self.method = method 27 | self.urlString = urlString 28 | self.headers = headers 29 | self.body = body 30 | self.deserializedBody = deserializedBody 31 | } 32 | 33 | /// Initializes the receiver. 34 | /// 35 | /// - Parameters: 36 | /// - identifier: A unique identifier of the request. 37 | /// - request: The URL request instance. 38 | /// - deserializedBody: The parsed body of the request. 39 | public convenience init(identifier: String, request: URLRequest, deserializedBody: String?) { 40 | self.init( 41 | identifier: identifier, 42 | method: request.httpMethod ?? "GET", 43 | urlString: request.url?.absoluteString ?? "", 44 | headers: request.allHTTPHeaderFields ?? [:], 45 | body: request.httpBody, 46 | deserializedBody: deserializedBody 47 | ) 48 | } 49 | 50 | // MARK: Properties 51 | 52 | /// A unique identifier of the request. 53 | public let identifier: String 54 | 55 | /// The HTTP method of the request. 56 | public let method: String 57 | 58 | /// The resolved URL string of the request. 59 | public let urlString: String 60 | 61 | /// The HTTP headers of the request. 62 | public let headers: [String: String] 63 | 64 | /// The content type of the request. 65 | public var contentType: String { 66 | return headers["Content-Type"] ?? "application/octet-stream" 67 | } 68 | 69 | /// The raw body data of the request. 70 | public let body: Data? 71 | 72 | /// The parsed body of the request. 73 | public let deserializedBody: String? 74 | 75 | } 76 | -------------------------------------------------------------------------------- /ResponseDetective/Sources/ResponseRepresentation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResponseRepresentation.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Represents a pair of `HTTPURLResponse` and `Data` instances. 11 | @objc(RDTResponseRepresentation) public final class ResponseRepresentation: NSObject { 12 | 13 | // MARK: Initializers 14 | 15 | /// Initializes the receiver. 16 | /// 17 | /// - Parameters: 18 | /// - requestIdentifier: The request's unique identifier. 19 | /// - statusCode: The status code of the response. 20 | /// - urlString: The URL string of the response. 21 | /// - headers: The HTTP headers of the response. 22 | /// - body: The raw body data of the response. 23 | /// - deserializedBody: The parsed body of the response. 24 | public init(requestIdentifier: String, statusCode: Int, urlString: String, headers: [String: String], body: Data?, deserializedBody: String?) { 25 | self.requestIdentifier = requestIdentifier 26 | self.statusCode = statusCode 27 | self.urlString = urlString 28 | self.headers = headers 29 | self.body = body 30 | self.deserializedBody = deserializedBody 31 | } 32 | 33 | /// Initializes the receiver. 34 | /// 35 | /// - Parameters: 36 | /// - requestIdentifier: The unique identifier of assocciated request. 37 | /// - response: The HTTP URL response instance. 38 | /// - body: The body that came with the response. 39 | /// - deserializedBody: The deserialized response body. 40 | public convenience init(requestIdentifier: String, response: HTTPURLResponse, body: Data?, deserializedBody: String?) { 41 | self.init( 42 | requestIdentifier: requestIdentifier, 43 | statusCode: response.statusCode, 44 | urlString: response.url?.absoluteString ?? "", 45 | headers: response.allHeaderFields as? [String: String] ?? [:], 46 | body: body, 47 | deserializedBody: deserializedBody 48 | ) 49 | } 50 | 51 | // MARK: Properties 52 | 53 | /// The request's unique identifier. 54 | public let requestIdentifier: String 55 | 56 | /// The status code of the response. 57 | public let statusCode: Int 58 | 59 | /// A verbal representation of the status code. 60 | public var statusString: String { 61 | return HTTPURLResponse.localizedString(forStatusCode: statusCode) 62 | } 63 | 64 | /// The URL string of the response (which may be different than originally 65 | /// requested because of a redirect). 66 | public let urlString: String 67 | 68 | /// The HTTP headers of the response. 69 | public let headers: [String: String] 70 | 71 | /// The content type of the response. 72 | public var contentType: String { 73 | return headers["Content-Type"] ?? "application/octet-stream" 74 | } 75 | 76 | /// The raw body data of the response. 77 | public let body: Data? 78 | 79 | /// The parsed body of the response. 80 | public let deserializedBody: String? 81 | 82 | } 83 | -------------------------------------------------------------------------------- /bitrise.yml: -------------------------------------------------------------------------------- 1 | # 2 | # bitrise.yml 3 | # 4 | # Copyright © 2017-2020 Netguru S.A. All rights reserved. 5 | # Licensed under the MIT License. 6 | # 7 | 8 | # CLI metadata 9 | 10 | format_version: 1.3.0 11 | default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git 12 | 13 | # Workflow trigger map 14 | 15 | trigger_map: 16 | 17 | - pull_request_target_branch: develop 18 | workflow: build-and-test 19 | 20 | - push_branch: develop 21 | workflow: build-and-test 22 | 23 | # Environment configuration 24 | 25 | app: 26 | envs: 27 | - XCODEBUILD_PROJECT: ResponseDetective.xcodeproj 28 | - XCODEBUILD_OPTIONS: '' 29 | 30 | # Workflow declarations 31 | 32 | workflows: 33 | 34 | # Top level build workflows 35 | 36 | build-and-test: 37 | envs: 38 | - XCODEBUILD_SCHEME_IOS: ResponseDetective-iOS 39 | - XCODEBUILD_SCHEME_MACOS: ResponseDetective-macOS 40 | - XCODEBUILD_SCHEME_TVOS: ResponseDetective-tvOS 41 | before_run: 42 | - cache-pull 43 | - bootstrap-carthage 44 | - xcode-test 45 | - cocoapods-test 46 | after_run: 47 | - cache-push 48 | - deploy-artifacts 49 | 50 | # Cache workflows 51 | 52 | cache-pull: 53 | steps: 54 | - cache-pull: {} 55 | 56 | cache-push: 57 | steps: 58 | - cache-push: 59 | inputs: 60 | - cache_paths: "./Carthage -> ./Carthage/Cachefile" 61 | 62 | # Bootstrap workflows 63 | 64 | bootstrap-carthage: 65 | steps: 66 | - carthage: 67 | inputs: 68 | - carthage_command: bootstrap 69 | 70 | # Test workflows 71 | 72 | xcode-test: 73 | steps: 74 | - xcode-test: 75 | title: xcode-test-ios 76 | inputs: 77 | - project_path: $XCODEBUILD_PROJECT 78 | - scheme: $XCODEBUILD_SCHEME_IOS 79 | - xcodebuild_test_options: $XCODEBUILD_OPTIONS 80 | - generate_code_coverage_files: 'yes' 81 | - xcode-test-mac: 82 | title: xcode-test-macos 83 | inputs: 84 | - project_path: $XCODEBUILD_PROJECT 85 | - scheme: $XCODEBUILD_SCHEME_MACOS 86 | - xcodebuild_test_options: $XCODEBUILD_OPTIONS 87 | - generate_code_coverage_files: 'yes' 88 | - xcode-test: 89 | title: xcode-test-tvos 90 | inputs: 91 | - project_path: $XCODEBUILD_PROJECT 92 | - scheme: $XCODEBUILD_SCHEME_TVOS 93 | - xcodebuild_test_options: $XCODEBUILD_OPTIONS 94 | - generate_code_coverage_files: 'yes' 95 | - simulator_platform: tvOS Simulator 96 | - simulator_device: Apple TV 1080p 97 | 98 | cocoapods-test: 99 | steps: 100 | - script: 101 | title: cocoapods-lint 102 | inputs: 103 | - content: pod lib lint 104 | 105 | # Deploy workflows 106 | 107 | deploy-artifacts: 108 | steps: 109 | - deploy-to-bitrise-io: 110 | inputs: 111 | - notify_user_groups: none 112 | - is_enable_public_page: false 113 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Vendor/Common/Variables.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Common/Variables.xcconfig 3 | // 4 | // Copyright © 2017 Netguru Sp. z o.o. All rights reserved. 5 | // Licensed under MIT License. 6 | // 7 | // This file contains variables that are mapped to build settings. 8 | // 9 | 10 | // MARK: Environment 11 | 12 | // The environments build setting, typically overridden in environment-specific 13 | // `xcconfig` files. 14 | _ENVIRONMENTS = ENV_DEFAULT 15 | 16 | // MARK: Versioning 17 | 18 | // The build semantic version, mapped later to `CFBundleVersionString`. 19 | _BUILD_VERSION = 20 | 21 | // The build number, mapped later to `CFBundleVersion`. 22 | _BUILD_NUMBER = $(_BUILD_VERSION) 23 | 24 | // MARK: Bundle 25 | 26 | // Bundle name of the product, used by Xcode to create an `.app` bundle. 27 | _BUNDLE_NAME = $(TARGET_NAME) 28 | 29 | // Bundle identifier of the product. 30 | _BUNDLE_IDENTIFIER = 31 | 32 | /// Path to `Info.plist` file of the product. 33 | _BUNDLE_INFOPLIST_PATH = 34 | 35 | // Whether the bundle is `@testable`. 36 | _BUNDLE_TESTABLE = YES 37 | 38 | // MARK: Deployment 39 | 40 | // Minimum iOS deployment target. 41 | _DEPLOYMENT_TARGET_IOS = 42 | 43 | // Targeted device families on iOS. 44 | _DEPLOYMENT_DEVICES_IOS = 1,2 45 | 46 | // Minimum macOS deployment target. 47 | _DEPLOYMENT_TARGET_MACOS = 48 | 49 | // Minimum tvOS deployment target. 50 | _DEPLOYMENT_TARGET_TVOS = 51 | 52 | // Targeted device families on tvOS. 53 | _DEPLOYMENT_DEVICES_TVOS = 3 54 | 55 | // Minimum watchOS deployment target. 56 | _DEPLOYMENT_TARGET_WATCHOS = 57 | 58 | // Targeted device families on watchOS. 59 | _DEPLOYMENT_DEVICES_WATCHOS = 4 60 | 61 | // MARK: Signing 62 | 63 | // Code signign style. 64 | _CODESIGN_STYLE = Automatic 65 | 66 | // Development team to be used with `_CODESIGN_PROFILE_SPECIFIER` to manually 67 | // code sign the product. 68 | _CODESIGN_DEVELOPMENT_TEAM = 69 | 70 | // The identity to be used with `_CODESIGN_PROFILE_SPECIFIER` to manually code 71 | // sign the product. 72 | _CODESIGN_IDENTITY = 73 | 74 | // The provisioning profile specifier to be used with `_CODESIGN_IDENTITY` to 75 | // manually code sign the product. 76 | _CODESIGN_PROFILE_SPECIFIER = 77 | 78 | // Path to `.entitlements` file of the product. 79 | _CODESIGN_ENTITLEMENTS_PATH = 80 | 81 | // MARK: Assets 82 | 83 | // Name of the icon asset to be used. 84 | _ASSET_ICON = 85 | 86 | // Name of the launch image asset to be used. 87 | _ASSET_LAUNCHIMAGE = 88 | 89 | // MARK: Compiler 90 | 91 | // Whether to enable hard mode (a.k.a. treat warnings as errors). 92 | _COMPILER_HARD_MODE = YES 93 | 94 | // The version of Swift to be used by compiler. 95 | _COMPILER_SWIFT_VERSION = 96 | 97 | // Additional flags for the Swift compiler. 98 | _COMPILER_SWIFT_FLAGS = 99 | 100 | // Path to Swift bridging header to be used by compiler. 101 | _COMPILER_SWIFT_BRIDGING_HEADER_PATH = 102 | 103 | // Additional flags for Objective-C linker. 104 | _COMPILER_OBJC_LINKER_FLAGS = 105 | 106 | // Additional search paths when looking for headers. 107 | _COMPILER_OBJC_HEADER_SEARCH_PATHS = 108 | 109 | // Additional search paths when looking for frameworks. 110 | _COMPILER_FRAMEWORK_SEARCH_PATHS = 111 | -------------------------------------------------------------------------------- /ResponseDetective.xcodeproj/xcshareddata/xcschemes/ResponseDetective-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 52 | 53 | 54 | 55 | 57 | 63 | 64 | 65 | 66 | 67 | 77 | 78 | 84 | 85 | 86 | 87 | 93 | 94 | 96 | 97 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /ResponseDetective.xcodeproj/xcshareddata/xcschemes/ResponseDetective-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 52 | 53 | 54 | 55 | 57 | 63 | 64 | 65 | 66 | 67 | 77 | 78 | 84 | 85 | 86 | 87 | 93 | 94 | 96 | 97 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "ResponseDetective", 6 | platforms: [ 7 | .iOS(.v8), 8 | .macOS(.v10_10), 9 | .tvOS(.v9) 10 | ], 11 | products: [ 12 | .library( 13 | name: "ResponseDetective", 14 | targets: [ 15 | "ResponseDetective", 16 | "ResponseDetectiveObjC" 17 | ] 18 | ) 19 | ], 20 | dependencies: [ 21 | .package( 22 | url: "https://github.com/Quick/Quick.git", 23 | .exact("3.0.0") 24 | ), 25 | .package( 26 | url: "https://github.com/Quick/Nimble.git", 27 | .exact("9.0.0") 28 | ), 29 | .package( 30 | url: "https://github.com/AliSoftware/OHHTTPStubs.git", 31 | .exact("9.1.0") 32 | ) 33 | ], 34 | targets: [ 35 | .target( 36 | name: "ResponseDetective", 37 | dependencies: ["ResponseDetectiveObjC"], 38 | path: "ResponseDetective", 39 | exclude: [ 40 | "Configuration", 41 | "Resources", 42 | "Tests", 43 | "include", 44 | "Sources/RDTHTMLBodyDeserializer.h", 45 | "Sources/RDTXMLBodyDeserializer.h", 46 | "Sources/ResponseDetective.h", 47 | "Sources/RDTHTMLBodyDeserializer.m", 48 | "Sources/RDTBodyDeserializer.h", 49 | "Sources/RDTXMLBodyDeserializer.m" 50 | ], 51 | sources: [ 52 | "Sources/ResponseRepresentation.swift", 53 | "Sources/ErrorRepresentation.swift", 54 | "Sources/ResponseDetective.swift", 55 | "Sources/BufferOutputFacility.swift", 56 | "Sources/ConsoleOutputFacility.swift", 57 | "Sources/URLEncodedBodyDeserializer.swift", 58 | "Sources/PlaintextBodyDeserializer.swift", 59 | "Sources/ImageBodyDeserializer.swift", 60 | "Sources/JSONBodyDeserializer.swift", 61 | "Sources/Dictionary.swift", 62 | "Sources/URLProtocol.swift", 63 | "Sources/OutputFacility.swift", 64 | "Sources/RequestRepresentation.swift" 65 | ] 66 | ), 67 | .target( 68 | name: "ResponseDetectiveObjC", 69 | dependencies: [], 70 | path: "ResponseDetective", 71 | exclude: [ 72 | "Configuration", 73 | "Resources", 74 | "Tests", 75 | "Sources/ResponseRepresentation.swift", 76 | "Sources/ErrorRepresentation.swift", 77 | "Sources/ResponseDetective.swift", 78 | "Sources/BufferOutputFacility.swift", 79 | "Sources/ConsoleOutputFacility.swift", 80 | "Sources/URLEncodedBodyDeserializer.swift", 81 | "Sources/PlaintextBodyDeserializer.swift", 82 | "Sources/ImageBodyDeserializer.swift", 83 | "Sources/JSONBodyDeserializer.swift", 84 | "Sources/Dictionary.swift", 85 | "Sources/URLProtocol.swift", 86 | "Sources/OutputFacility.swift", 87 | "Sources/RequestRepresentation.swift" 88 | ], 89 | sources: [ 90 | "Sources/RDTHTMLBodyDeserializer.h", 91 | "Sources/RDTXMLBodyDeserializer.h", 92 | "Sources/ResponseDetective.h", 93 | "Sources/RDTHTMLBodyDeserializer.m", 94 | "Sources/RDTBodyDeserializer.h", 95 | "Sources/RDTXMLBodyDeserializer.m" 96 | ], 97 | cSettings: [ 98 | .headerSearchPath("ResponseDetective/Sources"), 99 | .headerSearchPath("ResponseDetective") 100 | ] 101 | ), 102 | .testTarget( 103 | name: "ResponseDetectiveTests", 104 | dependencies: [ 105 | "ResponseDetective", 106 | "ResponseDetectiveObjC", 107 | "Quick", 108 | "Nimble", 109 | "OHHTTPStubs" 110 | ], 111 | path: "ResponseDetective", 112 | exclude: [ 113 | "Configuration", 114 | "Resources", 115 | "Sources", 116 | "include", 117 | ], 118 | sources: ["Tests"] 119 | ), 120 | ] 121 | ) 122 | -------------------------------------------------------------------------------- /ResponseDetective.xcodeproj/xcshareddata/xcschemes/ResponseDetective-macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 52 | 53 | 54 | 55 | 57 | 63 | 64 | 65 | 66 | 67 | 77 | 78 | 84 | 85 | 86 | 87 | 93 | 94 | 100 | 101 | 102 | 103 | 105 | 106 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /ResponseDetective/Sources/ConsoleOutputFacility.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConsoleOutputFacility.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | 10 | /// An output facility which outputs requests, responses and errors to console. 11 | @objc(RDTConsoleOutputFacility) public final class ConsoleOutputFacility: NSObject, OutputFacility { 12 | 13 | // MARK: Initializers 14 | 15 | /// Initializes the receiver with default print closure. 16 | public convenience override init() { 17 | self.init(printClosure: { print($0) }) 18 | } 19 | 20 | /// Initializes the receiver. 21 | /// 22 | /// - Parameters: 23 | /// - printClosure: The print closure used to output strings into the 24 | /// console. 25 | @objc(initWithPrintBlock:) public init(printClosure: @escaping @convention(block) (String) -> Void) { 26 | self.printClosure = printClosure 27 | } 28 | 29 | // MARK: Properties 30 | 31 | /// Print closure used to output strings into the console. 32 | private let printClosure: @convention(block) (String) -> Void 33 | 34 | // MARK: OutputFacility 35 | 36 | /// Prints the request in the following format: 37 | /// 38 | /// <0xbadf00d> [REQUEST] POST https://httpbin.org/post 39 | /// ├─ Headers 40 | /// │ Content-Type: application/json 41 | /// │ Content-Length: 14 42 | /// ├─ Body 43 | /// │ { 44 | /// │ "foo": "bar" 45 | /// │ } 46 | /// 47 | /// - SeeAlso: OutputFacility.output(requestRepresentation:) 48 | public func output(requestRepresentation request: RequestRepresentation) { 49 | let headers = request.headers.reduce([]) { 50 | $0 + ["\($1.0): \($1.1)"] 51 | } 52 | let body = request.deserializedBody.map { 53 | #if swift(>=3.2) 54 | return $0.split { $0 == "\n" }.map(String.init) 55 | #else 56 | return $0.characters.split { $0 == "\n" }.map(String.init) 57 | #endif 58 | } ?? [""] 59 | printBoxString(title: "<\(request.identifier)> [REQUEST] \(request.method) \(request.urlString)", sections: [ 60 | ("Headers", headers), 61 | ("Body", body), 62 | ]) 63 | } 64 | 65 | /// Prints the response in the following format: 66 | /// 67 | /// <0xbadf00d> [RESPONSE] 200 (NO ERROR) https://httpbin.org/post 68 | /// ├─ Headers 69 | /// │ Content-Type: application/json 70 | /// │ Content-Length: 24 71 | /// ├─ Body 72 | /// │ { 73 | /// │ "args": {}, 74 | /// │ "headers": {} 75 | /// │ } 76 | /// 77 | /// - SeeAlso: OutputFacility.output(responseRepresentation:) 78 | public func output(responseRepresentation response: ResponseRepresentation) { 79 | let headers = response.headers.reduce([]) { 80 | $0 + ["\($1.0): \($1.1)"] 81 | } 82 | let body = response.deserializedBody.map { 83 | #if swift(>=3.2) 84 | return $0.split { $0 == "\n" }.map(String.init) 85 | #else 86 | return $0.characters.split { $0 == "\n" }.map(String.init) 87 | #endif 88 | } ?? [""] 89 | printBoxString(title: "<\(response.requestIdentifier)> [RESPONSE] \(response.statusCode) (\(response.statusString.uppercased())) \(response.urlString)", sections: [ 90 | ("Headers", headers), 91 | ("Body", body), 92 | ]) 93 | } 94 | 95 | /// Prints the error in the following format: 96 | /// 97 | /// <0xbadf00d> [ERROR] NSURLErrorDomain -1009 98 | /// ├─ User Info 99 | /// │ NSLocalizedDescriptionKey: The device is not connected to the internet. 100 | /// │ NSURLErrorKey: https://httpbin.org/post 101 | /// 102 | /// - SeeAlso: OutputFacility.output(errorRepresentation:) 103 | public func output(errorRepresentation error: ErrorRepresentation) { 104 | let userInfo = error.userInfo.reduce([]) { 105 | $0 + ["\($1.0): \($1.1)"] 106 | } 107 | printBoxString(title: "<\(error.requestIdentifier)> [ERROR] \(error.domain) \(error.code)", sections: [ 108 | ("User Info", userInfo), 109 | ]) 110 | } 111 | 112 | // MARK: Printing boxes 113 | 114 | /// Composes a box string in the following format: 115 | /// 116 | /// box title 117 | /// ├─ section title 118 | /// │ section 119 | /// │ contents 120 | /// 121 | /// - Parameters: 122 | /// - title: The title of the box 123 | /// - sections: A dictionary with section titles as keys and content 124 | /// lines as values. 125 | /// 126 | /// - Returns: A composed box string. 127 | private func composeBoxString(title: String, sections: [(String, [String])]) -> String { 128 | return "\(title)\n" + sections.reduce("") { 129 | "\($0) ├─ \($1.0)\n" + $1.1.reduce("") { 130 | "\($0) │ \($1)\n" 131 | } 132 | } 133 | } 134 | 135 | /// Composes and prints the box sting in the console. 136 | /// 137 | /// - Parameters: 138 | /// - title: The title of the box 139 | /// - sections: A dictionary with section titles as keys and content 140 | /// lines as values. 141 | private func printBoxString(title: String, sections: [(String, [String])]) { 142 | printClosure(composeBoxString(title: title, sections: sections)) 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](Images/Header.png) 2 | 3 | ![](https://img.shields.io/badge/swift-5.3-orange.svg) 4 | ![](https://img.shields.io/github/release/netguru/ResponseDetective.svg) 5 | ![](https://img.shields.io/badge/carthage-compatible-green.svg) 6 | ![](https://img.shields.io/badge/cocoapods-compatible-green.svg) 7 | ![](https://img.shields.io/badge/spm-compatible-green.svg) 8 | ![](https://img.shields.io/badge/license-MIT-blue.svg) 9 | 10 | **ResponseDetective** is a non-intrusive framework for intercepting any outgoing requests and incoming responses between your app and your server for debugging purposes. 11 | 12 | ## Requirements 13 | 14 | ResponseDetective is written in **Swift 5.3** and supports **iOS 9.0+**, **macOS 10.10+** and **tvOS 9.0+**. 15 | 16 | ## Usage 17 | 18 | Incorporating ResponseDetective in your project is very simple – it all comes down to just two steps: 19 | 20 | ### Step 1: Enable interception 21 | 22 | For ResponseDetective to work, it needs to be added as a middleman between your `(NS)URLSession` and the Internet. You can do this by registering the provided `URLProtocol` class in your session's `(NS)URLSessionConfiguration.protocolClasses`, or use a shortcut method: 23 | 24 | ```objc 25 | // Objective-C 26 | 27 | NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; 28 | [RDTResponseDetective enableInConfiguration:configuration]; 29 | ``` 30 | 31 | ```swift 32 | // Swift 33 | 34 | let configuration = URLSessionConfiguration.default 35 | ResponseDetective.enable(inConfiguration: configuration) 36 | ``` 37 | 38 | Then, you should use that configuration with your `(NS)URLSession`: 39 | 40 | ```objc 41 | // Objective-C 42 | 43 | NSURLSession *session = [[NSURLSession alloc] initWithConfiguration:configuration]; 44 | ``` 45 | 46 | ```swift 47 | // Swift 48 | 49 | let session = URLSession(configuration: configuration) 50 | ``` 51 | 52 | Or, if you're using [AFNetworking](https://github.com/AFNetworking/AFNetworking)/[Alamofire](https://github.com/Alamofire/Alamofire) as your networking framework, integrating ResponseDetective comes down to just initializing your `AFURLSessionManager`/`Manager` with the above `(NS)URLSessionConfiguration`: 53 | 54 | ```objc 55 | // Objective-C (AFNetworking) 56 | 57 | AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration]; 58 | ``` 59 | 60 | ```swift 61 | // Swift (Alamofire) 62 | 63 | let manager = Alamofire.SessionManager(configuration: configuration) 64 | ``` 65 | 66 | And that's all! 67 | 68 | ### Step 2: Profit 69 | 70 | Now it's time to perform the actual request: 71 | 72 | ```objc 73 | // Objective-C 74 | 75 | NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://httpbin.org/get"]]; 76 | [[session dataTaskWithRequest:request] resume]; 77 | ``` 78 | 79 | ```swift 80 | // Swift 81 | 82 | let request = URLRequest(URL: URL(string: "http://httpbin.org/get")!) 83 | session.dataTask(with: request).resume() 84 | ``` 85 | 86 | Voilà! 🎉 Check out your console output: 87 | 88 | ```none 89 | <0x000000000badf00d> [REQUEST] GET https://httpbin.org/get 90 | ├─ Headers 91 | ├─ Body 92 | │ 93 | 94 | <0x000000000badf00d> [RESPONSE] 200 (NO ERROR) https://httpbin.org/get 95 | ├─ Headers 96 | │ Server: nginx 97 | │ Date: Thu, 01 Jan 1970 00:00:00 GMT 98 | │ Content-Type: application/json 99 | ├─ Body 100 | │ { 101 | │ "args" : { 102 | │ }, 103 | │ "headers" : { 104 | │ "User-Agent" : "ResponseDetective\/1 CFNetwork\/758.3.15 Darwin\/15.4.0", 105 | │ "Accept-Encoding" : "gzip, deflate", 106 | │ "Host" : "httpbin.org", 107 | │ "Accept-Language" : "en-us", 108 | │ "Accept" : "*\/*" 109 | │ }, 110 | │ "url" : "https:\/\/httpbin.org\/get" 111 | │ } 112 | ``` 113 | 114 | ## Installation 115 | 116 | ### Carthage 117 | 118 | If you're using [Carthage](https://github.com/Carthage/Carthage), add the following dependency to your `Cartfile`: 119 | 120 | ```none 121 | github "netguru/ResponseDetective" ~> {version} 122 | ``` 123 | 124 | ### CocoaPods 125 | 126 | If you're using [CocoaPods](http://cocoapods.org), add the following dependency to your `Podfile`: 127 | 128 | ```none 129 | use_frameworks! 130 | pod 'ResponseDetective', '~> {version}' 131 | ``` 132 | 133 | ### Swift Package Manager 134 | 135 | If you're using [Swift Package Manager](https://github.com/apple/swift-package-manager), add this repository to the Swift Packages in your project settings. 136 | 137 | ### Local 138 | 139 | To install the test dependencies or to build ResponseDetective itself, do not run `carthage` directly. It can't handle the Apple Silicon architectures introduced in Xcode 12. Instead, run it through the `carthage.sh` script: 140 | 141 | ```bash 142 | $ ./carthage.sh bootstrap 143 | ``` 144 | 145 | Alternatively, you can run the tests locally using Swift Package Manager with the following command: 146 | 147 | ```bash 148 | $ swift test 149 | ``` 150 | 151 | ## About 152 | 153 | This project was made with ♡ by [Netguru](https://netguru.com). 154 | 155 | ### Release names 156 | 157 | Starting from version 1.0.0, ResponseDetective's releases are named after [Sherlock Holmes canon stories](http://www.sherlockian.net/investigating/canon/), in chronological order. **What happens if we reach 60 releases and there are no more stories?** We don't know, maybe we'll start naming them after cats or something. 158 | 159 | ### License 160 | 161 | This project is licensed under **MIT License**. See [LICENSE.md](LICENSE.md) for more info. 162 | -------------------------------------------------------------------------------- /ResponseDetective/Sources/ResponseDetective.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResponseDetective.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | #if SWIFT_PACKAGE 10 | import ResponseDetectiveObjC 11 | #endif 12 | 13 | /// ResponseDetective configuration cluster class that defines the behavior 14 | /// of request interception and logging. 15 | @objc(RDTResponseDetective) public final class ResponseDetective: NSObject { 16 | 17 | // MARK: Properties 18 | 19 | /// An output facility for reporting requests, responses and errors. 20 | public static var outputFacility: OutputFacility = ConsoleOutputFacility() 21 | 22 | /// A class of the URL protocol used to intercept requests. 23 | public static let URLProtocolClass: Foundation.URLProtocol.Type = URLProtocol.self 24 | 25 | /// A storage for request predicates. 26 | private static var requestPredicates: [NSPredicate] = [] 27 | 28 | /// Body deserializers stored by a supported content type. 29 | private static var customBodyDeserializers: [String: BodyDeserializer] = [:] 30 | 31 | /// Default body deserializers provided by ResponseDetective. 32 | private static let defaultBodyDeserializers: [String: BodyDeserializer] = [ 33 | "*/json": JSONBodyDeserializer(), 34 | "*/xml": XMLBodyDeserializer(), 35 | "*/html": HTMLBodyDeserializer(), 36 | "*/x-www-form-urlencoded": URLEncodedBodyDeserializer(), 37 | "image/*": ImageBodyDeserializer(), 38 | "text/*": PlaintextBodyDeserializer(), 39 | ] 40 | 41 | // MARK: Configuration 42 | 43 | /// Resets the ResponseDetective mutable state. 44 | public static func reset() { 45 | outputFacility = ConsoleOutputFacility() 46 | requestPredicates = [] 47 | customBodyDeserializers = [:] 48 | } 49 | 50 | /// Enables ResponseDetective in an URL session configuration. 51 | /// 52 | /// - Parameters: 53 | /// - configuration: The URL session configuration to enable the 54 | /// session in. 55 | @objc(enableInConfiguration:) public static func enable(inConfiguration configuration: URLSessionConfiguration) { 56 | configuration.protocolClasses?.insert(URLProtocolClass, at: 0) 57 | } 58 | 59 | /// Ignores requests matching the given predicate. The predicate will be 60 | /// evaluated with an instance of NSURLRequest. 61 | /// 62 | /// - Parameters: 63 | /// - predicate: A predicate for matching a request. If the 64 | /// predicate evaluates to `false`, the request is not intercepted. 65 | @objc(ignoreRequestsMatchingPredicate:) public static func ignoreRequests(matchingPredicate predicate: NSPredicate) { 66 | requestPredicates.append(predicate) 67 | } 68 | 69 | /// Checks whether the given request can be incercepted. 70 | /// 71 | /// - Parameters: 72 | /// - request: The request to check. 73 | /// 74 | /// - Returns: `true` if request can be intercepted, `false` otherwise. 75 | @objc(canInterceptRequest:) public static func canIncercept(request: URLRequest) -> Bool { 76 | return requestPredicates.reduce(true) { 77 | return $0 && !$1.evaluate(with: request) 78 | } 79 | } 80 | 81 | // MARK: Deserialization 82 | 83 | /// Registers a body deserializer. 84 | /// 85 | /// - Parameters: 86 | /// - deserializer: The deserializer to register. 87 | /// - contentType: The supported content type. 88 | @objc(registerBodyDeserializer:forContentType:) public static func registerBodyDeserializer(_ deserializer: BodyDeserializer, forContentType contentType: String) { 89 | customBodyDeserializers[contentType] = deserializer 90 | } 91 | 92 | /// Registers a body deserializer. 93 | /// 94 | /// - Parameters: 95 | /// - deserializer: The deserializer to register. 96 | /// - contentTypes: The supported content types. 97 | @objc(registerBodyDeserializer:forContentTypes:) public static func registerBodyDeserializer(_ deserializer: BodyDeserializer, forContentTypes contentTypes: [String]) { 98 | for contentType in contentTypes { 99 | registerBodyDeserializer(deserializer, forContentType: contentType) 100 | } 101 | } 102 | 103 | /// Deserializes a HTTP body into a string. 104 | /// 105 | /// - Parameters: 106 | /// - body: The body to deserialize. 107 | /// - contentType: The content type of the body. 108 | /// 109 | /// - Returns: A deserialized body or `nil` if no serializer is capable of 110 | /// deserializing body with the given content type. 111 | @objc(deserializeBody:contentType:) public static func deserialize(body: Data, contentType: String) -> String? { 112 | if let deserializer = findBodyDeserializer(forContentType: contentType) { 113 | return deserializer.deserialize(body: body) 114 | } else { 115 | return nil 116 | } 117 | } 118 | 119 | /// Finds a body deserializer by pattern. 120 | /// 121 | /// - Parameters: 122 | /// - contentType: The content type to find a deserializer for. 123 | /// 124 | /// - Returns: A body deserializer for given `contentType` or `nil`. 125 | @objc(findBodyDeserializerForContentType:) private static func findBodyDeserializer(forContentType contentType: String) -> BodyDeserializer? { 126 | guard let trimmedContentType = contentType.components(separatedBy: ";").first?.trimmingCharacters(in: .whitespaces) else { 127 | return nil 128 | } 129 | for (pattern, deserializer) in defaultBodyDeserializers.appending(elementsOf: customBodyDeserializers) { 130 | let patternParts = pattern.components(separatedBy: "/") 131 | let actualParts = trimmedContentType.components(separatedBy: "/") 132 | guard patternParts.count == 2 && actualParts.count == 2 else { 133 | return nil 134 | } 135 | if ["*" , actualParts[0]].contains(patternParts[0]) && ["*" , actualParts[1]].contains(patternParts[1]) { 136 | return deserializer 137 | } 138 | } 139 | return nil 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /ResponseDetective/Sources/URLProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLProtocol.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | 10 | /// The intercepting URL protocol. 11 | @objc(RDTURLProtocol) internal final class URLProtocol: Foundation.URLProtocol, URLSessionTaskDelegate, URLSessionDataDelegate { 12 | 13 | // MARK: Initialization 14 | 15 | /// - SeeAlso: Foundation.URLProtocol.canInit(with:) 16 | internal override static func canInit(with request: URLRequest) -> Bool { 17 | guard let URL = request.url, let scheme = URL.scheme else { return false } 18 | return ["http", "https"].contains(scheme) && ResponseDetective.canIncercept(request: request) 19 | } 20 | 21 | /// - SeeAlso: Foundation.URLProtocol.init(request:cachedResponse:client:) 22 | internal override init(request: URLRequest, cachedResponse: CachedURLResponse?, client: URLProtocolClient?) { 23 | super.init(request: request, cachedResponse: cachedResponse, client: client) 24 | internalSession = Foundation.URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil) 25 | internalTask = internalSession.dataTask(with: request) 26 | } 27 | 28 | // MARK: Properties 29 | 30 | /// Internal session object used to perform the request. 31 | private var internalSession: Foundation.URLSession! 32 | 33 | /// Internal session data dark responsible for request execution. 34 | private var internalTask: URLSessionDataTask! 35 | 36 | /// Internal task response storage. 37 | private var internalResponse: HTTPURLResponse? 38 | 39 | /// Internal response data storage. 40 | private lazy var internalResponseData = Data() 41 | 42 | /// A unique identifier of the request. 43 | private let requestIdentifier: String = UUID().uuidString.uppercased() 44 | 45 | // MARK: URLProtocol 46 | 47 | /// - SeeAlso: Foundation.URLProtocol.canonicalRequest(for:) 48 | internal override static func canonicalRequest(for request: URLRequest) -> URLRequest { 49 | return request 50 | } 51 | 52 | /// - SeeAlso: Foundation.URLProtocol.startLoading() 53 | internal override func startLoading() { 54 | intercept(request: request) 55 | internalTask.resume() 56 | } 57 | 58 | /// - SeeAlso: Foundation.URLProtocol.stopLoading() 59 | internal override func stopLoading() { 60 | internalSession.invalidateAndCancel() 61 | } 62 | 63 | // MARK: URLSessionTaskDelegate 64 | 65 | /// - SeeAlso: URLSessionTaskDelegate.urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:) 66 | internal func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) { 67 | client?.urlProtocol(self, wasRedirectedTo: request, redirectResponse: response) 68 | completionHandler(request) 69 | } 70 | 71 | /// - SeeAlso: URLSessionTaskDelegate.urlSession(_:task:didCompleteWithError:) 72 | internal func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { 73 | if let error = error { 74 | intercept(error: error as NSError, response: internalResponse, data: internalResponseData) 75 | client?.urlProtocol(self, didFailWithError: error) 76 | } else if let response = internalResponse { 77 | intercept(response: response, data: internalResponseData) 78 | client?.urlProtocolDidFinishLoading(self) 79 | } 80 | internalSession.finishTasksAndInvalidate() 81 | } 82 | 83 | // MARK: URLSessionDataDelegate 84 | 85 | /// - SeeAlso: URLSessionDataDelegate.urlSession(_:dataTask:didReceive:completionHandler:) 86 | internal func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) { 87 | internalResponse = response as? HTTPURLResponse 88 | completionHandler(.allow) 89 | client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .allowed) 90 | } 91 | 92 | /// - SeeAlso: URLSessionDataDelegate.urlSession(_:dataTask:didReceive:) 93 | internal func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { 94 | internalResponseData.append(data) 95 | client?.urlProtocol(self, didLoad: data) 96 | } 97 | 98 | // MARK: Interception 99 | 100 | /// Incercepts the given request and passes it to the ResponseDetective 101 | /// instance. 102 | /// 103 | /// - Parameter request: The intercepted request. 104 | private func intercept(request: URLRequest) { 105 | let deserializedBody = standardizedData(of: request).flatMap { data in 106 | ResponseDetective.deserialize(body: data, contentType: request.value(forHTTPHeaderField: "Content-Type") ?? "application/octet-stream") 107 | } 108 | let requestRepresentation = RequestRepresentation(identifier: requestIdentifier, request: request, deserializedBody: deserializedBody) 109 | ResponseDetective.outputFacility.output(requestRepresentation: requestRepresentation) 110 | } 111 | 112 | /// Incercepts the given response and passes it to the ResponseDetective 113 | /// instance. 114 | /// 115 | /// - Parameters: 116 | /// - response: The intercepted response. 117 | /// - data: The intercepted response data. 118 | private func intercept(response: HTTPURLResponse, data: Data?) { 119 | let deserializedBody = data.flatMap { data in 120 | ResponseDetective.deserialize(body: data, contentType: (response.allHeaderFields["Content-Type"] as? String) ?? "application/octet-stream") 121 | } 122 | let responseRepresentation = ResponseRepresentation(requestIdentifier: requestIdentifier, response: response, body: data, deserializedBody: deserializedBody) 123 | ResponseDetective.outputFacility.output(responseRepresentation: responseRepresentation) 124 | } 125 | 126 | /// Incercepts the given error and passes it to the ResponseDetective 127 | /// instance. 128 | /// 129 | /// - Parameters: 130 | /// - error: The intercepted request. 131 | /// - response: The intercepted response. 132 | /// - data: The intercepted response data. 133 | private func intercept(error: NSError, response: HTTPURLResponse?, data: Data?) { 134 | let deserializedBody = response.flatMap { response in 135 | return data.flatMap { data in 136 | ResponseDetective.deserialize(body: data, contentType: (response.allHeaderFields["Content-Type"] as? String) ?? "application/octet-stream") 137 | } 138 | } 139 | let responseRepresentation = response.flatMap { response in 140 | ResponseRepresentation(requestIdentifier: requestIdentifier, response: response, body: data, deserializedBody: deserializedBody) 141 | } 142 | let errorRepresentation = ErrorRepresentation(requestIdentifier: requestIdentifier, error: error, response: responseRepresentation) 143 | ResponseDetective.outputFacility.output(errorRepresentation: errorRepresentation) 144 | } 145 | 146 | // MARK: Private 147 | 148 | /// Extracts and standardizes data of a request. 149 | /// 150 | /// - Parameters: 151 | /// - request: The request which data should be standardized. 152 | /// 153 | /// - Returns: Data of the `request`. 154 | private func standardizedData(of request: URLRequest) -> Data? { 155 | return request.httpBody ?? request.httpBodyStream.flatMap { stream in 156 | let data = NSMutableData() 157 | stream.open() 158 | while stream.hasBytesAvailable { 159 | var buffer = [UInt8](repeating: 0, count: 1024) 160 | let length = stream.read(&buffer, maxLength: buffer.count) 161 | data.append(buffer, length: length) 162 | } 163 | stream.close() 164 | return data as Data 165 | } 166 | } 167 | 168 | } 169 | -------------------------------------------------------------------------------- /ResponseDetective/Tests/Specs/ResponseDetectiveSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResponseDetectiveSpec.swift 3 | // 4 | // Copyright © 2016-2020 Netguru S.A. All rights reserved. 5 | // Licensed under the MIT License. 6 | // 7 | 8 | import Foundation 9 | import Nimble 10 | import OHHTTPStubs 11 | import ResponseDetective 12 | import Quick 13 | 14 | internal final class ResponseDetectiveSpec: QuickSpec { 15 | 16 | override func spec() { 17 | 18 | describe("ResponseDetective") { 19 | 20 | beforeSuite { 21 | HTTPStubs.stubRequests { request in 22 | return request.url?.host == "httpbin.org" 23 | } withStubResponse: { _ in 24 | return HTTPStubsResponse(data: Data(), statusCode: 200, headers: nil) 25 | } 26 | } 27 | 28 | beforeEach { 29 | ResponseDetective.reset() 30 | } 31 | 32 | afterSuite { 33 | HTTPStubs.removeAllStubs() 34 | } 35 | 36 | describe("initial state") { 37 | 38 | it("should use default output facility") { 39 | expect(type(of: ResponseDetective.outputFacility) == ConsoleOutputFacility.self).to(beTruthy()) 40 | } 41 | 42 | it("should use default url protocol class") { 43 | expect(ResponseDetective.URLProtocolClass).to(beIdenticalTo(NSClassFromString("RDTURLProtocol")!)) 44 | } 45 | 46 | } 47 | 48 | describe("enabling in url session configuration") { 49 | 50 | let configuration = URLSessionConfiguration.default 51 | 52 | beforeEach { 53 | ResponseDetective.enable(inConfiguration: configuration) 54 | } 55 | 56 | it("should add protocol class at the beginning of array") { 57 | expect(configuration.protocolClasses!.first == ResponseDetective.URLProtocolClass).to(beTrue()) 58 | } 59 | 60 | } 61 | 62 | describe("ignoring requests") { 63 | 64 | let request = URLRequest(url: URL(string: "http://foo.bar")!) 65 | 66 | context("before adding predicate") { 67 | 68 | it("should not ignore the request") { 69 | expect { 70 | ResponseDetective.canIncercept(request: request) 71 | }.to(beTruthy()) 72 | } 73 | 74 | } 75 | 76 | context("after adding predicate") { 77 | 78 | beforeEach { 79 | ResponseDetective.ignoreRequests(matchingPredicate: NSPredicate { subject, _ in 80 | guard let subject = subject as? URLRequest, let url = subject.url else { 81 | return true 82 | } 83 | let string = url.absoluteString 84 | 85 | return string.contains("foo") 86 | }) 87 | } 88 | 89 | it("should ignore the request") { 90 | expect { 91 | ResponseDetective.canIncercept(request: request) 92 | }.to(beFalsy()) 93 | } 94 | 95 | } 96 | 97 | } 98 | 99 | describe("body deserialization") { 100 | 101 | context("before registering a custom body deserializer") { 102 | 103 | it("should return no deserialized body") { 104 | expect { 105 | ResponseDetective.deserialize(body: Data(), contentType: "foo/bar") 106 | }.to(beNil()) 107 | } 108 | 109 | } 110 | 111 | context("after registering an explicit body deserializer") { 112 | 113 | beforeEach { 114 | ResponseDetective.registerBodyDeserializer( 115 | TestBodyDeserializer(fixedDeserializedBody: "lorem ipsum"), 116 | forContentType: "foo/bar" 117 | ) 118 | } 119 | 120 | it("should return a deserialized body") { 121 | expect { 122 | ResponseDetective.deserialize(body: Data(), contentType: "foo/bar") 123 | }.to(equal("lorem ipsum")) 124 | } 125 | 126 | it("should return a deserialized body for content type containing properties") { 127 | expect { 128 | ResponseDetective.deserialize(body: Data(), contentType: "foo/bar; charset=utf8") 129 | }.to(equal("lorem ipsum")) 130 | } 131 | 132 | } 133 | 134 | context("after registering a wildcard body deserializer") { 135 | 136 | beforeEach { 137 | ResponseDetective.registerBodyDeserializer( 138 | TestBodyDeserializer(fixedDeserializedBody: "dolor sit amet"), 139 | forContentType: "foo/*" 140 | ) 141 | } 142 | 143 | it("should return a deserialized body") { 144 | expect { 145 | ResponseDetective.deserialize(body: Data(), contentType: "foo/baz") 146 | }.to(equal("dolor sit amet")) 147 | } 148 | 149 | } 150 | 151 | } 152 | 153 | describe("request interception") { 154 | 155 | var buffer: BufferOutputFacility! 156 | let configuration = URLSessionConfiguration.default 157 | 158 | beforeEach { 159 | buffer = BufferOutputFacility() 160 | ResponseDetective.outputFacility = buffer 161 | ResponseDetective.registerBodyDeserializer(TestBodyDeserializer(), forContentType: "*/*") 162 | ResponseDetective.enable(inConfiguration: configuration) 163 | } 164 | 165 | context("before request has been sent") { 166 | 167 | it("should intercept no requests") { 168 | expect(buffer.requestRepresentations).to(beEmpty()) 169 | } 170 | 171 | } 172 | 173 | context("after request has been sent") { 174 | 175 | let request: URLRequest = { 176 | var request = URLRequest(url: URL(string: "https://httpbin.org/post")!) 177 | request.httpMethod = "POST" 178 | request.httpBody = Data(base64Encoded: "foo", options: []) 179 | return request 180 | }() 181 | 182 | beforeEach { 183 | let session = URLSession(configuration: configuration) 184 | session.dataTask(with: request).resume() 185 | } 186 | 187 | it("should eventually intercept it") { 188 | expect(buffer.requestRepresentations.count).toEventually(beGreaterThanOrEqualTo(1), timeout: .seconds(5)) 189 | expect(buffer.responseRepresentations.last?.body).toEventuallyNot(beNil(), timeout: .seconds(5)) 190 | } 191 | 192 | } 193 | 194 | } 195 | 196 | describe("response interception") { 197 | 198 | var buffer: BufferOutputFacility! 199 | let configuration = URLSessionConfiguration.default 200 | 201 | beforeEach { 202 | buffer = BufferOutputFacility() 203 | ResponseDetective.outputFacility = buffer 204 | ResponseDetective.registerBodyDeserializer(TestBodyDeserializer(), forContentType: "*/*") 205 | ResponseDetective.enable(inConfiguration: configuration) 206 | } 207 | 208 | context("before request has been sent") { 209 | 210 | it("should intercept no responses") { 211 | expect(buffer.responseRepresentations).to(beEmpty()) 212 | } 213 | 214 | } 215 | 216 | context("after request has been sent") { 217 | 218 | let request = URLRequest(url: URL(string: "https://httpbin.org/get")!) 219 | 220 | beforeEach { 221 | let session = URLSession(configuration: configuration) 222 | session.dataTask(with: request).resume() 223 | } 224 | 225 | it("should eventually intercept its response") { 226 | expect(buffer.responseRepresentations.count).toEventually(beGreaterThanOrEqualTo(1), timeout: .seconds(5)) 227 | } 228 | 229 | } 230 | 231 | } 232 | 233 | describe("error interception") { 234 | 235 | var buffer: BufferOutputFacility! 236 | let configuration = URLSessionConfiguration.default 237 | 238 | beforeEach { 239 | buffer = BufferOutputFacility() 240 | ResponseDetective.outputFacility = buffer 241 | ResponseDetective.registerBodyDeserializer(TestBodyDeserializer(), forContentType: "*/*") 242 | ResponseDetective.enable(inConfiguration: configuration) 243 | } 244 | 245 | context("before request has been sent") { 246 | 247 | it("should intercept no errors") { 248 | expect(buffer.responseRepresentations).to(beEmpty()) 249 | } 250 | 251 | } 252 | 253 | context("after request has been sent") { 254 | 255 | let request = URLRequest(url: URL(string: "https://foobar")!) 256 | 257 | beforeEach { 258 | let session = URLSession(configuration: configuration) 259 | session.dataTask(with: request).resume() 260 | } 261 | 262 | it("should eventually intercept its error") { 263 | expect(buffer.errorRepresentations.count).toEventually(beGreaterThanOrEqualTo(1), timeout: .seconds(5)) 264 | } 265 | 266 | } 267 | 268 | } 269 | 270 | } 271 | 272 | } 273 | 274 | } 275 | -------------------------------------------------------------------------------- /ResponseDetective/Configuration/Vendor/Common/Common.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Common/Common.xcconfig 3 | // 4 | // Copyright © 2017 Netguru Sp. z o.o. All rights reserved. 5 | // Licensed under MIT License. 6 | // 7 | // This file contains base build settings for all targets. 8 | // 9 | 10 | // Include default values of variables. 11 | #include "Variables.xcconfig" 12 | 13 | // MARK: Build options 14 | 15 | // The format of debugging symbols. 16 | DEBUG_INFORMATION_FORMAT = dwarf-with-dsym 17 | 18 | // MARK: Search paths 19 | 20 | // Disable legacy-compatible header searching. 21 | ALWAYS_SEARCH_USER_PATHS = NO 22 | 23 | // Additional framework search paths. 24 | FRAMEWORK_SEARCH_PATHS = $(inherited) $(_CARTHAGE_BUILD_PATH) $(_COMPILER_FRAMEWORK_SEARCH_PATHS) 25 | 26 | // Additional header search paths. 27 | HEADER_SEARCH_PATHS = $(inherited) $(_COMPILER_OBJC_HEADER_SEARCH_PATHS) 28 | 29 | // MARK: Linking 30 | 31 | // Other linker flags. 32 | OTHER_LDFLAGS = $(inherited) $(_COMPILER_OBJC_LINKER_FLAGS) 33 | 34 | // MARK: Packaging 35 | 36 | // Path to `Info.plist` file. 37 | INFOPLIST_FILE = $(_BUNDLE_INFOPLIST_PATH) 38 | 39 | // `CFBundleVersionString`. 40 | PRODUCT_BUNDLE_VERSION_STRING = $(_BUILD_VERSION) 41 | 42 | // `CFBundleVersion`. 43 | PRODUCT_BUNDLE_VERSION = $(_BUILD_NUMBER) 44 | 45 | // `CFBundleIdentifier`. 46 | PRODUCT_BUNDLE_IDENTIFIER = $(_BUNDLE_IDENTIFIER) 47 | 48 | // Name of the product. 49 | PRODUCT_NAME = $(_BUNDLE_NAME) 50 | 51 | // MARK: Signing 52 | 53 | // Code signing style. 54 | CODE_SIGN_STYLE = $(_CODESIGN_STYLE) 55 | 56 | // Development team to use for code signing. 57 | DEVELOPMENT_TEAM = $(_CODESIGN_DEVELOPMENT_TEAM) 58 | 59 | // Identity to use for code signing. 60 | CODE_SIGN_IDENTITY = $(_CODESIGN_IDENTITY) 61 | 62 | // Provisioning profile specifier for code signing. 63 | PROVISIONING_PROFILE_SPECIFIER = $(_CODESIGN_PROFILE_SPECIFIER) 64 | 65 | // Path to `.entitlements` file for code signing. 66 | CODE_SIGN_ENTITLEMENTS = $(_CODESIGN_ENTITLEMENTS_PATH) 67 | 68 | // MARK: LLVM compiler 69 | 70 | // Disable link-time optimizations. 71 | LLVM_LTO = NO 72 | 73 | // Compile `NSAssert` assertions. 74 | ENABLE_NS_ASSERTIONS = YES 75 | 76 | // Require `objc_msgSend` to be cast before invocation. 77 | ENABLE_STRICT_OBJC_MSGSEND = YES 78 | 79 | // Preprocessor definitions, mapped from `_ENVIRONMENT`. 80 | GCC_PREPROCESSOR_DEFINITIONS = $(_ENVIRONMENTS) $(inherited) 81 | 82 | // Use GNU++11 C++ language standard. 83 | CLANG_CXX_LANGUAGE_STANDARD = gnu++11 84 | 85 | // Use LLVM C++ standard library. 86 | CLANG_CXX_LIBRARY = libc++ 87 | 88 | // Enable module imports and definitions. 89 | CLANG_ENABLE_MODULES = YES 90 | 91 | // Enable Objective-C ARC. 92 | CLANG_ENABLE_OBJC_ARC = YES 93 | 94 | // Warn about block capture autoreleasing. 95 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES 96 | 97 | // Warn about suspucious commas. 98 | CLANG_WARN_COMMA = YES 99 | 100 | // Warn about suspicious empty loop bodies. 101 | CLANG_WARN_EMPTY_BODY = YES 102 | 103 | // Warn when a function will recursively call itself on every code path. 104 | CLANG_WARN_INFINITE_RECURSION = YES 105 | 106 | // Warn about non-literal null conversion. 107 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES 108 | 109 | // Whether to warn on suspicious implicit conversions. 110 | CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES 111 | 112 | // Warn about strict prototypes. 113 | CLANG_WARN_STRICT_PROTOTYPES = YES 114 | 115 | // Warn about unreachable code. 116 | CLANG_WARN_UNREACHABLE_CODE = YES 117 | 118 | // Warn about range-based `for` loops in C++. 119 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES 120 | 121 | // Warning about suspicious uses of `std::move`. 122 | CLANG_WARN_SUSPICIOUS_MOVE = YES 123 | 124 | // Warn when overriding deprecated Objective-C methods. 125 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES 126 | 127 | // Warn about implicit Objective-C literal conversions. 128 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES 129 | 130 | // Warn about direct accesses to the Objective-C `isa` pointer instead of using 131 | // the Objective-C runtime API. 132 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR 133 | 134 | // Warn about classes that unintentionally do not subclass a root class. 135 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR 136 | 137 | // Warn duplicate method declarations within the same `@interface`. 138 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 139 | 140 | // Warn about implicit capture of `self` (e.g. direct ivar access). 141 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 142 | 143 | // Whether to run the static analyzer with every build. 144 | RUN_CLANG_STATIC_ANALYZER = YES 145 | 146 | // Warn for misused nullability attributes. 147 | CLANG_ANALYZER_NONNULL = YES 148 | 149 | // Warn when a non-localized string is passed to a user-interface method 150 | // expecting a localized string. 151 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES 152 | 153 | // Warn about suspicius conversions between `NSNumber` and `CFNumberRef`. 154 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE 155 | 156 | // Warn when a floating-point value is used as a loop counter. 157 | CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES 158 | 159 | // Warn when using `rand()` and `random()` instead of `arc4random()`. 160 | CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES 161 | 162 | // Warn when using `strcpy() and `strcat()`. 163 | CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES 164 | 165 | // Warn about incorrect uses of `nullable` values. 166 | CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES 167 | 168 | // Function calls should not be position-dependent. 169 | GCC_DYNAMIC_NO_PIC = NO 170 | 171 | // Warn if the same variable is declared in two binaries that are linked 172 | // together. 173 | GCC_NO_COMMON_BLOCKS = YES 174 | 175 | // Use GNU99 C variant. 176 | GCC_C_LANGUAGE_STANDARD = gnu99 177 | 178 | // Whether to precompile the prefix header (if one is specified). 179 | GCC_PRECOMPILE_PREFIX_HEADER = YES 180 | 181 | // Treat warnings as errors (a.k.a. hard mode). 182 | GCC_TREAT_WARNINGS_AS_ERRORS = $(_COMPILER_HARD_MODE) 183 | 184 | // Warn about 64-bit values being implicitly shortened to 32 bits. 185 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES 186 | 187 | // Warn about fields missing from structure initializers (only if designated 188 | // initializers aren't used). 189 | GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES 190 | 191 | // Warn when the returned value does not match its return type. 192 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR 193 | 194 | // Warn about the use of four-character constants. 195 | GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES 196 | 197 | // Warn about an aggregate data type's initializer not being fully bracketed. 198 | GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES 199 | 200 | // Whether to warn about unsafe comparisons between values of different 201 | // signedness. 202 | GCC_WARN_SIGN_COMPARE = YES 203 | 204 | // Warn if a variable might be clobbered by a setjmp call or if an automatic 205 | // variable is used without prior initialization. 206 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE 207 | 208 | // Warn about static functions that are unused. 209 | GCC_WARN_UNUSED_FUNCTION = YES 210 | 211 | // Whether to warn about labels that are unused. 212 | GCC_WARN_UNUSED_LABEL = YES 213 | 214 | // Warn about variables that are never used. 215 | GCC_WARN_UNUSED_VARIABLE = YES 216 | 217 | // Warn if a `@selector` expression refers to an undeclared selector. 218 | GCC_WARN_UNDECLARED_SELECTOR = YES 219 | 220 | // MARK: Asset catalog compiler 221 | 222 | // Asset to use as app icon. 223 | ASSETCATALOG_COMPILER_APPICON_NAME = $(_ASSET_ICON) 224 | 225 | // Asset to use as launch image. 226 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = $(_ASSET_LAUNCHIMAGE) 227 | 228 | // MARK: Swift compiler 229 | 230 | // Swift version to be used. 231 | SWIFT_VERSION = $(_COMPILER_SWIFT_VERSION) 232 | 233 | // Treat warning as errors (a.k.a. hard mode). 234 | SWIFT_TREAT_WARNINGS_AS_ERRORS = $(_COMPILER_HARD_MODE) 235 | 236 | // Compilation conditions for Swift, mapped from `_ENVIRONMENTS`. 237 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(_ENVIRONMENTS) $(inherited) 238 | 239 | // Additional flags passed to Swift compiler. 240 | OTHER_SWIFT_FLAGS = $(inherited) $(_COMPILER_SWIFT_FLAGS) 241 | 242 | // Path to bridging header between Swift and Objective-C. 243 | SWIFT_OBJC_BRIDGING_HEADER = $(_COMPILER_SWIFT_BRIDGING_HEADER_PATH) 244 | -------------------------------------------------------------------------------- /ResponseDetective.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3A2007E31B5501BC00769021 /* ResponseDetective.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A2007D81B5501BC00769021 /* ResponseDetective.framework */; }; 11 | 3AB9E9801EBB64C8004E575E /* URLEncodedBodyDeserializerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB9E97E1EBB64C0004E575E /* URLEncodedBodyDeserializerSpec.swift */; }; 12 | 3AB9E9811EBB64C9004E575E /* URLEncodedBodyDeserializerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB9E97E1EBB64C0004E575E /* URLEncodedBodyDeserializerSpec.swift */; }; 13 | 3AB9E9821EBB64C9004E575E /* URLEncodedBodyDeserializerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB9E97E1EBB64C0004E575E /* URLEncodedBodyDeserializerSpec.swift */; }; 14 | 3AC631211EBB5CF8000F6353 /* URLEncodedBodyDeserializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AC631201EBB5CF8000F6353 /* URLEncodedBodyDeserializer.swift */; }; 15 | 3AC631221EBB5CF8000F6353 /* URLEncodedBodyDeserializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AC631201EBB5CF8000F6353 /* URLEncodedBodyDeserializer.swift */; }; 16 | 3AC631231EBB5CF8000F6353 /* URLEncodedBodyDeserializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AC631201EBB5CF8000F6353 /* URLEncodedBodyDeserializer.swift */; }; 17 | 3AE546331E152DB600E74469 /* ResponseDetective.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AE5462A1E152DB500E74469 /* ResponseDetective.framework */; }; 18 | 3AED3ED81B1DD7B400FA35FC /* ResponseDetective.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AED3ECC1B1DD7B400FA35FC /* ResponseDetective.framework */; }; 19 | 3AF56E841E5B37A500F1CEBC /* BufferOutputFacility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E721E5B37A500F1CEBC /* BufferOutputFacility.swift */; }; 20 | 3AF56E851E5B37A500F1CEBC /* BufferOutputFacility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E721E5B37A500F1CEBC /* BufferOutputFacility.swift */; }; 21 | 3AF56E861E5B37A500F1CEBC /* BufferOutputFacility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E721E5B37A500F1CEBC /* BufferOutputFacility.swift */; }; 22 | 3AF56E871E5B37A500F1CEBC /* ConsoleOutputFacility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E731E5B37A500F1CEBC /* ConsoleOutputFacility.swift */; }; 23 | 3AF56E881E5B37A500F1CEBC /* ConsoleOutputFacility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E731E5B37A500F1CEBC /* ConsoleOutputFacility.swift */; }; 24 | 3AF56E891E5B37A500F1CEBC /* ConsoleOutputFacility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E731E5B37A500F1CEBC /* ConsoleOutputFacility.swift */; }; 25 | 3AF56E8A1E5B37A500F1CEBC /* Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E741E5B37A500F1CEBC /* Dictionary.swift */; }; 26 | 3AF56E8B1E5B37A500F1CEBC /* Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E741E5B37A500F1CEBC /* Dictionary.swift */; }; 27 | 3AF56E8C1E5B37A500F1CEBC /* Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E741E5B37A500F1CEBC /* Dictionary.swift */; }; 28 | 3AF56E8D1E5B37A500F1CEBC /* ErrorRepresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E751E5B37A500F1CEBC /* ErrorRepresentation.swift */; }; 29 | 3AF56E8E1E5B37A500F1CEBC /* ErrorRepresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E751E5B37A500F1CEBC /* ErrorRepresentation.swift */; }; 30 | 3AF56E8F1E5B37A500F1CEBC /* ErrorRepresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E751E5B37A500F1CEBC /* ErrorRepresentation.swift */; }; 31 | 3AF56E901E5B37A500F1CEBC /* ImageBodyDeserializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E761E5B37A500F1CEBC /* ImageBodyDeserializer.swift */; }; 32 | 3AF56E911E5B37A500F1CEBC /* ImageBodyDeserializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E761E5B37A500F1CEBC /* ImageBodyDeserializer.swift */; }; 33 | 3AF56E921E5B37A500F1CEBC /* ImageBodyDeserializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E761E5B37A500F1CEBC /* ImageBodyDeserializer.swift */; }; 34 | 3AF56E931E5B37A500F1CEBC /* JSONBodyDeserializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E771E5B37A500F1CEBC /* JSONBodyDeserializer.swift */; }; 35 | 3AF56E941E5B37A500F1CEBC /* JSONBodyDeserializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E771E5B37A500F1CEBC /* JSONBodyDeserializer.swift */; }; 36 | 3AF56E951E5B37A500F1CEBC /* JSONBodyDeserializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E771E5B37A500F1CEBC /* JSONBodyDeserializer.swift */; }; 37 | 3AF56E961E5B37A500F1CEBC /* OutputFacility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E781E5B37A500F1CEBC /* OutputFacility.swift */; }; 38 | 3AF56E971E5B37A500F1CEBC /* OutputFacility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E781E5B37A500F1CEBC /* OutputFacility.swift */; }; 39 | 3AF56E981E5B37A500F1CEBC /* OutputFacility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E781E5B37A500F1CEBC /* OutputFacility.swift */; }; 40 | 3AF56E991E5B37A500F1CEBC /* PlaintextBodyDeserializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E791E5B37A500F1CEBC /* PlaintextBodyDeserializer.swift */; }; 41 | 3AF56E9A1E5B37A500F1CEBC /* PlaintextBodyDeserializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E791E5B37A500F1CEBC /* PlaintextBodyDeserializer.swift */; }; 42 | 3AF56E9B1E5B37A500F1CEBC /* PlaintextBodyDeserializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E791E5B37A500F1CEBC /* PlaintextBodyDeserializer.swift */; }; 43 | 3AF56E9C1E5B37A500F1CEBC /* RDTBodyDeserializer.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AF56E7A1E5B37A500F1CEBC /* RDTBodyDeserializer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 44 | 3AF56E9D1E5B37A500F1CEBC /* RDTBodyDeserializer.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AF56E7A1E5B37A500F1CEBC /* RDTBodyDeserializer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 45 | 3AF56E9E1E5B37A500F1CEBC /* RDTBodyDeserializer.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AF56E7A1E5B37A500F1CEBC /* RDTBodyDeserializer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 46 | 3AF56E9F1E5B37A500F1CEBC /* RDTHTMLBodyDeserializer.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AF56E7B1E5B37A500F1CEBC /* RDTHTMLBodyDeserializer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 47 | 3AF56EA01E5B37A500F1CEBC /* RDTHTMLBodyDeserializer.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AF56E7B1E5B37A500F1CEBC /* RDTHTMLBodyDeserializer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 48 | 3AF56EA11E5B37A500F1CEBC /* RDTHTMLBodyDeserializer.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AF56E7B1E5B37A500F1CEBC /* RDTHTMLBodyDeserializer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 49 | 3AF56EA21E5B37A500F1CEBC /* RDTHTMLBodyDeserializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E7C1E5B37A500F1CEBC /* RDTHTMLBodyDeserializer.m */; }; 50 | 3AF56EA31E5B37A500F1CEBC /* RDTHTMLBodyDeserializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E7C1E5B37A500F1CEBC /* RDTHTMLBodyDeserializer.m */; }; 51 | 3AF56EA41E5B37A500F1CEBC /* RDTHTMLBodyDeserializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E7C1E5B37A500F1CEBC /* RDTHTMLBodyDeserializer.m */; }; 52 | 3AF56EA51E5B37A500F1CEBC /* RDTXMLBodyDeserializer.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AF56E7D1E5B37A500F1CEBC /* RDTXMLBodyDeserializer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 53 | 3AF56EA61E5B37A500F1CEBC /* RDTXMLBodyDeserializer.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AF56E7D1E5B37A500F1CEBC /* RDTXMLBodyDeserializer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 54 | 3AF56EA71E5B37A500F1CEBC /* RDTXMLBodyDeserializer.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AF56E7D1E5B37A500F1CEBC /* RDTXMLBodyDeserializer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 55 | 3AF56EA81E5B37A500F1CEBC /* RDTXMLBodyDeserializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E7E1E5B37A500F1CEBC /* RDTXMLBodyDeserializer.m */; }; 56 | 3AF56EA91E5B37A500F1CEBC /* RDTXMLBodyDeserializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E7E1E5B37A500F1CEBC /* RDTXMLBodyDeserializer.m */; }; 57 | 3AF56EAA1E5B37A500F1CEBC /* RDTXMLBodyDeserializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E7E1E5B37A500F1CEBC /* RDTXMLBodyDeserializer.m */; }; 58 | 3AF56EAB1E5B37A500F1CEBC /* RequestRepresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E7F1E5B37A500F1CEBC /* RequestRepresentation.swift */; }; 59 | 3AF56EAC1E5B37A500F1CEBC /* RequestRepresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E7F1E5B37A500F1CEBC /* RequestRepresentation.swift */; }; 60 | 3AF56EAD1E5B37A500F1CEBC /* RequestRepresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E7F1E5B37A500F1CEBC /* RequestRepresentation.swift */; }; 61 | 3AF56EAE1E5B37A500F1CEBC /* ResponseDetective.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AF56E801E5B37A500F1CEBC /* ResponseDetective.h */; settings = {ATTRIBUTES = (Public, ); }; }; 62 | 3AF56EAF1E5B37A500F1CEBC /* ResponseDetective.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AF56E801E5B37A500F1CEBC /* ResponseDetective.h */; settings = {ATTRIBUTES = (Public, ); }; }; 63 | 3AF56EB01E5B37A500F1CEBC /* ResponseDetective.h in Headers */ = {isa = PBXBuildFile; fileRef = 3AF56E801E5B37A500F1CEBC /* ResponseDetective.h */; settings = {ATTRIBUTES = (Public, ); }; }; 64 | 3AF56EB11E5B37A500F1CEBC /* ResponseDetective.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E811E5B37A500F1CEBC /* ResponseDetective.swift */; }; 65 | 3AF56EB21E5B37A500F1CEBC /* ResponseDetective.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E811E5B37A500F1CEBC /* ResponseDetective.swift */; }; 66 | 3AF56EB31E5B37A500F1CEBC /* ResponseDetective.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E811E5B37A500F1CEBC /* ResponseDetective.swift */; }; 67 | 3AF56EB41E5B37A500F1CEBC /* ResponseRepresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E821E5B37A500F1CEBC /* ResponseRepresentation.swift */; }; 68 | 3AF56EB51E5B37A500F1CEBC /* ResponseRepresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E821E5B37A500F1CEBC /* ResponseRepresentation.swift */; }; 69 | 3AF56EB61E5B37A500F1CEBC /* ResponseRepresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E821E5B37A500F1CEBC /* ResponseRepresentation.swift */; }; 70 | 3AF56EB71E5B37A500F1CEBC /* URLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E831E5B37A500F1CEBC /* URLProtocol.swift */; }; 71 | 3AF56EB81E5B37A500F1CEBC /* URLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E831E5B37A500F1CEBC /* URLProtocol.swift */; }; 72 | 3AF56EB91E5B37A500F1CEBC /* URLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56E831E5B37A500F1CEBC /* URLProtocol.swift */; }; 73 | 3AF56ECF1E5B37B500F1CEBC /* BufferOutputFacilitySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC01E5B37B500F1CEBC /* BufferOutputFacilitySpec.swift */; }; 74 | 3AF56ED01E5B37B500F1CEBC /* BufferOutputFacilitySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC01E5B37B500F1CEBC /* BufferOutputFacilitySpec.swift */; }; 75 | 3AF56ED11E5B37B500F1CEBC /* BufferOutputFacilitySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC01E5B37B500F1CEBC /* BufferOutputFacilitySpec.swift */; }; 76 | 3AF56ED21E5B37B500F1CEBC /* ConsoleOutputFacilitySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC11E5B37B500F1CEBC /* ConsoleOutputFacilitySpec.swift */; }; 77 | 3AF56ED31E5B37B500F1CEBC /* ConsoleOutputFacilitySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC11E5B37B500F1CEBC /* ConsoleOutputFacilitySpec.swift */; }; 78 | 3AF56ED41E5B37B500F1CEBC /* ConsoleOutputFacilitySpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC11E5B37B500F1CEBC /* ConsoleOutputFacilitySpec.swift */; }; 79 | 3AF56ED51E5B37B500F1CEBC /* ErrorRepresentationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC21E5B37B500F1CEBC /* ErrorRepresentationSpec.swift */; }; 80 | 3AF56ED61E5B37B500F1CEBC /* ErrorRepresentationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC21E5B37B500F1CEBC /* ErrorRepresentationSpec.swift */; }; 81 | 3AF56ED71E5B37B500F1CEBC /* ErrorRepresentationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC21E5B37B500F1CEBC /* ErrorRepresentationSpec.swift */; }; 82 | 3AF56ED81E5B37B500F1CEBC /* HTMLBodyDeserializerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC31E5B37B500F1CEBC /* HTMLBodyDeserializerSpec.swift */; }; 83 | 3AF56ED91E5B37B500F1CEBC /* HTMLBodyDeserializerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC31E5B37B500F1CEBC /* HTMLBodyDeserializerSpec.swift */; }; 84 | 3AF56EDA1E5B37B500F1CEBC /* HTMLBodyDeserializerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC31E5B37B500F1CEBC /* HTMLBodyDeserializerSpec.swift */; }; 85 | 3AF56EDB1E5B37B500F1CEBC /* ImageBodyDeserializerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC41E5B37B500F1CEBC /* ImageBodyDeserializerSpec.swift */; }; 86 | 3AF56EDC1E5B37B500F1CEBC /* ImageBodyDeserializerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC41E5B37B500F1CEBC /* ImageBodyDeserializerSpec.swift */; }; 87 | 3AF56EDD1E5B37B500F1CEBC /* ImageBodyDeserializerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC41E5B37B500F1CEBC /* ImageBodyDeserializerSpec.swift */; }; 88 | 3AF56EDE1E5B37B500F1CEBC /* JSONBodyDeserializerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC51E5B37B500F1CEBC /* JSONBodyDeserializerSpec.swift */; }; 89 | 3AF56EDF1E5B37B500F1CEBC /* JSONBodyDeserializerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC51E5B37B500F1CEBC /* JSONBodyDeserializerSpec.swift */; }; 90 | 3AF56EE01E5B37B500F1CEBC /* JSONBodyDeserializerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC51E5B37B500F1CEBC /* JSONBodyDeserializerSpec.swift */; }; 91 | 3AF56EE11E5B37B500F1CEBC /* PlaintextBodyDeserializerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC61E5B37B500F1CEBC /* PlaintextBodyDeserializerSpec.swift */; }; 92 | 3AF56EE21E5B37B500F1CEBC /* PlaintextBodyDeserializerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC61E5B37B500F1CEBC /* PlaintextBodyDeserializerSpec.swift */; }; 93 | 3AF56EE31E5B37B500F1CEBC /* PlaintextBodyDeserializerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC61E5B37B500F1CEBC /* PlaintextBodyDeserializerSpec.swift */; }; 94 | 3AF56EE41E5B37B500F1CEBC /* RequestRepresentationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC71E5B37B500F1CEBC /* RequestRepresentationSpec.swift */; }; 95 | 3AF56EE51E5B37B500F1CEBC /* RequestRepresentationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC71E5B37B500F1CEBC /* RequestRepresentationSpec.swift */; }; 96 | 3AF56EE61E5B37B500F1CEBC /* RequestRepresentationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC71E5B37B500F1CEBC /* RequestRepresentationSpec.swift */; }; 97 | 3AF56EE71E5B37B500F1CEBC /* ResponseDetectiveSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC81E5B37B500F1CEBC /* ResponseDetectiveSpec.swift */; }; 98 | 3AF56EE81E5B37B500F1CEBC /* ResponseDetectiveSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC81E5B37B500F1CEBC /* ResponseDetectiveSpec.swift */; }; 99 | 3AF56EE91E5B37B500F1CEBC /* ResponseDetectiveSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC81E5B37B500F1CEBC /* ResponseDetectiveSpec.swift */; }; 100 | 3AF56EEA1E5B37B500F1CEBC /* ResponseRepresentationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC91E5B37B500F1CEBC /* ResponseRepresentationSpec.swift */; }; 101 | 3AF56EEB1E5B37B500F1CEBC /* ResponseRepresentationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC91E5B37B500F1CEBC /* ResponseRepresentationSpec.swift */; }; 102 | 3AF56EEC1E5B37B500F1CEBC /* ResponseRepresentationSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56EC91E5B37B500F1CEBC /* ResponseRepresentationSpec.swift */; }; 103 | 3AF56EF01E5B37B500F1CEBC /* XMLBodyDeserializerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56ECB1E5B37B500F1CEBC /* XMLBodyDeserializerSpec.swift */; }; 104 | 3AF56EF11E5B37B500F1CEBC /* XMLBodyDeserializerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56ECB1E5B37B500F1CEBC /* XMLBodyDeserializerSpec.swift */; }; 105 | 3AF56EF21E5B37B500F1CEBC /* XMLBodyDeserializerSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56ECB1E5B37B500F1CEBC /* XMLBodyDeserializerSpec.swift */; }; 106 | 3AF56F0C1E5B4ED800F1CEBC /* TestBodyDeserializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56F0B1E5B4ED800F1CEBC /* TestBodyDeserializer.swift */; }; 107 | 3AF56F0D1E5B4ED800F1CEBC /* TestBodyDeserializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56F0B1E5B4ED800F1CEBC /* TestBodyDeserializer.swift */; }; 108 | 3AF56F0E1E5B4ED800F1CEBC /* TestBodyDeserializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF56F0B1E5B4ED800F1CEBC /* TestBodyDeserializer.swift */; }; 109 | /* End PBXBuildFile section */ 110 | 111 | /* Begin PBXContainerItemProxy section */ 112 | 3A2007E41B5501BC00769021 /* PBXContainerItemProxy */ = { 113 | isa = PBXContainerItemProxy; 114 | containerPortal = 3AED3EC31B1DD7B400FA35FC /* Project object */; 115 | proxyType = 1; 116 | remoteGlobalIDString = 3A2007D71B5501BC00769021; 117 | remoteInfo = "ResponseDetective (OS X)"; 118 | }; 119 | 3AE546341E152DB600E74469 /* PBXContainerItemProxy */ = { 120 | isa = PBXContainerItemProxy; 121 | containerPortal = 3AED3EC31B1DD7B400FA35FC /* Project object */; 122 | proxyType = 1; 123 | remoteGlobalIDString = 3AE546291E152DB500E74469; 124 | remoteInfo = "ResponseDetective (tvOS)"; 125 | }; 126 | 3AED3ED91B1DD7B400FA35FC /* PBXContainerItemProxy */ = { 127 | isa = PBXContainerItemProxy; 128 | containerPortal = 3AED3EC31B1DD7B400FA35FC /* Project object */; 129 | proxyType = 1; 130 | remoteGlobalIDString = 3AED3ECB1B1DD7B400FA35FC; 131 | remoteInfo = ResponseDetective; 132 | }; 133 | /* End PBXContainerItemProxy section */ 134 | 135 | /* Begin PBXFileReference section */ 136 | 3A2007D81B5501BC00769021 /* ResponseDetective.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ResponseDetective.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 137 | 3A2007E21B5501BC00769021 /* ResponseDetectiveTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ResponseDetectiveTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 138 | 3A5A99CB1E604064002EFC39 /* bitrise.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = bitrise.yml; sourceTree = ""; }; 139 | 3A5A99CC1E604064002EFC39 /* Cartfile.private */ = {isa = PBXFileReference; lastKnownFileType = text; path = Cartfile.private; sourceTree = ""; }; 140 | 3A5A99CD1E604064002EFC39 /* ResponseDetective.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = ResponseDetective.podspec; sourceTree = ""; }; 141 | 3A7A4A831EC71336005E2E9D /* Common-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Common-Base.xcconfig"; sourceTree = ""; }; 142 | 3A7A4A841EC71410005E2E9D /* Framework-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Framework-Base.xcconfig"; sourceTree = ""; }; 143 | 3AB9E97E1EBB64C0004E575E /* URLEncodedBodyDeserializerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLEncodedBodyDeserializerSpec.swift; sourceTree = ""; }; 144 | 3AC631201EBB5CF8000F6353 /* URLEncodedBodyDeserializer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLEncodedBodyDeserializer.swift; sourceTree = ""; }; 145 | 3AE5462A1E152DB500E74469 /* ResponseDetective.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ResponseDetective.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 146 | 3AE546321E152DB600E74469 /* ResponseDetectiveTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ResponseDetectiveTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 147 | 3AED3ECC1B1DD7B400FA35FC /* ResponseDetective.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ResponseDetective.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 148 | 3AED3ED71B1DD7B400FA35FC /* ResponseDetectiveTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ResponseDetectiveTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 149 | 3AF56E6F1E5B379A00F1CEBC /* Framework-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Framework-Info.plist"; sourceTree = ""; }; 150 | 3AF56E701E5B379A00F1CEBC /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; }; 151 | 3AF56E721E5B37A500F1CEBC /* BufferOutputFacility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BufferOutputFacility.swift; sourceTree = ""; }; 152 | 3AF56E731E5B37A500F1CEBC /* ConsoleOutputFacility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsoleOutputFacility.swift; sourceTree = ""; }; 153 | 3AF56E741E5B37A500F1CEBC /* Dictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Dictionary.swift; sourceTree = ""; }; 154 | 3AF56E751E5B37A500F1CEBC /* ErrorRepresentation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorRepresentation.swift; sourceTree = ""; }; 155 | 3AF56E761E5B37A500F1CEBC /* ImageBodyDeserializer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageBodyDeserializer.swift; sourceTree = ""; }; 156 | 3AF56E771E5B37A500F1CEBC /* JSONBodyDeserializer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONBodyDeserializer.swift; sourceTree = ""; }; 157 | 3AF56E781E5B37A500F1CEBC /* OutputFacility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutputFacility.swift; sourceTree = ""; }; 158 | 3AF56E791E5B37A500F1CEBC /* PlaintextBodyDeserializer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlaintextBodyDeserializer.swift; sourceTree = ""; }; 159 | 3AF56E7A1E5B37A500F1CEBC /* RDTBodyDeserializer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RDTBodyDeserializer.h; sourceTree = ""; }; 160 | 3AF56E7B1E5B37A500F1CEBC /* RDTHTMLBodyDeserializer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RDTHTMLBodyDeserializer.h; sourceTree = ""; }; 161 | 3AF56E7C1E5B37A500F1CEBC /* RDTHTMLBodyDeserializer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RDTHTMLBodyDeserializer.m; sourceTree = ""; }; 162 | 3AF56E7D1E5B37A500F1CEBC /* RDTXMLBodyDeserializer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RDTXMLBodyDeserializer.h; sourceTree = ""; }; 163 | 3AF56E7E1E5B37A500F1CEBC /* RDTXMLBodyDeserializer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RDTXMLBodyDeserializer.m; sourceTree = ""; }; 164 | 3AF56E7F1E5B37A500F1CEBC /* RequestRepresentation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestRepresentation.swift; sourceTree = ""; }; 165 | 3AF56E801E5B37A500F1CEBC /* ResponseDetective.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResponseDetective.h; sourceTree = ""; }; 166 | 3AF56E811E5B37A500F1CEBC /* ResponseDetective.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResponseDetective.swift; sourceTree = ""; }; 167 | 3AF56E821E5B37A500F1CEBC /* ResponseRepresentation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResponseRepresentation.swift; sourceTree = ""; }; 168 | 3AF56E831E5B37A500F1CEBC /* URLProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLProtocol.swift; sourceTree = ""; }; 169 | 3AF56EC01E5B37B500F1CEBC /* BufferOutputFacilitySpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BufferOutputFacilitySpec.swift; sourceTree = ""; }; 170 | 3AF56EC11E5B37B500F1CEBC /* ConsoleOutputFacilitySpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsoleOutputFacilitySpec.swift; sourceTree = ""; }; 171 | 3AF56EC21E5B37B500F1CEBC /* ErrorRepresentationSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorRepresentationSpec.swift; sourceTree = ""; }; 172 | 3AF56EC31E5B37B500F1CEBC /* HTMLBodyDeserializerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTMLBodyDeserializerSpec.swift; sourceTree = ""; }; 173 | 3AF56EC41E5B37B500F1CEBC /* ImageBodyDeserializerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageBodyDeserializerSpec.swift; sourceTree = ""; }; 174 | 3AF56EC51E5B37B500F1CEBC /* JSONBodyDeserializerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONBodyDeserializerSpec.swift; sourceTree = ""; }; 175 | 3AF56EC61E5B37B500F1CEBC /* PlaintextBodyDeserializerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlaintextBodyDeserializerSpec.swift; sourceTree = ""; }; 176 | 3AF56EC71E5B37B500F1CEBC /* RequestRepresentationSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestRepresentationSpec.swift; sourceTree = ""; }; 177 | 3AF56EC81E5B37B500F1CEBC /* ResponseDetectiveSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResponseDetectiveSpec.swift; sourceTree = ""; }; 178 | 3AF56EC91E5B37B500F1CEBC /* ResponseRepresentationSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResponseRepresentationSpec.swift; sourceTree = ""; }; 179 | 3AF56ECB1E5B37B500F1CEBC /* XMLBodyDeserializerSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XMLBodyDeserializerSpec.swift; sourceTree = ""; }; 180 | 3AF56F0B1E5B4ED800F1CEBC /* TestBodyDeserializer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestBodyDeserializer.swift; sourceTree = ""; }; 181 | 3AF695861ECB199700586DA7 /* Common-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Common-Debug.xcconfig"; sourceTree = ""; }; 182 | 3AF695871ECB19CC00586DA7 /* Framework-iOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Framework-iOS.xcconfig"; sourceTree = ""; }; 183 | 3AF695891ECB1A6A00586DA7 /* Tests-Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Tests-Base.xcconfig"; sourceTree = ""; }; 184 | 3AF6958A1ECB1A7900586DA7 /* Common-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Common-Release.xcconfig"; sourceTree = ""; }; 185 | 3AF6958B1ECB1B5100586DA7 /* Tests-iOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Tests-iOS.xcconfig"; sourceTree = ""; }; 186 | 3AF6958C1ECB1D2B00586DA7 /* Framework-macOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Framework-macOS.xcconfig"; sourceTree = ""; }; 187 | 3AF6958D1ECB1D4200586DA7 /* Framework-tvOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Framework-tvOS.xcconfig"; sourceTree = ""; }; 188 | 3AF6958E1ECB1D5800586DA7 /* Tests-macOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Tests-macOS.xcconfig"; sourceTree = ""; }; 189 | 3AF6958F1ECB1D6600586DA7 /* Tests-tvOS.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Tests-tvOS.xcconfig"; sourceTree = ""; }; 190 | 3AF96F121E60647900A9831E /* Framework.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = Framework.modulemap; sourceTree = ""; }; 191 | 3AF96F131E607C8F00A9831E /* ResponseDetective.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = ResponseDetective.playground; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 192 | /* End PBXFileReference section */ 193 | 194 | /* Begin PBXFrameworksBuildPhase section */ 195 | 3A2007D41B5501BC00769021 /* Frameworks */ = { 196 | isa = PBXFrameworksBuildPhase; 197 | buildActionMask = 2147483647; 198 | files = ( 199 | ); 200 | runOnlyForDeploymentPostprocessing = 0; 201 | }; 202 | 3A2007DF1B5501BC00769021 /* Frameworks */ = { 203 | isa = PBXFrameworksBuildPhase; 204 | buildActionMask = 2147483647; 205 | files = ( 206 | 3A2007E31B5501BC00769021 /* ResponseDetective.framework in Frameworks */, 207 | ); 208 | runOnlyForDeploymentPostprocessing = 0; 209 | }; 210 | 3AE546261E152DB500E74469 /* Frameworks */ = { 211 | isa = PBXFrameworksBuildPhase; 212 | buildActionMask = 2147483647; 213 | files = ( 214 | ); 215 | runOnlyForDeploymentPostprocessing = 0; 216 | }; 217 | 3AE5462F1E152DB600E74469 /* Frameworks */ = { 218 | isa = PBXFrameworksBuildPhase; 219 | buildActionMask = 2147483647; 220 | files = ( 221 | 3AE546331E152DB600E74469 /* ResponseDetective.framework in Frameworks */, 222 | ); 223 | runOnlyForDeploymentPostprocessing = 0; 224 | }; 225 | 3AED3EC81B1DD7B400FA35FC /* Frameworks */ = { 226 | isa = PBXFrameworksBuildPhase; 227 | buildActionMask = 2147483647; 228 | files = ( 229 | ); 230 | runOnlyForDeploymentPostprocessing = 0; 231 | }; 232 | 3AED3ED41B1DD7B400FA35FC /* Frameworks */ = { 233 | isa = PBXFrameworksBuildPhase; 234 | buildActionMask = 2147483647; 235 | files = ( 236 | 3AED3ED81B1DD7B400FA35FC /* ResponseDetective.framework in Frameworks */, 237 | ); 238 | runOnlyForDeploymentPostprocessing = 0; 239 | }; 240 | /* End PBXFrameworksBuildPhase section */ 241 | 242 | /* Begin PBXGroup section */ 243 | 3A5A99CA1E60404E002EFC39 /* Other Files */ = { 244 | isa = PBXGroup; 245 | children = ( 246 | 3A5A99CB1E604064002EFC39 /* bitrise.yml */, 247 | 3A5A99CC1E604064002EFC39 /* Cartfile.private */, 248 | 3A5A99CD1E604064002EFC39 /* ResponseDetective.podspec */, 249 | ); 250 | name = "Other Files"; 251 | sourceTree = ""; 252 | }; 253 | 3A7A4A821EC712FA005E2E9D /* Configuration */ = { 254 | isa = PBXGroup; 255 | children = ( 256 | 3AF695831ECB192F00586DA7 /* Common */, 257 | 3AF695841ECB193800586DA7 /* Framework */, 258 | 3AF695881ECB1A5700586DA7 /* Tests */, 259 | ); 260 | name = Configuration; 261 | path = ResponseDetective/Configuration; 262 | sourceTree = ""; 263 | }; 264 | 3AED3EC21B1DD7B400FA35FC = { 265 | isa = PBXGroup; 266 | children = ( 267 | 3AF96F131E607C8F00A9831E /* ResponseDetective.playground */, 268 | 3AF56E711E5B37A500F1CEBC /* Sources */, 269 | 3AF56EBA1E5B37B500F1CEBC /* Tests */, 270 | 3AF56E6E1E5B379A00F1CEBC /* Resources */, 271 | 3A7A4A821EC712FA005E2E9D /* Configuration */, 272 | 3A5A99CA1E60404E002EFC39 /* Other Files */, 273 | 3AED3ECD1B1DD7B400FA35FC /* Products */, 274 | ); 275 | sourceTree = ""; 276 | }; 277 | 3AED3ECD1B1DD7B400FA35FC /* Products */ = { 278 | isa = PBXGroup; 279 | children = ( 280 | 3AED3ECC1B1DD7B400FA35FC /* ResponseDetective.framework */, 281 | 3AED3ED71B1DD7B400FA35FC /* ResponseDetectiveTests.xctest */, 282 | 3A2007D81B5501BC00769021 /* ResponseDetective.framework */, 283 | 3A2007E21B5501BC00769021 /* ResponseDetectiveTests.xctest */, 284 | 3AE5462A1E152DB500E74469 /* ResponseDetective.framework */, 285 | 3AE546321E152DB600E74469 /* ResponseDetectiveTests.xctest */, 286 | ); 287 | name = Products; 288 | sourceTree = ""; 289 | }; 290 | 3AF56E6E1E5B379A00F1CEBC /* Resources */ = { 291 | isa = PBXGroup; 292 | children = ( 293 | 3AF96F121E60647900A9831E /* Framework.modulemap */, 294 | 3AF56E6F1E5B379A00F1CEBC /* Framework-Info.plist */, 295 | 3AF56E701E5B379A00F1CEBC /* Tests-Info.plist */, 296 | ); 297 | name = Resources; 298 | path = ResponseDetective/Resources; 299 | sourceTree = ""; 300 | }; 301 | 3AF56E711E5B37A500F1CEBC /* Sources */ = { 302 | isa = PBXGroup; 303 | children = ( 304 | 3AF56E811E5B37A500F1CEBC /* ResponseDetective.swift */, 305 | 3AF56EFD1E5B3CE700F1CEBC /* Representations */, 306 | 3AF56EFE1E5B3CF900F1CEBC /* Body Deserializers */, 307 | 3AF56F011E5B3D3A00F1CEBC /* Output Facilities */, 308 | 3AF56F031E5B3D5C00F1CEBC /* Internal */, 309 | 3AF56F021E5B3D4900F1CEBC /* Extensions */, 310 | ); 311 | name = Sources; 312 | path = ResponseDetective/Sources; 313 | sourceTree = ""; 314 | }; 315 | 3AF56EBA1E5B37B500F1CEBC /* Tests */ = { 316 | isa = PBXGroup; 317 | children = ( 318 | 3AF56EBB1E5B37B500F1CEBC /* Additions */, 319 | 3AF56EBF1E5B37B500F1CEBC /* Specs */, 320 | ); 321 | name = Tests; 322 | path = ResponseDetective/Tests; 323 | sourceTree = ""; 324 | }; 325 | 3AF56EBB1E5B37B500F1CEBC /* Additions */ = { 326 | isa = PBXGroup; 327 | children = ( 328 | 3AF56F0B1E5B4ED800F1CEBC /* TestBodyDeserializer.swift */, 329 | ); 330 | path = Additions; 331 | sourceTree = ""; 332 | }; 333 | 3AF56EBF1E5B37B500F1CEBC /* Specs */ = { 334 | isa = PBXGroup; 335 | children = ( 336 | 3AF56EC81E5B37B500F1CEBC /* ResponseDetectiveSpec.swift */, 337 | 3AF56F081E5B4E7700F1CEBC /* Representations */, 338 | 3AF56F091E5B4E8800F1CEBC /* Body Deserializers */, 339 | 3AF56F0A1E5B4E9A00F1CEBC /* Output Facilities */, 340 | ); 341 | path = Specs; 342 | sourceTree = ""; 343 | }; 344 | 3AF56EFD1E5B3CE700F1CEBC /* Representations */ = { 345 | isa = PBXGroup; 346 | children = ( 347 | 3AF56E7F1E5B37A500F1CEBC /* RequestRepresentation.swift */, 348 | 3AF56E821E5B37A500F1CEBC /* ResponseRepresentation.swift */, 349 | 3AF56E751E5B37A500F1CEBC /* ErrorRepresentation.swift */, 350 | ); 351 | name = Representations; 352 | sourceTree = ""; 353 | }; 354 | 3AF56EFE1E5B3CF900F1CEBC /* Body Deserializers */ = { 355 | isa = PBXGroup; 356 | children = ( 357 | 3AF56E7A1E5B37A500F1CEBC /* RDTBodyDeserializer.h */, 358 | 3AF56E791E5B37A500F1CEBC /* PlaintextBodyDeserializer.swift */, 359 | 3AF56E761E5B37A500F1CEBC /* ImageBodyDeserializer.swift */, 360 | 3AF56E771E5B37A500F1CEBC /* JSONBodyDeserializer.swift */, 361 | 3AC631201EBB5CF8000F6353 /* URLEncodedBodyDeserializer.swift */, 362 | 3AF56EFF1E5B3D1200F1CEBC /* XMLBodyDeserializer */, 363 | 3AF56F001E5B3D2200F1CEBC /* HTMLBodyDeserializer */, 364 | ); 365 | name = "Body Deserializers"; 366 | sourceTree = ""; 367 | }; 368 | 3AF56EFF1E5B3D1200F1CEBC /* XMLBodyDeserializer */ = { 369 | isa = PBXGroup; 370 | children = ( 371 | 3AF56E7D1E5B37A500F1CEBC /* RDTXMLBodyDeserializer.h */, 372 | 3AF56E7E1E5B37A500F1CEBC /* RDTXMLBodyDeserializer.m */, 373 | ); 374 | name = XMLBodyDeserializer; 375 | sourceTree = ""; 376 | }; 377 | 3AF56F001E5B3D2200F1CEBC /* HTMLBodyDeserializer */ = { 378 | isa = PBXGroup; 379 | children = ( 380 | 3AF56E7B1E5B37A500F1CEBC /* RDTHTMLBodyDeserializer.h */, 381 | 3AF56E7C1E5B37A500F1CEBC /* RDTHTMLBodyDeserializer.m */, 382 | ); 383 | name = HTMLBodyDeserializer; 384 | sourceTree = ""; 385 | }; 386 | 3AF56F011E5B3D3A00F1CEBC /* Output Facilities */ = { 387 | isa = PBXGroup; 388 | children = ( 389 | 3AF56E781E5B37A500F1CEBC /* OutputFacility.swift */, 390 | 3AF56E721E5B37A500F1CEBC /* BufferOutputFacility.swift */, 391 | 3AF56E731E5B37A500F1CEBC /* ConsoleOutputFacility.swift */, 392 | ); 393 | name = "Output Facilities"; 394 | sourceTree = ""; 395 | }; 396 | 3AF56F021E5B3D4900F1CEBC /* Extensions */ = { 397 | isa = PBXGroup; 398 | children = ( 399 | 3AF56E741E5B37A500F1CEBC /* Dictionary.swift */, 400 | ); 401 | name = Extensions; 402 | sourceTree = ""; 403 | }; 404 | 3AF56F031E5B3D5C00F1CEBC /* Internal */ = { 405 | isa = PBXGroup; 406 | children = ( 407 | 3AF56E801E5B37A500F1CEBC /* ResponseDetective.h */, 408 | 3AF56E831E5B37A500F1CEBC /* URLProtocol.swift */, 409 | ); 410 | name = Internal; 411 | sourceTree = ""; 412 | }; 413 | 3AF56F081E5B4E7700F1CEBC /* Representations */ = { 414 | isa = PBXGroup; 415 | children = ( 416 | 3AF56EC71E5B37B500F1CEBC /* RequestRepresentationSpec.swift */, 417 | 3AF56EC91E5B37B500F1CEBC /* ResponseRepresentationSpec.swift */, 418 | 3AF56EC21E5B37B500F1CEBC /* ErrorRepresentationSpec.swift */, 419 | ); 420 | name = Representations; 421 | sourceTree = ""; 422 | }; 423 | 3AF56F091E5B4E8800F1CEBC /* Body Deserializers */ = { 424 | isa = PBXGroup; 425 | children = ( 426 | 3AF56EC41E5B37B500F1CEBC /* ImageBodyDeserializerSpec.swift */, 427 | 3AF56EC31E5B37B500F1CEBC /* HTMLBodyDeserializerSpec.swift */, 428 | 3AF56ECB1E5B37B500F1CEBC /* XMLBodyDeserializerSpec.swift */, 429 | 3AF56EC61E5B37B500F1CEBC /* PlaintextBodyDeserializerSpec.swift */, 430 | 3AF56EC51E5B37B500F1CEBC /* JSONBodyDeserializerSpec.swift */, 431 | 3AB9E97E1EBB64C0004E575E /* URLEncodedBodyDeserializerSpec.swift */, 432 | ); 433 | name = "Body Deserializers"; 434 | sourceTree = ""; 435 | }; 436 | 3AF56F0A1E5B4E9A00F1CEBC /* Output Facilities */ = { 437 | isa = PBXGroup; 438 | children = ( 439 | 3AF56EC01E5B37B500F1CEBC /* BufferOutputFacilitySpec.swift */, 440 | 3AF56EC11E5B37B500F1CEBC /* ConsoleOutputFacilitySpec.swift */, 441 | ); 442 | name = "Output Facilities"; 443 | sourceTree = ""; 444 | }; 445 | 3AF695831ECB192F00586DA7 /* Common */ = { 446 | isa = PBXGroup; 447 | children = ( 448 | 3A7A4A831EC71336005E2E9D /* Common-Base.xcconfig */, 449 | 3AF695861ECB199700586DA7 /* Common-Debug.xcconfig */, 450 | 3AF6958A1ECB1A7900586DA7 /* Common-Release.xcconfig */, 451 | ); 452 | name = Common; 453 | sourceTree = ""; 454 | }; 455 | 3AF695841ECB193800586DA7 /* Framework */ = { 456 | isa = PBXGroup; 457 | children = ( 458 | 3A7A4A841EC71410005E2E9D /* Framework-Base.xcconfig */, 459 | 3AF695871ECB19CC00586DA7 /* Framework-iOS.xcconfig */, 460 | 3AF6958C1ECB1D2B00586DA7 /* Framework-macOS.xcconfig */, 461 | 3AF6958D1ECB1D4200586DA7 /* Framework-tvOS.xcconfig */, 462 | ); 463 | name = Framework; 464 | sourceTree = ""; 465 | }; 466 | 3AF695881ECB1A5700586DA7 /* Tests */ = { 467 | isa = PBXGroup; 468 | children = ( 469 | 3AF695891ECB1A6A00586DA7 /* Tests-Base.xcconfig */, 470 | 3AF6958B1ECB1B5100586DA7 /* Tests-iOS.xcconfig */, 471 | 3AF6958E1ECB1D5800586DA7 /* Tests-macOS.xcconfig */, 472 | 3AF6958F1ECB1D6600586DA7 /* Tests-tvOS.xcconfig */, 473 | ); 474 | name = Tests; 475 | sourceTree = ""; 476 | }; 477 | /* End PBXGroup section */ 478 | 479 | /* Begin PBXHeadersBuildPhase section */ 480 | 3A2007D51B5501BC00769021 /* Headers */ = { 481 | isa = PBXHeadersBuildPhase; 482 | buildActionMask = 2147483647; 483 | files = ( 484 | 3AF56E9D1E5B37A500F1CEBC /* RDTBodyDeserializer.h in Headers */, 485 | 3AF56EAF1E5B37A500F1CEBC /* ResponseDetective.h in Headers */, 486 | 3AF56EA61E5B37A500F1CEBC /* RDTXMLBodyDeserializer.h in Headers */, 487 | 3AF56EA01E5B37A500F1CEBC /* RDTHTMLBodyDeserializer.h in Headers */, 488 | ); 489 | runOnlyForDeploymentPostprocessing = 0; 490 | }; 491 | 3AE546271E152DB500E74469 /* Headers */ = { 492 | isa = PBXHeadersBuildPhase; 493 | buildActionMask = 2147483647; 494 | files = ( 495 | 3AF56E9E1E5B37A500F1CEBC /* RDTBodyDeserializer.h in Headers */, 496 | 3AF56EB01E5B37A500F1CEBC /* ResponseDetective.h in Headers */, 497 | 3AF56EA71E5B37A500F1CEBC /* RDTXMLBodyDeserializer.h in Headers */, 498 | 3AF56EA11E5B37A500F1CEBC /* RDTHTMLBodyDeserializer.h in Headers */, 499 | ); 500 | runOnlyForDeploymentPostprocessing = 0; 501 | }; 502 | 3AED3EC91B1DD7B400FA35FC /* Headers */ = { 503 | isa = PBXHeadersBuildPhase; 504 | buildActionMask = 2147483647; 505 | files = ( 506 | 3AF56E9C1E5B37A500F1CEBC /* RDTBodyDeserializer.h in Headers */, 507 | 3AF56EAE1E5B37A500F1CEBC /* ResponseDetective.h in Headers */, 508 | 3AF56EA51E5B37A500F1CEBC /* RDTXMLBodyDeserializer.h in Headers */, 509 | 3AF56E9F1E5B37A500F1CEBC /* RDTHTMLBodyDeserializer.h in Headers */, 510 | ); 511 | runOnlyForDeploymentPostprocessing = 0; 512 | }; 513 | /* End PBXHeadersBuildPhase section */ 514 | 515 | /* Begin PBXNativeTarget section */ 516 | 3A2007D71B5501BC00769021 /* ResponseDetective-macOS */ = { 517 | isa = PBXNativeTarget; 518 | buildConfigurationList = 3A2007EB1B5501BC00769021 /* Build configuration list for PBXNativeTarget "ResponseDetective-macOS" */; 519 | buildPhases = ( 520 | 3A2007D31B5501BC00769021 /* Sources */, 521 | 3A2007D41B5501BC00769021 /* Frameworks */, 522 | 3A2007D51B5501BC00769021 /* Headers */, 523 | 3A2007D61B5501BC00769021 /* Resources */, 524 | ); 525 | buildRules = ( 526 | ); 527 | dependencies = ( 528 | ); 529 | name = "ResponseDetective-macOS"; 530 | productName = "ResponseDetective (OS X)"; 531 | productReference = 3A2007D81B5501BC00769021 /* ResponseDetective.framework */; 532 | productType = "com.apple.product-type.framework"; 533 | }; 534 | 3A2007E11B5501BC00769021 /* ResponseDetective-macOS-Tests */ = { 535 | isa = PBXNativeTarget; 536 | buildConfigurationList = 3A2007EE1B5501BC00769021 /* Build configuration list for PBXNativeTarget "ResponseDetective-macOS-Tests" */; 537 | buildPhases = ( 538 | 3A2007DE1B5501BC00769021 /* Sources */, 539 | 3A2007DF1B5501BC00769021 /* Frameworks */, 540 | 3A2007E01B5501BC00769021 /* Resources */, 541 | 3A7D018A1CD3C09B00B7FBD7 /* Copy Carthage Frameworks */, 542 | ); 543 | buildRules = ( 544 | ); 545 | dependencies = ( 546 | 3A2007E51B5501BC00769021 /* PBXTargetDependency */, 547 | ); 548 | name = "ResponseDetective-macOS-Tests"; 549 | productName = "ResponseDetective (OS X)Tests"; 550 | productReference = 3A2007E21B5501BC00769021 /* ResponseDetectiveTests.xctest */; 551 | productType = "com.apple.product-type.bundle.unit-test"; 552 | }; 553 | 3AE546291E152DB500E74469 /* ResponseDetective-tvOS */ = { 554 | isa = PBXNativeTarget; 555 | buildConfigurationList = 3AE5463B1E152DB600E74469 /* Build configuration list for PBXNativeTarget "ResponseDetective-tvOS" */; 556 | buildPhases = ( 557 | 3AE546251E152DB500E74469 /* Sources */, 558 | 3AE546261E152DB500E74469 /* Frameworks */, 559 | 3AE546271E152DB500E74469 /* Headers */, 560 | 3AE546281E152DB500E74469 /* Resources */, 561 | ); 562 | buildRules = ( 563 | ); 564 | dependencies = ( 565 | ); 566 | name = "ResponseDetective-tvOS"; 567 | productName = "ResponseDetective (tvOS)"; 568 | productReference = 3AE5462A1E152DB500E74469 /* ResponseDetective.framework */; 569 | productType = "com.apple.product-type.framework"; 570 | }; 571 | 3AE546311E152DB600E74469 /* ResponseDetective-tvOS-Tests */ = { 572 | isa = PBXNativeTarget; 573 | buildConfigurationList = 3AE5463E1E152DB600E74469 /* Build configuration list for PBXNativeTarget "ResponseDetective-tvOS-Tests" */; 574 | buildPhases = ( 575 | 3AE5462E1E152DB600E74469 /* Sources */, 576 | 3AE5462F1E152DB600E74469 /* Frameworks */, 577 | 3AE546301E152DB600E74469 /* Resources */, 578 | 3AE546671E1531B300E74469 /* Copy Carthage Frameworks */, 579 | ); 580 | buildRules = ( 581 | ); 582 | dependencies = ( 583 | 3AE546351E152DB600E74469 /* PBXTargetDependency */, 584 | ); 585 | name = "ResponseDetective-tvOS-Tests"; 586 | productName = "ResponseDetective (tvOS)Tests"; 587 | productReference = 3AE546321E152DB600E74469 /* ResponseDetectiveTests.xctest */; 588 | productType = "com.apple.product-type.bundle.unit-test"; 589 | }; 590 | 3AED3ECB1B1DD7B400FA35FC /* ResponseDetective-iOS */ = { 591 | isa = PBXNativeTarget; 592 | buildConfigurationList = 3AED3EE21B1DD7B400FA35FC /* Build configuration list for PBXNativeTarget "ResponseDetective-iOS" */; 593 | buildPhases = ( 594 | 3AED3EC71B1DD7B400FA35FC /* Sources */, 595 | 3AED3EC81B1DD7B400FA35FC /* Frameworks */, 596 | 3AED3EC91B1DD7B400FA35FC /* Headers */, 597 | 3AED3ECA1B1DD7B400FA35FC /* Resources */, 598 | ); 599 | buildRules = ( 600 | ); 601 | dependencies = ( 602 | ); 603 | name = "ResponseDetective-iOS"; 604 | productName = ResponseDetective; 605 | productReference = 3AED3ECC1B1DD7B400FA35FC /* ResponseDetective.framework */; 606 | productType = "com.apple.product-type.framework"; 607 | }; 608 | 3AED3ED61B1DD7B400FA35FC /* ResponseDetective-iOS-Tests */ = { 609 | isa = PBXNativeTarget; 610 | buildConfigurationList = 3AED3EE51B1DD7B400FA35FC /* Build configuration list for PBXNativeTarget "ResponseDetective-iOS-Tests" */; 611 | buildPhases = ( 612 | 3AED3ED31B1DD7B400FA35FC /* Sources */, 613 | 3AED3ED41B1DD7B400FA35FC /* Frameworks */, 614 | 3AED3ED51B1DD7B400FA35FC /* Resources */, 615 | 3ACCEBDB1B21D70D006C0FF4 /* Copy Carthage Frameworks */, 616 | ); 617 | buildRules = ( 618 | ); 619 | dependencies = ( 620 | 3AED3EDA1B1DD7B400FA35FC /* PBXTargetDependency */, 621 | ); 622 | name = "ResponseDetective-iOS-Tests"; 623 | productName = ResponseDetectiveTests; 624 | productReference = 3AED3ED71B1DD7B400FA35FC /* ResponseDetectiveTests.xctest */; 625 | productType = "com.apple.product-type.bundle.unit-test"; 626 | }; 627 | /* End PBXNativeTarget section */ 628 | 629 | /* Begin PBXProject section */ 630 | 3AED3EC31B1DD7B400FA35FC /* Project object */ = { 631 | isa = PBXProject; 632 | attributes = { 633 | CLASSPREFIX = RDT; 634 | LastSwiftMigration = 0700; 635 | LastSwiftUpdateCheck = 0820; 636 | LastUpgradeCheck = 1220; 637 | ORGANIZATIONNAME = "Netguru S.A."; 638 | TargetAttributes = { 639 | 3A2007D71B5501BC00769021 = { 640 | CreatedOnToolsVersion = 6.4; 641 | LastSwiftMigration = 0930; 642 | }; 643 | 3A2007E11B5501BC00769021 = { 644 | CreatedOnToolsVersion = 6.4; 645 | LastSwiftMigration = 0930; 646 | }; 647 | 3AE546291E152DB500E74469 = { 648 | CreatedOnToolsVersion = 8.2.1; 649 | ProvisioningStyle = Manual; 650 | }; 651 | 3AE546311E152DB600E74469 = { 652 | CreatedOnToolsVersion = 8.2.1; 653 | ProvisioningStyle = Automatic; 654 | }; 655 | 3AED3ECB1B1DD7B400FA35FC = { 656 | CreatedOnToolsVersion = 6.3.2; 657 | LastSwiftMigration = 0820; 658 | }; 659 | 3AED3ED61B1DD7B400FA35FC = { 660 | CreatedOnToolsVersion = 6.3.2; 661 | LastSwiftMigration = 0820; 662 | }; 663 | }; 664 | }; 665 | buildConfigurationList = 3AED3EC61B1DD7B400FA35FC /* Build configuration list for PBXProject "ResponseDetective" */; 666 | compatibilityVersion = "Xcode 3.2"; 667 | developmentRegion = en; 668 | hasScannedForEncodings = 0; 669 | knownRegions = ( 670 | en, 671 | Base, 672 | ); 673 | mainGroup = 3AED3EC21B1DD7B400FA35FC; 674 | productRefGroup = 3AED3ECD1B1DD7B400FA35FC /* Products */; 675 | projectDirPath = ""; 676 | projectRoot = ""; 677 | targets = ( 678 | 3AED3ECB1B1DD7B400FA35FC /* ResponseDetective-iOS */, 679 | 3AED3ED61B1DD7B400FA35FC /* ResponseDetective-iOS-Tests */, 680 | 3A2007D71B5501BC00769021 /* ResponseDetective-macOS */, 681 | 3A2007E11B5501BC00769021 /* ResponseDetective-macOS-Tests */, 682 | 3AE546291E152DB500E74469 /* ResponseDetective-tvOS */, 683 | 3AE546311E152DB600E74469 /* ResponseDetective-tvOS-Tests */, 684 | ); 685 | }; 686 | /* End PBXProject section */ 687 | 688 | /* Begin PBXResourcesBuildPhase section */ 689 | 3A2007D61B5501BC00769021 /* Resources */ = { 690 | isa = PBXResourcesBuildPhase; 691 | buildActionMask = 2147483647; 692 | files = ( 693 | ); 694 | runOnlyForDeploymentPostprocessing = 0; 695 | }; 696 | 3A2007E01B5501BC00769021 /* Resources */ = { 697 | isa = PBXResourcesBuildPhase; 698 | buildActionMask = 2147483647; 699 | files = ( 700 | ); 701 | runOnlyForDeploymentPostprocessing = 0; 702 | }; 703 | 3AE546281E152DB500E74469 /* Resources */ = { 704 | isa = PBXResourcesBuildPhase; 705 | buildActionMask = 2147483647; 706 | files = ( 707 | ); 708 | runOnlyForDeploymentPostprocessing = 0; 709 | }; 710 | 3AE546301E152DB600E74469 /* Resources */ = { 711 | isa = PBXResourcesBuildPhase; 712 | buildActionMask = 2147483647; 713 | files = ( 714 | ); 715 | runOnlyForDeploymentPostprocessing = 0; 716 | }; 717 | 3AED3ECA1B1DD7B400FA35FC /* Resources */ = { 718 | isa = PBXResourcesBuildPhase; 719 | buildActionMask = 2147483647; 720 | files = ( 721 | ); 722 | runOnlyForDeploymentPostprocessing = 0; 723 | }; 724 | 3AED3ED51B1DD7B400FA35FC /* Resources */ = { 725 | isa = PBXResourcesBuildPhase; 726 | buildActionMask = 2147483647; 727 | files = ( 728 | ); 729 | runOnlyForDeploymentPostprocessing = 0; 730 | }; 731 | /* End PBXResourcesBuildPhase section */ 732 | 733 | /* Begin PBXShellScriptBuildPhase section */ 734 | 3A7D018A1CD3C09B00B7FBD7 /* Copy Carthage Frameworks */ = { 735 | isa = PBXShellScriptBuildPhase; 736 | buildActionMask = 2147483647; 737 | files = ( 738 | ); 739 | inputPaths = ( 740 | "$(_CARTHAGE_BUILD_PATH)/Quick.framework", 741 | "$(_CARTHAGE_BUILD_PATH)/Nimble.framework", 742 | "$(_CARTHAGE_BUILD_PATH)/OHHTTPStubs.framework", 743 | ); 744 | name = "Copy Carthage Frameworks"; 745 | outputPaths = ( 746 | ); 747 | runOnlyForDeploymentPostprocessing = 0; 748 | shellPath = /bin/sh; 749 | shellScript = "carthage copy-frameworks"; 750 | showEnvVarsInLog = 0; 751 | }; 752 | 3ACCEBDB1B21D70D006C0FF4 /* Copy Carthage Frameworks */ = { 753 | isa = PBXShellScriptBuildPhase; 754 | buildActionMask = 2147483647; 755 | files = ( 756 | ); 757 | inputPaths = ( 758 | "$(_CARTHAGE_BUILD_PATH)/Nimble.framework", 759 | "$(_CARTHAGE_BUILD_PATH)/Quick.framework", 760 | "$(_CARTHAGE_BUILD_PATH)/OHHTTPStubs.framework", 761 | ); 762 | name = "Copy Carthage Frameworks"; 763 | outputPaths = ( 764 | ); 765 | runOnlyForDeploymentPostprocessing = 0; 766 | shellPath = /bin/sh; 767 | shellScript = "carthage copy-frameworks"; 768 | showEnvVarsInLog = 0; 769 | }; 770 | 3AE546671E1531B300E74469 /* Copy Carthage Frameworks */ = { 771 | isa = PBXShellScriptBuildPhase; 772 | buildActionMask = 2147483647; 773 | files = ( 774 | ); 775 | inputPaths = ( 776 | "$(_CARTHAGE_BUILD_PATH)/Quick.framework", 777 | "$(_CARTHAGE_BUILD_PATH)/Nimble.framework", 778 | "$(_CARTHAGE_BUILD_PATH)/OHHTTPStubs.framework", 779 | ); 780 | name = "Copy Carthage Frameworks"; 781 | outputPaths = ( 782 | ); 783 | runOnlyForDeploymentPostprocessing = 0; 784 | shellPath = /bin/sh; 785 | shellScript = "carthage copy-frameworks"; 786 | }; 787 | /* End PBXShellScriptBuildPhase section */ 788 | 789 | /* Begin PBXSourcesBuildPhase section */ 790 | 3A2007D31B5501BC00769021 /* Sources */ = { 791 | isa = PBXSourcesBuildPhase; 792 | buildActionMask = 2147483647; 793 | files = ( 794 | 3AF56E971E5B37A500F1CEBC /* OutputFacility.swift in Sources */, 795 | 3AF56E941E5B37A500F1CEBC /* JSONBodyDeserializer.swift in Sources */, 796 | 3AC631221EBB5CF8000F6353 /* URLEncodedBodyDeserializer.swift in Sources */, 797 | 3AF56E851E5B37A500F1CEBC /* BufferOutputFacility.swift in Sources */, 798 | 3AF56EA91E5B37A500F1CEBC /* RDTXMLBodyDeserializer.m in Sources */, 799 | 3AF56EAC1E5B37A500F1CEBC /* RequestRepresentation.swift in Sources */, 800 | 3AF56EB51E5B37A500F1CEBC /* ResponseRepresentation.swift in Sources */, 801 | 3AF56E911E5B37A500F1CEBC /* ImageBodyDeserializer.swift in Sources */, 802 | 3AF56EB21E5B37A500F1CEBC /* ResponseDetective.swift in Sources */, 803 | 3AF56E9A1E5B37A500F1CEBC /* PlaintextBodyDeserializer.swift in Sources */, 804 | 3AF56E8E1E5B37A500F1CEBC /* ErrorRepresentation.swift in Sources */, 805 | 3AF56E881E5B37A500F1CEBC /* ConsoleOutputFacility.swift in Sources */, 806 | 3AF56EB81E5B37A500F1CEBC /* URLProtocol.swift in Sources */, 807 | 3AF56E8B1E5B37A500F1CEBC /* Dictionary.swift in Sources */, 808 | 3AF56EA31E5B37A500F1CEBC /* RDTHTMLBodyDeserializer.m in Sources */, 809 | ); 810 | runOnlyForDeploymentPostprocessing = 0; 811 | }; 812 | 3A2007DE1B5501BC00769021 /* Sources */ = { 813 | isa = PBXSourcesBuildPhase; 814 | buildActionMask = 2147483647; 815 | files = ( 816 | 3AF56EF11E5B37B500F1CEBC /* XMLBodyDeserializerSpec.swift in Sources */, 817 | 3AF56EEB1E5B37B500F1CEBC /* ResponseRepresentationSpec.swift in Sources */, 818 | 3AF56ED61E5B37B500F1CEBC /* ErrorRepresentationSpec.swift in Sources */, 819 | 3AF56EDC1E5B37B500F1CEBC /* ImageBodyDeserializerSpec.swift in Sources */, 820 | 3AF56ED01E5B37B500F1CEBC /* BufferOutputFacilitySpec.swift in Sources */, 821 | 3AF56EE81E5B37B500F1CEBC /* ResponseDetectiveSpec.swift in Sources */, 822 | 3AF56EE21E5B37B500F1CEBC /* PlaintextBodyDeserializerSpec.swift in Sources */, 823 | 3AB9E9811EBB64C9004E575E /* URLEncodedBodyDeserializerSpec.swift in Sources */, 824 | 3AF56ED31E5B37B500F1CEBC /* ConsoleOutputFacilitySpec.swift in Sources */, 825 | 3AF56ED91E5B37B500F1CEBC /* HTMLBodyDeserializerSpec.swift in Sources */, 826 | 3AF56EE51E5B37B500F1CEBC /* RequestRepresentationSpec.swift in Sources */, 827 | 3AF56F0D1E5B4ED800F1CEBC /* TestBodyDeserializer.swift in Sources */, 828 | 3AF56EDF1E5B37B500F1CEBC /* JSONBodyDeserializerSpec.swift in Sources */, 829 | ); 830 | runOnlyForDeploymentPostprocessing = 0; 831 | }; 832 | 3AE546251E152DB500E74469 /* Sources */ = { 833 | isa = PBXSourcesBuildPhase; 834 | buildActionMask = 2147483647; 835 | files = ( 836 | 3AF56E981E5B37A500F1CEBC /* OutputFacility.swift in Sources */, 837 | 3AF56E951E5B37A500F1CEBC /* JSONBodyDeserializer.swift in Sources */, 838 | 3AC631231EBB5CF8000F6353 /* URLEncodedBodyDeserializer.swift in Sources */, 839 | 3AF56E861E5B37A500F1CEBC /* BufferOutputFacility.swift in Sources */, 840 | 3AF56EAA1E5B37A500F1CEBC /* RDTXMLBodyDeserializer.m in Sources */, 841 | 3AF56EAD1E5B37A500F1CEBC /* RequestRepresentation.swift in Sources */, 842 | 3AF56EB61E5B37A500F1CEBC /* ResponseRepresentation.swift in Sources */, 843 | 3AF56E921E5B37A500F1CEBC /* ImageBodyDeserializer.swift in Sources */, 844 | 3AF56EB31E5B37A500F1CEBC /* ResponseDetective.swift in Sources */, 845 | 3AF56E9B1E5B37A500F1CEBC /* PlaintextBodyDeserializer.swift in Sources */, 846 | 3AF56E8F1E5B37A500F1CEBC /* ErrorRepresentation.swift in Sources */, 847 | 3AF56E891E5B37A500F1CEBC /* ConsoleOutputFacility.swift in Sources */, 848 | 3AF56EB91E5B37A500F1CEBC /* URLProtocol.swift in Sources */, 849 | 3AF56E8C1E5B37A500F1CEBC /* Dictionary.swift in Sources */, 850 | 3AF56EA41E5B37A500F1CEBC /* RDTHTMLBodyDeserializer.m in Sources */, 851 | ); 852 | runOnlyForDeploymentPostprocessing = 0; 853 | }; 854 | 3AE5462E1E152DB600E74469 /* Sources */ = { 855 | isa = PBXSourcesBuildPhase; 856 | buildActionMask = 2147483647; 857 | files = ( 858 | 3AF56EF21E5B37B500F1CEBC /* XMLBodyDeserializerSpec.swift in Sources */, 859 | 3AF56EEC1E5B37B500F1CEBC /* ResponseRepresentationSpec.swift in Sources */, 860 | 3AF56ED71E5B37B500F1CEBC /* ErrorRepresentationSpec.swift in Sources */, 861 | 3AF56EDD1E5B37B500F1CEBC /* ImageBodyDeserializerSpec.swift in Sources */, 862 | 3AF56ED11E5B37B500F1CEBC /* BufferOutputFacilitySpec.swift in Sources */, 863 | 3AF56EE91E5B37B500F1CEBC /* ResponseDetectiveSpec.swift in Sources */, 864 | 3AF56EE31E5B37B500F1CEBC /* PlaintextBodyDeserializerSpec.swift in Sources */, 865 | 3AB9E9821EBB64C9004E575E /* URLEncodedBodyDeserializerSpec.swift in Sources */, 866 | 3AF56ED41E5B37B500F1CEBC /* ConsoleOutputFacilitySpec.swift in Sources */, 867 | 3AF56EDA1E5B37B500F1CEBC /* HTMLBodyDeserializerSpec.swift in Sources */, 868 | 3AF56EE61E5B37B500F1CEBC /* RequestRepresentationSpec.swift in Sources */, 869 | 3AF56F0E1E5B4ED800F1CEBC /* TestBodyDeserializer.swift in Sources */, 870 | 3AF56EE01E5B37B500F1CEBC /* JSONBodyDeserializerSpec.swift in Sources */, 871 | ); 872 | runOnlyForDeploymentPostprocessing = 0; 873 | }; 874 | 3AED3EC71B1DD7B400FA35FC /* Sources */ = { 875 | isa = PBXSourcesBuildPhase; 876 | buildActionMask = 2147483647; 877 | files = ( 878 | 3AF56E961E5B37A500F1CEBC /* OutputFacility.swift in Sources */, 879 | 3AF56E931E5B37A500F1CEBC /* JSONBodyDeserializer.swift in Sources */, 880 | 3AC631211EBB5CF8000F6353 /* URLEncodedBodyDeserializer.swift in Sources */, 881 | 3AF56E841E5B37A500F1CEBC /* BufferOutputFacility.swift in Sources */, 882 | 3AF56EA81E5B37A500F1CEBC /* RDTXMLBodyDeserializer.m in Sources */, 883 | 3AF56EAB1E5B37A500F1CEBC /* RequestRepresentation.swift in Sources */, 884 | 3AF56EB41E5B37A500F1CEBC /* ResponseRepresentation.swift in Sources */, 885 | 3AF56E901E5B37A500F1CEBC /* ImageBodyDeserializer.swift in Sources */, 886 | 3AF56EB11E5B37A500F1CEBC /* ResponseDetective.swift in Sources */, 887 | 3AF56E991E5B37A500F1CEBC /* PlaintextBodyDeserializer.swift in Sources */, 888 | 3AF56E8D1E5B37A500F1CEBC /* ErrorRepresentation.swift in Sources */, 889 | 3AF56E871E5B37A500F1CEBC /* ConsoleOutputFacility.swift in Sources */, 890 | 3AF56EB71E5B37A500F1CEBC /* URLProtocol.swift in Sources */, 891 | 3AF56E8A1E5B37A500F1CEBC /* Dictionary.swift in Sources */, 892 | 3AF56EA21E5B37A500F1CEBC /* RDTHTMLBodyDeserializer.m in Sources */, 893 | ); 894 | runOnlyForDeploymentPostprocessing = 0; 895 | }; 896 | 3AED3ED31B1DD7B400FA35FC /* Sources */ = { 897 | isa = PBXSourcesBuildPhase; 898 | buildActionMask = 2147483647; 899 | files = ( 900 | 3AF56EF01E5B37B500F1CEBC /* XMLBodyDeserializerSpec.swift in Sources */, 901 | 3AF56EEA1E5B37B500F1CEBC /* ResponseRepresentationSpec.swift in Sources */, 902 | 3AF56ED51E5B37B500F1CEBC /* ErrorRepresentationSpec.swift in Sources */, 903 | 3AF56EDB1E5B37B500F1CEBC /* ImageBodyDeserializerSpec.swift in Sources */, 904 | 3AF56ECF1E5B37B500F1CEBC /* BufferOutputFacilitySpec.swift in Sources */, 905 | 3AF56EE71E5B37B500F1CEBC /* ResponseDetectiveSpec.swift in Sources */, 906 | 3AF56EE11E5B37B500F1CEBC /* PlaintextBodyDeserializerSpec.swift in Sources */, 907 | 3AB9E9801EBB64C8004E575E /* URLEncodedBodyDeserializerSpec.swift in Sources */, 908 | 3AF56ED21E5B37B500F1CEBC /* ConsoleOutputFacilitySpec.swift in Sources */, 909 | 3AF56ED81E5B37B500F1CEBC /* HTMLBodyDeserializerSpec.swift in Sources */, 910 | 3AF56EE41E5B37B500F1CEBC /* RequestRepresentationSpec.swift in Sources */, 911 | 3AF56F0C1E5B4ED800F1CEBC /* TestBodyDeserializer.swift in Sources */, 912 | 3AF56EDE1E5B37B500F1CEBC /* JSONBodyDeserializerSpec.swift in Sources */, 913 | ); 914 | runOnlyForDeploymentPostprocessing = 0; 915 | }; 916 | /* End PBXSourcesBuildPhase section */ 917 | 918 | /* Begin PBXTargetDependency section */ 919 | 3A2007E51B5501BC00769021 /* PBXTargetDependency */ = { 920 | isa = PBXTargetDependency; 921 | target = 3A2007D71B5501BC00769021 /* ResponseDetective-macOS */; 922 | targetProxy = 3A2007E41B5501BC00769021 /* PBXContainerItemProxy */; 923 | }; 924 | 3AE546351E152DB600E74469 /* PBXTargetDependency */ = { 925 | isa = PBXTargetDependency; 926 | target = 3AE546291E152DB500E74469 /* ResponseDetective-tvOS */; 927 | targetProxy = 3AE546341E152DB600E74469 /* PBXContainerItemProxy */; 928 | }; 929 | 3AED3EDA1B1DD7B400FA35FC /* PBXTargetDependency */ = { 930 | isa = PBXTargetDependency; 931 | target = 3AED3ECB1B1DD7B400FA35FC /* ResponseDetective-iOS */; 932 | targetProxy = 3AED3ED91B1DD7B400FA35FC /* PBXContainerItemProxy */; 933 | }; 934 | /* End PBXTargetDependency section */ 935 | 936 | /* Begin XCBuildConfiguration section */ 937 | 3A2007EC1B5501BC00769021 /* Debug */ = { 938 | isa = XCBuildConfiguration; 939 | baseConfigurationReference = 3AF6958C1ECB1D2B00586DA7 /* Framework-macOS.xcconfig */; 940 | buildSettings = { 941 | }; 942 | name = Debug; 943 | }; 944 | 3A2007ED1B5501BC00769021 /* Release */ = { 945 | isa = XCBuildConfiguration; 946 | baseConfigurationReference = 3AF6958C1ECB1D2B00586DA7 /* Framework-macOS.xcconfig */; 947 | buildSettings = { 948 | }; 949 | name = Release; 950 | }; 951 | 3A2007EF1B5501BC00769021 /* Debug */ = { 952 | isa = XCBuildConfiguration; 953 | baseConfigurationReference = 3AF6958E1ECB1D5800586DA7 /* Tests-macOS.xcconfig */; 954 | buildSettings = { 955 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 956 | }; 957 | name = Debug; 958 | }; 959 | 3A2007F01B5501BC00769021 /* Release */ = { 960 | isa = XCBuildConfiguration; 961 | baseConfigurationReference = 3AF6958E1ECB1D5800586DA7 /* Tests-macOS.xcconfig */; 962 | buildSettings = { 963 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 964 | }; 965 | name = Release; 966 | }; 967 | 3AE5463C1E152DB600E74469 /* Debug */ = { 968 | isa = XCBuildConfiguration; 969 | baseConfigurationReference = 3AF6958D1ECB1D4200586DA7 /* Framework-tvOS.xcconfig */; 970 | buildSettings = { 971 | }; 972 | name = Debug; 973 | }; 974 | 3AE5463D1E152DB600E74469 /* Release */ = { 975 | isa = XCBuildConfiguration; 976 | baseConfigurationReference = 3AF6958D1ECB1D4200586DA7 /* Framework-tvOS.xcconfig */; 977 | buildSettings = { 978 | }; 979 | name = Release; 980 | }; 981 | 3AE5463F1E152DB600E74469 /* Debug */ = { 982 | isa = XCBuildConfiguration; 983 | baseConfigurationReference = 3AF6958F1ECB1D6600586DA7 /* Tests-tvOS.xcconfig */; 984 | buildSettings = { 985 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 986 | }; 987 | name = Debug; 988 | }; 989 | 3AE546401E152DB600E74469 /* Release */ = { 990 | isa = XCBuildConfiguration; 991 | baseConfigurationReference = 3AF6958F1ECB1D6600586DA7 /* Tests-tvOS.xcconfig */; 992 | buildSettings = { 993 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 994 | }; 995 | name = Release; 996 | }; 997 | 3AED3EE01B1DD7B400FA35FC /* Debug */ = { 998 | isa = XCBuildConfiguration; 999 | baseConfigurationReference = 3AF695861ECB199700586DA7 /* Common-Debug.xcconfig */; 1000 | buildSettings = { 1001 | }; 1002 | name = Debug; 1003 | }; 1004 | 3AED3EE11B1DD7B400FA35FC /* Release */ = { 1005 | isa = XCBuildConfiguration; 1006 | baseConfigurationReference = 3AF6958A1ECB1A7900586DA7 /* Common-Release.xcconfig */; 1007 | buildSettings = { 1008 | }; 1009 | name = Release; 1010 | }; 1011 | 3AED3EE31B1DD7B400FA35FC /* Debug */ = { 1012 | isa = XCBuildConfiguration; 1013 | baseConfigurationReference = 3AF695871ECB19CC00586DA7 /* Framework-iOS.xcconfig */; 1014 | buildSettings = { 1015 | }; 1016 | name = Debug; 1017 | }; 1018 | 3AED3EE41B1DD7B400FA35FC /* Release */ = { 1019 | isa = XCBuildConfiguration; 1020 | baseConfigurationReference = 3AF695871ECB19CC00586DA7 /* Framework-iOS.xcconfig */; 1021 | buildSettings = { 1022 | }; 1023 | name = Release; 1024 | }; 1025 | 3AED3EE61B1DD7B400FA35FC /* Debug */ = { 1026 | isa = XCBuildConfiguration; 1027 | baseConfigurationReference = 3AF6958B1ECB1B5100586DA7 /* Tests-iOS.xcconfig */; 1028 | buildSettings = { 1029 | }; 1030 | name = Debug; 1031 | }; 1032 | 3AED3EE71B1DD7B400FA35FC /* Release */ = { 1033 | isa = XCBuildConfiguration; 1034 | baseConfigurationReference = 3AF6958B1ECB1B5100586DA7 /* Tests-iOS.xcconfig */; 1035 | buildSettings = { 1036 | }; 1037 | name = Release; 1038 | }; 1039 | /* End XCBuildConfiguration section */ 1040 | 1041 | /* Begin XCConfigurationList section */ 1042 | 3A2007EB1B5501BC00769021 /* Build configuration list for PBXNativeTarget "ResponseDetective-macOS" */ = { 1043 | isa = XCConfigurationList; 1044 | buildConfigurations = ( 1045 | 3A2007EC1B5501BC00769021 /* Debug */, 1046 | 3A2007ED1B5501BC00769021 /* Release */, 1047 | ); 1048 | defaultConfigurationIsVisible = 0; 1049 | defaultConfigurationName = Release; 1050 | }; 1051 | 3A2007EE1B5501BC00769021 /* Build configuration list for PBXNativeTarget "ResponseDetective-macOS-Tests" */ = { 1052 | isa = XCConfigurationList; 1053 | buildConfigurations = ( 1054 | 3A2007EF1B5501BC00769021 /* Debug */, 1055 | 3A2007F01B5501BC00769021 /* Release */, 1056 | ); 1057 | defaultConfigurationIsVisible = 0; 1058 | defaultConfigurationName = Release; 1059 | }; 1060 | 3AE5463B1E152DB600E74469 /* Build configuration list for PBXNativeTarget "ResponseDetective-tvOS" */ = { 1061 | isa = XCConfigurationList; 1062 | buildConfigurations = ( 1063 | 3AE5463C1E152DB600E74469 /* Debug */, 1064 | 3AE5463D1E152DB600E74469 /* Release */, 1065 | ); 1066 | defaultConfigurationIsVisible = 0; 1067 | defaultConfigurationName = Release; 1068 | }; 1069 | 3AE5463E1E152DB600E74469 /* Build configuration list for PBXNativeTarget "ResponseDetective-tvOS-Tests" */ = { 1070 | isa = XCConfigurationList; 1071 | buildConfigurations = ( 1072 | 3AE5463F1E152DB600E74469 /* Debug */, 1073 | 3AE546401E152DB600E74469 /* Release */, 1074 | ); 1075 | defaultConfigurationIsVisible = 0; 1076 | defaultConfigurationName = Release; 1077 | }; 1078 | 3AED3EC61B1DD7B400FA35FC /* Build configuration list for PBXProject "ResponseDetective" */ = { 1079 | isa = XCConfigurationList; 1080 | buildConfigurations = ( 1081 | 3AED3EE01B1DD7B400FA35FC /* Debug */, 1082 | 3AED3EE11B1DD7B400FA35FC /* Release */, 1083 | ); 1084 | defaultConfigurationIsVisible = 0; 1085 | defaultConfigurationName = Release; 1086 | }; 1087 | 3AED3EE21B1DD7B400FA35FC /* Build configuration list for PBXNativeTarget "ResponseDetective-iOS" */ = { 1088 | isa = XCConfigurationList; 1089 | buildConfigurations = ( 1090 | 3AED3EE31B1DD7B400FA35FC /* Debug */, 1091 | 3AED3EE41B1DD7B400FA35FC /* Release */, 1092 | ); 1093 | defaultConfigurationIsVisible = 0; 1094 | defaultConfigurationName = Release; 1095 | }; 1096 | 3AED3EE51B1DD7B400FA35FC /* Build configuration list for PBXNativeTarget "ResponseDetective-iOS-Tests" */ = { 1097 | isa = XCConfigurationList; 1098 | buildConfigurations = ( 1099 | 3AED3EE61B1DD7B400FA35FC /* Debug */, 1100 | 3AED3EE71B1DD7B400FA35FC /* Release */, 1101 | ); 1102 | defaultConfigurationIsVisible = 0; 1103 | defaultConfigurationName = Release; 1104 | }; 1105 | /* End XCConfigurationList section */ 1106 | }; 1107 | rootObject = 3AED3EC31B1DD7B400FA35FC /* Project object */; 1108 | } 1109 | --------------------------------------------------------------------------------