From e02efd5ecc5ee9399c3dd4e51016c9d977daa207 Mon Sep 17 00:00:00 2001 From: Aron Balog Date: Mon, 5 Mar 2018 23:59:02 +0100 Subject: [PATCH] - Added Alamofire plugin - Added `Client` protocol docs - https://github.com/aronbalog/Vox/issues/2 --- .travis.yml | 2 + Podfile | 1 + Podfile.lock | 5 +- README.md | 65 +++++++++++++-- Vox.podspec | 35 +++++--- Vox.xcodeproj/project.pbxproj | 68 ++++++++++++++-- ...34CD9175-837D-417B-8E87-2804E7E86114.plist | 2 +- .../xcshareddata/xcschemes/Vox.xcscheme | 5 -- Vox/{ => Core}/Class/BaseResource.h | 0 Vox/{ => Core}/Class/BaseResource.m | 0 Vox/{ => Core}/Class/Context.swift | 0 Vox/{ => Core}/Class/Context_Query.swift | 0 Vox/{ => Core}/Class/Deserializer.swift | 0 .../Class/Deserializer_Collection.swift | 0 .../Class/Deserializer_Single.swift | 0 Vox/{ => Core}/Class/Document.swift | 0 Vox/{ => Core}/Class/ErrorObject.swift | 0 Vox/{ => Core}/Class/JSONAPIDecoder.swift | 0 Vox/{ => Core}/Class/Links.swift | 0 Vox/{ => Core}/Class/Resource.swift | 0 Vox/{ => Core}/Class/ResourcePool.swift | 0 Vox/{ => Core}/Enum/JSONAPIError.swift | 0 .../Networking/Class/DataSource.swift | 0 Vox/{ => Core}/Networking/Class/Request.swift | 25 ++---- .../Networking/Client/JSONAPIClient.swift | 1 + .../Networking/Extensions/Pagination.swift | 0 .../Extensions/Pagination_CursorBased.swift | 0 .../Extensions/Pagination_OffsetBased.swift | 0 .../Extensions/Pagination_PageBased.swift | 0 Vox/{ => Core}/Networking/Protocol/CRUD.swift | 0 Vox/Core/Networking/Protocol/Client.swift | 9 +++ .../Networking/Protocol/Creatable.swift | 0 .../Protocol/DataSourceResultable.swift | 2 +- .../Networking/Protocol/Deletable.swift | 0 .../Protocol/FetchConfigurable.swift | 0 .../Networking/Protocol/FetchFieldable.swift | 0 .../Networking/Protocol/FetchFilterable.swift | 0 .../Networking/Protocol/FetchIncludable.swift | 0 .../Networking/Protocol/FetchPageable.swift | 2 +- .../Networking/Protocol/FetchSortable.swift | 0 .../Networking/Protocol/Fetchable.swift | 0 .../Networking/Protocol/FilterStrategy.swift | 0 .../Protocol/PaginationStrategy.swift | 0 .../Protocol/QueryItemsCustomizable.swift | 0 .../Networking/Protocol/Router.swift | 0 .../Networking/Protocol/Updatable.swift | 0 Vox/{ => Core}/Protocol/NullableValue.swift | 0 Vox/{ => Core}/Vox.h | 0 Vox/Networking/Protocol/Client.swift | 9 --- .../Alamofire/JSONAPIClient_Alamofire.swift | 67 ++++++++++++++++ VoxTests/Alamofire/AlamofireClientSpec.swift | 80 +++++++++++++++++++ VoxTests/DataSource/DataSourceSpec.swift | 2 +- VoxTests/DataSource/PaginationSpec.swift | 2 +- 53 files changed, 322 insertions(+), 60 deletions(-) rename Vox/{ => Core}/Class/BaseResource.h (100%) rename Vox/{ => Core}/Class/BaseResource.m (100%) rename Vox/{ => Core}/Class/Context.swift (100%) rename Vox/{ => Core}/Class/Context_Query.swift (100%) rename Vox/{ => Core}/Class/Deserializer.swift (100%) rename Vox/{ => Core}/Class/Deserializer_Collection.swift (100%) rename Vox/{ => Core}/Class/Deserializer_Single.swift (100%) rename Vox/{ => Core}/Class/Document.swift (100%) rename Vox/{ => Core}/Class/ErrorObject.swift (100%) rename Vox/{ => Core}/Class/JSONAPIDecoder.swift (100%) rename Vox/{ => Core}/Class/Links.swift (100%) rename Vox/{ => Core}/Class/Resource.swift (100%) rename Vox/{ => Core}/Class/ResourcePool.swift (100%) rename Vox/{ => Core}/Enum/JSONAPIError.swift (100%) rename Vox/{ => Core}/Networking/Class/DataSource.swift (100%) rename Vox/{ => Core}/Networking/Class/Request.swift (87%) create mode 100644 Vox/Core/Networking/Client/JSONAPIClient.swift rename Vox/{ => Core}/Networking/Extensions/Pagination.swift (100%) rename Vox/{ => Core}/Networking/Extensions/Pagination_CursorBased.swift (100%) rename Vox/{ => Core}/Networking/Extensions/Pagination_OffsetBased.swift (100%) rename Vox/{ => Core}/Networking/Extensions/Pagination_PageBased.swift (100%) rename Vox/{ => Core}/Networking/Protocol/CRUD.swift (100%) create mode 100644 Vox/Core/Networking/Protocol/Client.swift rename Vox/{ => Core}/Networking/Protocol/Creatable.swift (100%) rename Vox/{ => Core}/Networking/Protocol/DataSourceResultable.swift (83%) rename Vox/{ => Core}/Networking/Protocol/Deletable.swift (100%) rename Vox/{ => Core}/Networking/Protocol/FetchConfigurable.swift (100%) rename Vox/{ => Core}/Networking/Protocol/FetchFieldable.swift (100%) rename Vox/{ => Core}/Networking/Protocol/FetchFilterable.swift (100%) rename Vox/{ => Core}/Networking/Protocol/FetchIncludable.swift (100%) rename Vox/{ => Core}/Networking/Protocol/FetchPageable.swift (98%) rename Vox/{ => Core}/Networking/Protocol/FetchSortable.swift (100%) rename Vox/{ => Core}/Networking/Protocol/Fetchable.swift (100%) rename Vox/{ => Core}/Networking/Protocol/FilterStrategy.swift (100%) rename Vox/{ => Core}/Networking/Protocol/PaginationStrategy.swift (100%) rename Vox/{ => Core}/Networking/Protocol/QueryItemsCustomizable.swift (100%) rename Vox/{ => Core}/Networking/Protocol/Router.swift (100%) rename Vox/{ => Core}/Networking/Protocol/Updatable.swift (100%) rename Vox/{ => Core}/Protocol/NullableValue.swift (100%) rename Vox/{ => Core}/Vox.h (100%) delete mode 100644 Vox/Networking/Protocol/Client.swift create mode 100644 Vox/Plugins/Alamofire/JSONAPIClient_Alamofire.swift create mode 100644 VoxTests/Alamofire/AlamofireClientSpec.swift diff --git a/.travis.yml b/.travis.yml index 360d403..7142ba5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ os: osx osx_image: xcode9.2 language: swift +before_install: +- pod repo update > /dev/null script: - set -o pipefail - xcodebuild test -workspace Vox.xcworkspace -scheme Vox -destination 'platform=iOS Simulator,name=iPhone 8,OS=11.2' ONLY_ACTIVE_ARCH=NO diff --git a/Podfile b/Podfile index fa15120..cf233dd 100644 --- a/Podfile +++ b/Podfile @@ -1,6 +1,7 @@ target 'Vox' do use_frameworks! + pod 'Alamofire', '~> 4.7' target 'VoxTests' do inherit! :search_paths diff --git a/Podfile.lock b/Podfile.lock index 2df4177..fdeb7d2 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,15 +1,18 @@ PODS: + - Alamofire (4.7.0) - Nimble (7.0.3) - Quick (1.2.0) DEPENDENCIES: + - Alamofire (~> 4.7) - Nimble - Quick SPEC CHECKSUMS: + Alamofire: 907e0a98eb68cdb7f9d1f541a563d6ac5dc77b25 Nimble: 7f5a9c447a33002645a071bddafbfb24ea70e0ac Quick: 58d203b1c5e27fff7229c4c1ae445ad7069a7a08 -PODFILE CHECKSUM: 66b133f477257acc4bcbde7cbe44557233e54c46 +PODFILE CHECKSUM: d6370329bd52b0306d7c72b4a2cf75a0248944c9 COCOAPODS: 1.4.0 diff --git a/README.md b/README.md index 7ac1240..ae73e63 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,15 @@ Vox is a Swift JSONAPI standard implementation. - [Defining resource](#defining-resource) - [Serializing](#serializing) - [Single resource](#single-resource) + - [Alamofire plugin](#alamofire-plugin) - [Resource collection](#resource-collection) - [Nullability](#nullability) - [Deserializing](#deserializing) - [Single resource](#single-resource) - [Resource collection](#resource-collection) - [Networking](#networking) + - [Client protocol](#client-protocol) + - [Alamofire client plugin](#alamofire-client-plugin) - [Fetching single resource](#fetching-single-resource) - [Fetching resource collection](#fetching-resource-collection) - [Creating resource](#creating-resource) @@ -84,10 +87,18 @@ This opens up the possibility to easily handle the cases with: * Xcode 9 * Cocoapods +Basic + ```ruby pod 'Vox' ``` +With [Alamofire](https://github.com/Alamofire/Alamofire) plugin + +```ruby +pod 'Vox/Alamofire' +``` + ## Usage ### Defining resource @@ -157,7 +168,7 @@ let person = Person() let json: [String: Any] = try! person.documentDictionary() -// or if you need Data +// or if `Data` is needed let data: Data = try! person.documentData() ``` @@ -208,8 +219,8 @@ let person2 = Person() let json: [String: Any] = try! [person1, person2].documentDictionary() -// or if you need Data -let data: Data = try! person.documentData() +// or if `Data` is needed +let data: Data = try! [person1, person2].documentData() ``` Previous example will resolve to following JSON: @@ -271,7 +282,7 @@ let data: Data // -> provide data received from JSONAPI server let deserializer = Deserializer.Single
() do { - let document = try sut.deserialize(data: self.data) + let document = try deserializer.deserialize(data: self.data) // `document.data` is an Article object @@ -296,7 +307,7 @@ let data: Data // -> provide data received from JSONAPI server let deserializer = Deserializer.Collection
() -let document = try! sut.deserialize(data: self.data) +let document = try! deserializer.deserialize(data: self.data) // `document.data` is an [Article] object ``` @@ -319,7 +330,49 @@ Deserializer can also be declared without generic parameter but in that case the ### Networking -You can use `` and `` annotations in path strings. If possible, they'll get replaced with adequate values. +`` and `` annotations can be used in path strings. If possible, they'll get replaced with adequate values. + +#### Client protocol + +Implement following method from `Client` protocol: + +```swift +func executeRequest(path: String, + method: String, + queryItems: [URLQueryItem], + bodyParameters: [String : Any]?, + success: @escaping ClientSuccessBlock, + failure: @escaping ClientFailureBlock) +``` + +where + +- `ClientSuccessBlock` = `(HTTPURLResponse?, Data?) -> Void` +- `ClientFailureBlock` = `(Error?, Data?) -> Void` + +#### Alamofire client plugin + +If custom networking is not required, there is a plugin which wraps [Alamofire](https://github.com/Alamofire/Alamofire) and provides networking client in accordance with JSON:API specification. + +> Alamofire is Elegant HTTP Networking in Swift + +Example: + +```swift +let baseURL = URL(string: "http://demo7377577.mockable.io")! +let client = JSONAPIClient.Alamofire(baseURL: baseURL) +let dataSource = DataSource
(strategy: .path("vox/articles"), client: client) + +dataSource + .fetch() + ... +``` + +##### Installation + +```ruby +pod 'Vox/Alamofire' +``` #### Fetching single resource diff --git a/Vox.podspec b/Vox.podspec index 43c54f9..62f6771 100644 --- a/Vox.podspec +++ b/Vox.podspec @@ -1,13 +1,26 @@ Pod::Spec.new do |spec| - spec.name = 'Vox' - spec.version = '1.0.4' - spec.license = 'MIT' - spec.summary = 'A Swift JSONAPI framework' - spec.author = 'Aron Balog' - spec.homepage = 'http://undabot.com' - spec.source = { :git => 'https://github.com/aronbalog/Vox.git', :tag => spec.version } - spec.source_files = 'Vox/**/*' - spec.requires_arc = true - spec.xcconfig = { 'SWIFT_VERSION' => '4.0' } - spec.platform = :ios, '8.0' + spec.name = 'Vox' + spec.version = '1.1.0' + spec.license = 'MIT' + spec.summary = 'A Swift JSONAPI framework' + spec.author = 'Aron Balog' + spec.homepage = 'http://undabot.com' + spec.source = { :git => 'https://github.com/aronbalog/Vox.git', :tag => spec.version } + spec.requires_arc = true + spec.xcconfig = { 'SWIFT_VERSION' => '4.0' } + spec.platform = :ios, '8.0' + spec.default_subspec = 'Core' + + spec.subspec 'Core' do |core| + core.source_files = 'Vox/Core/**/*.{swift,m,h}' + end + + spec.subspec 'Alamofire' do |alamofire| + alamofire.source_files = 'Vox/Plugins/Alamofire/**/*.{swift}' + alamofire.pod_target_xcconfig = { + 'SWIFT_ACTIVE_COMPILATION_CONDITIONS' => 'ALAMOFIRE', + } + alamofire.dependency 'Vox/Core' + alamofire.dependency 'Alamofire', '~> 4.7' + end end \ No newline at end of file diff --git a/Vox.xcodeproj/project.pbxproj b/Vox.xcodeproj/project.pbxproj index ab8452d..09e1c34 100644 --- a/Vox.xcodeproj/project.pbxproj +++ b/Vox.xcodeproj/project.pbxproj @@ -11,6 +11,8 @@ 3811C014203D888700EE52B8 /* ErrorsWithoutSource.json in Resources */ = {isa = PBXBuildFile; fileRef = 3811C013203D888700EE52B8 /* ErrorsWithoutSource.json */; }; 3821D26A2039E51D00AE241E /* Context_Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3821D2692039E51D00AE241E /* Context_Query.swift */; }; 3821D26C2039F64F00AE241E /* ResourcePool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3821D26B2039F64F00AE241E /* ResourcePool.swift */; }; + 384F03B5204E04FE0086F5E1 /* JSONAPIClient_Alamofire.swift in Sources */ = {isa = PBXBuildFile; fileRef = 384F03B4204E04FE0086F5E1 /* JSONAPIClient_Alamofire.swift */; }; + 384F03B8204E0B070086F5E1 /* AlamofireClientSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 384F03B7204E0B070086F5E1 /* AlamofireClientSpec.swift */; }; 3872FA3820379F8300CC26BD /* Vox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3872FA2E20379F8300CC26BD /* Vox.framework */; }; 3872FA3D20379F8300CC26BD /* VoxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3872FA3C20379F8300CC26BD /* VoxTests.swift */; }; 3872FA3F20379F8300CC26BD /* Vox.h in Headers */ = {isa = PBXBuildFile; fileRef = 3872FA3120379F8300CC26BD /* Vox.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -72,6 +74,7 @@ 38E00D41203911A100B45B4C /* ErrorObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38E00D40203911A100B45B4C /* ErrorObject.swift */; }; 38E00D44203911B700B45B4C /* JSONAPIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38E00D43203911B700B45B4C /* JSONAPIError.swift */; }; 38E00D4620391CE500B45B4C /* DeserializerSinglePolymorphic.json in Resources */ = {isa = PBXBuildFile; fileRef = 38E00D4520391CE400B45B4C /* DeserializerSinglePolymorphic.json */; }; + 38EE017B204E2124009A1FCF /* JSONAPIClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38EE017A204E2124009A1FCF /* JSONAPIClient.swift */; }; 70EC6E6C7F0EC2212D677322 /* Pods_Vox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8F6BBA32DFBB2745B08DC67C /* Pods_Vox.framework */; }; /* End PBXBuildFile section */ @@ -93,6 +96,8 @@ 3821D26B2039F64F00AE241E /* ResourcePool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResourcePool.swift; sourceTree = ""; }; 3821D273203A02B200AE241E /* SerializerCollectionSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SerializerCollectionSpec.swift; sourceTree = ""; }; 3821D274203A02B200AE241E /* SerializerSingleSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SerializerSingleSpec.swift; sourceTree = ""; }; + 384F03B4204E04FE0086F5E1 /* JSONAPIClient_Alamofire.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONAPIClient_Alamofire.swift; sourceTree = ""; }; + 384F03B7204E0B070086F5E1 /* AlamofireClientSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlamofireClientSpec.swift; sourceTree = ""; }; 3872FA2E20379F8300CC26BD /* Vox.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Vox.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3872FA3120379F8300CC26BD /* Vox.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Vox.h; sourceTree = ""; }; 3872FA3220379F8300CC26BD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -153,6 +158,7 @@ 38E00D4520391CE400B45B4C /* DeserializerSinglePolymorphic.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = DeserializerSinglePolymorphic.json; sourceTree = ""; }; 38E00D4820391D3A00B45B4C /* DataSourceSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataSourceSpec.swift; sourceTree = ""; }; 38E00D4B20391E2A00B45B4C /* PerformanceSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PerformanceSpec.swift; sourceTree = ""; }; + 38EE017A204E2124009A1FCF /* JSONAPIClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONAPIClient.swift; sourceTree = ""; }; 4C377C4C5E8EC0F47CC8A09A /* Pods_VoxTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_VoxTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 57CD3473858065C0C3F263AF /* Pods-Vox.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Vox.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Vox/Pods-Vox.debug.xcconfig"; sourceTree = ""; }; 8F6BBA32DFBB2745B08DC67C /* Pods_Vox.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Vox.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -200,6 +206,30 @@ path = Serializer; sourceTree = ""; }; + 384F03B2204E04480086F5E1 /* Plugins */ = { + isa = PBXGroup; + children = ( + 384F03B3204E045E0086F5E1 /* Alamofire */, + ); + path = Plugins; + sourceTree = ""; + }; + 384F03B3204E045E0086F5E1 /* Alamofire */ = { + isa = PBXGroup; + children = ( + 384F03B4204E04FE0086F5E1 /* JSONAPIClient_Alamofire.swift */, + ); + path = Alamofire; + sourceTree = ""; + }; + 384F03B6204E0AFE0086F5E1 /* Alamofire */ = { + isa = PBXGroup; + children = ( + 384F03B7204E0B070086F5E1 /* AlamofireClientSpec.swift */, + ); + path = Alamofire; + sourceTree = ""; + }; 3872FA2420379F8300CC26BD = { isa = PBXGroup; children = ( @@ -224,12 +254,8 @@ 3872FA3020379F8300CC26BD /* Vox */ = { isa = PBXGroup; children = ( - 38E00D42203911B700B45B4C /* Enum */, - 38E00CFF20390C2D00B45B4C /* Networking */, - 38E00CFC2038D30400B45B4C /* Helpers */, - 3872FA532037ABC600CC26BD /* Protocol */, - 3872FA4820379FD600CC26BD /* Class */, - 3872FA3120379F8300CC26BD /* Vox.h */, + 38EE017C204E2569009A1FCF /* Core */, + 384F03B2204E04480086F5E1 /* Plugins */, 3872FA3220379F8300CC26BD /* Info.plist */, ); path = Vox; @@ -238,6 +264,7 @@ 3872FA3B20379F8300CC26BD /* VoxTests */ = { isa = PBXGroup; children = ( + 384F03B6204E0AFE0086F5E1 /* Alamofire */, 3821D272203A02B200AE241E /* Serializer */, 38E00D4A20391E2A00B45B4C /* Performance */, 38E00D4720391D3A00B45B4C /* DataSource */, @@ -316,6 +343,7 @@ 38E00CFF20390C2D00B45B4C /* Networking */ = { isa = PBXGroup; children = ( + 38EE0179204E2110009A1FCF /* Client */, 3899555E20430EBC007E2DE5 /* Extensions */, 38E00D0020390C2D00B45B4C /* Class */, 38E00D0320390C2D00B45B4C /* Protocol */, @@ -392,6 +420,27 @@ path = Performance; sourceTree = ""; }; + 38EE0179204E2110009A1FCF /* Client */ = { + isa = PBXGroup; + children = ( + 38EE017A204E2124009A1FCF /* JSONAPIClient.swift */, + ); + path = Client; + sourceTree = ""; + }; + 38EE017C204E2569009A1FCF /* Core */ = { + isa = PBXGroup; + children = ( + 3872FA3120379F8300CC26BD /* Vox.h */, + 38E00D42203911B700B45B4C /* Enum */, + 38E00CFF20390C2D00B45B4C /* Networking */, + 38E00CFC2038D30400B45B4C /* Helpers */, + 3872FA532037ABC600CC26BD /* Protocol */, + 3872FA4820379FD600CC26BD /* Class */, + ); + path = Core; + sourceTree = ""; + }; F88997C466CC6768904BE1D9 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -573,11 +622,13 @@ "${SRCROOT}/Pods/Target Support Files/Pods-VoxTests/Pods-VoxTests-frameworks.sh", "${BUILT_PRODUCTS_DIR}/Nimble/Nimble.framework", "${BUILT_PRODUCTS_DIR}/Quick/Quick.framework", + "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nimble.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Quick.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -626,9 +677,11 @@ 3899556620431121007E2DE5 /* Pagination_CursorBased.swift in Sources */, 38E00D41203911A100B45B4C /* ErrorObject.swift in Sources */, 38E00D1A20390C2D00B45B4C /* FetchConfigurable.swift in Sources */, + 38EE017B204E2124009A1FCF /* JSONAPIClient.swift in Sources */, 38E00D1520390C2D00B45B4C /* Creatable.swift in Sources */, 38E00D2720390D5B00B45B4C /* Deserializer_Single.swift in Sources */, 38995564204310D0007E2DE5 /* Pagination_OffsetBased.swift in Sources */, + 384F03B5204E04FE0086F5E1 /* JSONAPIClient_Alamofire.swift in Sources */, 38E00D44203911B700B45B4C /* JSONAPIError.swift in Sources */, 3872FA502037A10800CC26BD /* Resource.swift in Sources */, 38E00D1D20390C2D00B45B4C /* FetchIncludable.swift in Sources */, @@ -674,6 +727,7 @@ 387FB92B203D08C100CE47F9 /* SerializerCollectionSpec.swift in Sources */, 387FB927203D07A500CE47F9 /* DataSourceSpec.swift in Sources */, 387FB925203D030800CE47F9 /* DeserializerCollectionSpec.swift in Sources */, + 384F03B8204E0B070086F5E1 /* AlamofireClientSpec.swift in Sources */, 387FB91E203CF04700CE47F9 /* DeserializerSingleSpec.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -820,6 +874,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.aronbalog.Vox; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG ALAMOFIRE"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -844,6 +899,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.aronbalog.Vox; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = ALAMOFIRE; SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; diff --git a/Vox.xcodeproj/xcshareddata/xcbaselines/3872FA3620379F8300CC26BD.xcbaseline/34CD9175-837D-417B-8E87-2804E7E86114.plist b/Vox.xcodeproj/xcshareddata/xcbaselines/3872FA3620379F8300CC26BD.xcbaseline/34CD9175-837D-417B-8E87-2804E7E86114.plist index ff01e85..bda759f 100644 --- a/Vox.xcodeproj/xcshareddata/xcbaselines/3872FA3620379F8300CC26BD.xcbaseline/34CD9175-837D-417B-8E87-2804E7E86114.plist +++ b/Vox.xcodeproj/xcshareddata/xcbaselines/3872FA3620379F8300CC26BD.xcbaseline/34CD9175-837D-417B-8E87-2804E7E86114.plist @@ -11,7 +11,7 @@ com.apple.XCTPerformanceMetric_WallClockTime baselineAverage - 2 + 2.5 baselineIntegrationDisplayName Local Baseline maxPercentRelativeStandardDeviation diff --git a/Vox.xcodeproj/xcshareddata/xcschemes/Vox.xcscheme b/Vox.xcodeproj/xcshareddata/xcschemes/Vox.xcscheme index 73f7fcc..d1219d0 100644 --- a/Vox.xcodeproj/xcshareddata/xcschemes/Vox.xcscheme +++ b/Vox.xcodeproj/xcshareddata/xcschemes/Vox.xcscheme @@ -39,11 +39,6 @@ BlueprintName = "VoxTests" ReferencedContainer = "container:Vox.xcodeproj"> - - - - diff --git a/Vox/Class/BaseResource.h b/Vox/Core/Class/BaseResource.h similarity index 100% rename from Vox/Class/BaseResource.h rename to Vox/Core/Class/BaseResource.h diff --git a/Vox/Class/BaseResource.m b/Vox/Core/Class/BaseResource.m similarity index 100% rename from Vox/Class/BaseResource.m rename to Vox/Core/Class/BaseResource.m diff --git a/Vox/Class/Context.swift b/Vox/Core/Class/Context.swift similarity index 100% rename from Vox/Class/Context.swift rename to Vox/Core/Class/Context.swift diff --git a/Vox/Class/Context_Query.swift b/Vox/Core/Class/Context_Query.swift similarity index 100% rename from Vox/Class/Context_Query.swift rename to Vox/Core/Class/Context_Query.swift diff --git a/Vox/Class/Deserializer.swift b/Vox/Core/Class/Deserializer.swift similarity index 100% rename from Vox/Class/Deserializer.swift rename to Vox/Core/Class/Deserializer.swift diff --git a/Vox/Class/Deserializer_Collection.swift b/Vox/Core/Class/Deserializer_Collection.swift similarity index 100% rename from Vox/Class/Deserializer_Collection.swift rename to Vox/Core/Class/Deserializer_Collection.swift diff --git a/Vox/Class/Deserializer_Single.swift b/Vox/Core/Class/Deserializer_Single.swift similarity index 100% rename from Vox/Class/Deserializer_Single.swift rename to Vox/Core/Class/Deserializer_Single.swift diff --git a/Vox/Class/Document.swift b/Vox/Core/Class/Document.swift similarity index 100% rename from Vox/Class/Document.swift rename to Vox/Core/Class/Document.swift diff --git a/Vox/Class/ErrorObject.swift b/Vox/Core/Class/ErrorObject.swift similarity index 100% rename from Vox/Class/ErrorObject.swift rename to Vox/Core/Class/ErrorObject.swift diff --git a/Vox/Class/JSONAPIDecoder.swift b/Vox/Core/Class/JSONAPIDecoder.swift similarity index 100% rename from Vox/Class/JSONAPIDecoder.swift rename to Vox/Core/Class/JSONAPIDecoder.swift diff --git a/Vox/Class/Links.swift b/Vox/Core/Class/Links.swift similarity index 100% rename from Vox/Class/Links.swift rename to Vox/Core/Class/Links.swift diff --git a/Vox/Class/Resource.swift b/Vox/Core/Class/Resource.swift similarity index 100% rename from Vox/Class/Resource.swift rename to Vox/Core/Class/Resource.swift diff --git a/Vox/Class/ResourcePool.swift b/Vox/Core/Class/ResourcePool.swift similarity index 100% rename from Vox/Class/ResourcePool.swift rename to Vox/Core/Class/ResourcePool.swift diff --git a/Vox/Enum/JSONAPIError.swift b/Vox/Core/Enum/JSONAPIError.swift similarity index 100% rename from Vox/Enum/JSONAPIError.swift rename to Vox/Core/Enum/JSONAPIError.swift diff --git a/Vox/Networking/Class/DataSource.swift b/Vox/Core/Networking/Class/DataSource.swift similarity index 100% rename from Vox/Networking/Class/DataSource.swift rename to Vox/Core/Networking/Class/DataSource.swift diff --git a/Vox/Networking/Class/Request.swift b/Vox/Core/Networking/Class/Request.swift similarity index 87% rename from Vox/Networking/Class/Request.swift rename to Vox/Core/Networking/Class/Request.swift index 4b9dcb0..3fa10b7 100644 --- a/Vox/Networking/Class/Request.swift +++ b/Vox/Core/Networking/Class/Request.swift @@ -12,7 +12,7 @@ public class Request: DataSourceRes var resource: ResourceType? var successBlock: SuccessCallbackType? - var failureBlock: ((Error) -> Void)? + var failureBlock: ((Error?) -> Void)? init(path: String, httpMethod: String, client: Client) { self.path = path @@ -20,7 +20,7 @@ public class Request: DataSourceRes self.client = client } - public func result(_ success: SuccessCallbackType, _ failure: ((Error) -> Void)?) throws { + public func result(_ success: SuccessCallbackType, _ failure: ((Error?) -> Void)?) throws { successBlock = success failureBlock = failure @@ -36,7 +36,7 @@ public class Request: DataSourceRes func execute() throws { let parameters: [String: Any]? = try resource?.documentDictionary() - client.executeRequest(path, method: httpMethod, queryItems: queryItems, parameters: parameters, success: { (response, data) in + client.executeRequest(path: path, method: httpMethod, queryItems: queryItems, bodyParameters: parameters, success: { (response, data) in if let success = self.successBlock as? DataSource.ResourceSuccessBlock { guard let data = data else { fatalError("Unhandled exception") @@ -45,10 +45,8 @@ public class Request: DataSourceRes do { let document: Document = try Deserializer.Single().deserialize(data: data) success(document) - } catch let __error as JSONAPIError { + } catch let __error { self.failureBlock?(__error) - } catch { - fatalError("Unhandled exception") } } else if let success = self.successBlock as? DataSource.OptionalResourceSuccessBlock { guard let data = data else { @@ -59,10 +57,8 @@ public class Request: DataSourceRes do { let document: Document = try Deserializer.Single().deserialize(data: data) success(document) - } catch let __error as JSONAPIError { + } catch let __error { self.failureBlock?(__error) - } catch { - fatalError("Unhandled exception") } } else if let success = self.successBlock as? DataSource.ResourceCollectionSuccessBlock { guard let data = data else { @@ -73,10 +69,8 @@ public class Request: DataSourceRes let document: Document<[ResourceType]> = try Deserializer.Collection().deserialize(data: data) document.client = self.client success(document) - } catch let __error as JSONAPIError { + } catch let __error { self.failureBlock?(__error) - } catch { - fatalError("Unhandled exception") } } else if let success = self.successBlock as? DataSource.DeleteSuccessBlock { success() @@ -89,10 +83,8 @@ public class Request: DataSourceRes do { let _: Document = try JSONAPIDecoder.decode(data: data) - } catch let error as JSONAPIError { - self.failureBlock?(error) - } catch { - fatalError("Unhandled exception") + } catch let __error { + self.failureBlock?(__error) } } } @@ -168,7 +160,6 @@ public class FetchRequest: Request< self.filter = filter return self - } diff --git a/Vox/Core/Networking/Client/JSONAPIClient.swift b/Vox/Core/Networking/Client/JSONAPIClient.swift new file mode 100644 index 0000000..3a7bf49 --- /dev/null +++ b/Vox/Core/Networking/Client/JSONAPIClient.swift @@ -0,0 +1 @@ +public struct JSONAPIClient {} diff --git a/Vox/Networking/Extensions/Pagination.swift b/Vox/Core/Networking/Extensions/Pagination.swift similarity index 100% rename from Vox/Networking/Extensions/Pagination.swift rename to Vox/Core/Networking/Extensions/Pagination.swift diff --git a/Vox/Networking/Extensions/Pagination_CursorBased.swift b/Vox/Core/Networking/Extensions/Pagination_CursorBased.swift similarity index 100% rename from Vox/Networking/Extensions/Pagination_CursorBased.swift rename to Vox/Core/Networking/Extensions/Pagination_CursorBased.swift diff --git a/Vox/Networking/Extensions/Pagination_OffsetBased.swift b/Vox/Core/Networking/Extensions/Pagination_OffsetBased.swift similarity index 100% rename from Vox/Networking/Extensions/Pagination_OffsetBased.swift rename to Vox/Core/Networking/Extensions/Pagination_OffsetBased.swift diff --git a/Vox/Networking/Extensions/Pagination_PageBased.swift b/Vox/Core/Networking/Extensions/Pagination_PageBased.swift similarity index 100% rename from Vox/Networking/Extensions/Pagination_PageBased.swift rename to Vox/Core/Networking/Extensions/Pagination_PageBased.swift diff --git a/Vox/Networking/Protocol/CRUD.swift b/Vox/Core/Networking/Protocol/CRUD.swift similarity index 100% rename from Vox/Networking/Protocol/CRUD.swift rename to Vox/Core/Networking/Protocol/CRUD.swift diff --git a/Vox/Core/Networking/Protocol/Client.swift b/Vox/Core/Networking/Protocol/Client.swift new file mode 100644 index 0000000..c542cc9 --- /dev/null +++ b/Vox/Core/Networking/Protocol/Client.swift @@ -0,0 +1,9 @@ +import Foundation + +public typealias ClientSuccessBlock = (_ response: HTTPURLResponse?, _ data: Data?) -> Void +public typealias ClientFailureBlock = (_ error: Error?, _ data: Data?) -> Void + +public protocol Client: class { + func executeRequest(path: String, method: String, queryItems: [URLQueryItem], bodyParameters: [String: Any]?, success: @escaping ClientSuccessBlock, failure: @escaping ClientFailureBlock) +} + diff --git a/Vox/Networking/Protocol/Creatable.swift b/Vox/Core/Networking/Protocol/Creatable.swift similarity index 100% rename from Vox/Networking/Protocol/Creatable.swift rename to Vox/Core/Networking/Protocol/Creatable.swift diff --git a/Vox/Networking/Protocol/DataSourceResultable.swift b/Vox/Core/Networking/Protocol/DataSourceResultable.swift similarity index 83% rename from Vox/Networking/Protocol/DataSourceResultable.swift rename to Vox/Core/Networking/Protocol/DataSourceResultable.swift index c4a7288..2274d1a 100644 --- a/Vox/Networking/Protocol/DataSourceResultable.swift +++ b/Vox/Core/Networking/Protocol/DataSourceResultable.swift @@ -3,5 +3,5 @@ import Foundation public protocol DataSourceResultable { associatedtype DataSourceResourceSuccessfulBlock - func result(_ success: DataSourceResourceSuccessfulBlock, _ failure: ((Error) -> Void)?) throws + func result(_ success: DataSourceResourceSuccessfulBlock, _ failure: ((Error?) -> Void)?) throws } diff --git a/Vox/Networking/Protocol/Deletable.swift b/Vox/Core/Networking/Protocol/Deletable.swift similarity index 100% rename from Vox/Networking/Protocol/Deletable.swift rename to Vox/Core/Networking/Protocol/Deletable.swift diff --git a/Vox/Networking/Protocol/FetchConfigurable.swift b/Vox/Core/Networking/Protocol/FetchConfigurable.swift similarity index 100% rename from Vox/Networking/Protocol/FetchConfigurable.swift rename to Vox/Core/Networking/Protocol/FetchConfigurable.swift diff --git a/Vox/Networking/Protocol/FetchFieldable.swift b/Vox/Core/Networking/Protocol/FetchFieldable.swift similarity index 100% rename from Vox/Networking/Protocol/FetchFieldable.swift rename to Vox/Core/Networking/Protocol/FetchFieldable.swift diff --git a/Vox/Networking/Protocol/FetchFilterable.swift b/Vox/Core/Networking/Protocol/FetchFilterable.swift similarity index 100% rename from Vox/Networking/Protocol/FetchFilterable.swift rename to Vox/Core/Networking/Protocol/FetchFilterable.swift diff --git a/Vox/Networking/Protocol/FetchIncludable.swift b/Vox/Core/Networking/Protocol/FetchIncludable.swift similarity index 100% rename from Vox/Networking/Protocol/FetchIncludable.swift rename to Vox/Core/Networking/Protocol/FetchIncludable.swift diff --git a/Vox/Networking/Protocol/FetchPageable.swift b/Vox/Core/Networking/Protocol/FetchPageable.swift similarity index 98% rename from Vox/Networking/Protocol/FetchPageable.swift rename to Vox/Core/Networking/Protocol/FetchPageable.swift index a012b7b..3b52376 100644 --- a/Vox/Networking/Protocol/FetchPageable.swift +++ b/Vox/Core/Networking/Protocol/FetchPageable.swift @@ -64,7 +64,7 @@ public extension Document where DataType: Collection, DataType.Element: Resource return dataSource.fetch() } - public func appendNext(_ completion: ((PaginationData) -> Void)? = nil, _ failure: ((Error) -> Void)? = nil) { + public func appendNext(_ completion: ((PaginationData) -> Void)? = nil, _ failure: ((Error?) -> Void)? = nil) { guard let next = self.links?.next else { return } diff --git a/Vox/Networking/Protocol/FetchSortable.swift b/Vox/Core/Networking/Protocol/FetchSortable.swift similarity index 100% rename from Vox/Networking/Protocol/FetchSortable.swift rename to Vox/Core/Networking/Protocol/FetchSortable.swift diff --git a/Vox/Networking/Protocol/Fetchable.swift b/Vox/Core/Networking/Protocol/Fetchable.swift similarity index 100% rename from Vox/Networking/Protocol/Fetchable.swift rename to Vox/Core/Networking/Protocol/Fetchable.swift diff --git a/Vox/Networking/Protocol/FilterStrategy.swift b/Vox/Core/Networking/Protocol/FilterStrategy.swift similarity index 100% rename from Vox/Networking/Protocol/FilterStrategy.swift rename to Vox/Core/Networking/Protocol/FilterStrategy.swift diff --git a/Vox/Networking/Protocol/PaginationStrategy.swift b/Vox/Core/Networking/Protocol/PaginationStrategy.swift similarity index 100% rename from Vox/Networking/Protocol/PaginationStrategy.swift rename to Vox/Core/Networking/Protocol/PaginationStrategy.swift diff --git a/Vox/Networking/Protocol/QueryItemsCustomizable.swift b/Vox/Core/Networking/Protocol/QueryItemsCustomizable.swift similarity index 100% rename from Vox/Networking/Protocol/QueryItemsCustomizable.swift rename to Vox/Core/Networking/Protocol/QueryItemsCustomizable.swift diff --git a/Vox/Networking/Protocol/Router.swift b/Vox/Core/Networking/Protocol/Router.swift similarity index 100% rename from Vox/Networking/Protocol/Router.swift rename to Vox/Core/Networking/Protocol/Router.swift diff --git a/Vox/Networking/Protocol/Updatable.swift b/Vox/Core/Networking/Protocol/Updatable.swift similarity index 100% rename from Vox/Networking/Protocol/Updatable.swift rename to Vox/Core/Networking/Protocol/Updatable.swift diff --git a/Vox/Protocol/NullableValue.swift b/Vox/Core/Protocol/NullableValue.swift similarity index 100% rename from Vox/Protocol/NullableValue.swift rename to Vox/Core/Protocol/NullableValue.swift diff --git a/Vox/Vox.h b/Vox/Core/Vox.h similarity index 100% rename from Vox/Vox.h rename to Vox/Core/Vox.h diff --git a/Vox/Networking/Protocol/Client.swift b/Vox/Networking/Protocol/Client.swift deleted file mode 100644 index 1b24185..0000000 --- a/Vox/Networking/Protocol/Client.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Foundation - -public typealias ClientSuccessBlock = (_ response: HTTPURLResponse?, _ data: Data?) -> Void -public typealias ClientFailureBlock = (_ error: Error, _ data: Data?) -> Void - -public protocol Client: class { - func executeRequest(_ path: String, method: String, queryItems: [URLQueryItem], parameters: [String: Any]?, success: @escaping ClientSuccessBlock, _ failure: @escaping ClientFailureBlock) -} - diff --git a/Vox/Plugins/Alamofire/JSONAPIClient_Alamofire.swift b/Vox/Plugins/Alamofire/JSONAPIClient_Alamofire.swift new file mode 100644 index 0000000..08efe29 --- /dev/null +++ b/Vox/Plugins/Alamofire/JSONAPIClient_Alamofire.swift @@ -0,0 +1,67 @@ +import Foundation +import Alamofire + +extension JSONAPIClient { + #if ALAMOFIRE + public class Alamofire: Client { + public let baseURL: URL + + public init(baseURL: URL) { + self.baseURL = baseURL + } + + public func executeRequest(path: String, method: String, queryItems: [URLQueryItem], bodyParameters: [String : Any]?, success: @escaping ClientSuccessBlock, failure: @escaping ClientFailureBlock) { + let sessionManager = SessionManager.default + let url = baseURL.appendingPathComponent(path) + let headers: HTTPHeaders = [ + "Content-Type": "application/vnd.api+json" + ] + + let request = multiEncodedURLRequest(url: url, method: method, queryItems: queryItems, bodyParameters: bodyParameters, headers: headers) + + sessionManager + .request(request) + .validate(statusCode: 200..<300) + .validate(contentType: ["application/vnd.api+json"]) + .responseData { (dataResponse) in + let response = dataResponse.response + let data = dataResponse.data + let error = dataResponse.error + + dataResponse + .result + .ifSuccess { + success(response, data) + } + .ifFailure { + failure(error, data) + } + } + } + + private func multiEncodedURLRequest(url: URL, method: String, queryItems: [URLQueryItem], bodyParameters: [String: Any]?, headers: HTTPHeaders) -> URLRequest { + let temporaryRequest = URLRequest(url: url) + + var parameters: [String: Any] = [:] + + queryItems.forEach({ (item) in + parameters[item.name] = item.value ?? "" + }) + + let urlEncoding = try! URLEncoding.default.encode(temporaryRequest, with: parameters) + let bodyEncoding = try! JSONEncoding.default.encode(temporaryRequest, with: bodyParameters) + + var compositeRequest = urlEncoding.urlRequest + + compositeRequest?.httpMethod = method + compositeRequest?.httpBody = bodyEncoding.httpBody + + headers.forEach({ (header) in + compositeRequest?.addValue(header.value, forHTTPHeaderField: header.key) + }) + + return compositeRequest! + } + } + #endif +} diff --git a/VoxTests/Alamofire/AlamofireClientSpec.swift b/VoxTests/Alamofire/AlamofireClientSpec.swift new file mode 100644 index 0000000..c9f4594 --- /dev/null +++ b/VoxTests/Alamofire/AlamofireClientSpec.swift @@ -0,0 +1,80 @@ +import UIKit +import Quick +import Nimble + +@testable import Vox + +fileprivate class Article4: Resource { + override class var resourceType: String { + return "articles4" + } + + override class var codingKeys: [String: String] { + return [ + "descriptionText": "description" + ] + } + + @objc dynamic var title: String? + @objc dynamic var descriptionText: String? + @objc dynamic var keywords: [String]? + @objc dynamic var coauthors: [Person4]? + @objc dynamic var author: Person4? + @objc dynamic var hint: String? + @objc dynamic var customObject: [String: Any]? +} + +fileprivate class Person4: Resource { + override class var resourceType: String { + return "persons4" + } + + @objc dynamic var name: String? + @objc dynamic var age: NSNumber? + @objc dynamic var gender: String? + @objc dynamic var favoriteArticle: Article4? + +} + +class AlamofireClientSpec: QuickSpec { + override func spec() { + describe("DataSource with Alamofire client") { + let client = JSONAPIClient.Alamofire(baseURL: URL(string: "http://demo7377577.mockable.io")!) + + context("when executing request with valid URL", { + let dataSource = DataSource(strategy: .path("vox/articles"), client: client) + + var document: Document<[Article4]>? + + try! dataSource.fetch().result({ (_document) in + document = _document + }, nil) + + it("receives response", closure: { + expect(document).toEventuallyNot(beNil()) + }) + }) + + context("when executing request with invalid URL", { + let dataSource = DataSource(strategy: .path("invalid-url"), client: client) + + var error: Error? + + try! dataSource + .fetch() + .fields([ + "articles": ["title"] + ]) + .result({ (_) in + + }, { (_error) in + error = _error + }) + + it("receives error", closure: { + expect(error).toEventuallyNot(beNil()) + }) + }) + } + } +} diff --git a/VoxTests/DataSource/DataSourceSpec.swift b/VoxTests/DataSource/DataSourceSpec.swift index e3bfd5e..e5c7d7f 100644 --- a/VoxTests/DataSource/DataSourceSpec.swift +++ b/VoxTests/DataSource/DataSourceSpec.swift @@ -93,7 +93,7 @@ fileprivate class MockClient: Client { let invocation = Invocation() let executeRequestInspector = ExecuteRequestInspector() - func executeRequest(_ path: String, method: String, queryItems: [URLQueryItem], parameters: [String : Any]?, success: @escaping ClientSuccessBlock, _ failure: @escaping ClientFailureBlock) { + func executeRequest(path: String, method: String, queryItems: [URLQueryItem], bodyParameters: [String : Any]?, success: @escaping ClientSuccessBlock, failure: @escaping ClientFailureBlock) { invocation.executeRequest.invoke() executeRequestInspector.path = path executeRequestInspector.queryItems = queryItems diff --git a/VoxTests/DataSource/PaginationSpec.swift b/VoxTests/DataSource/PaginationSpec.swift index abd95ae..ae699af 100644 --- a/VoxTests/DataSource/PaginationSpec.swift +++ b/VoxTests/DataSource/PaginationSpec.swift @@ -69,7 +69,7 @@ fileprivate class MockClient: Client { var nextPath: String? var queryItems: [URLQueryItem]? - func executeRequest(_ path: String, method: String, queryItems: [URLQueryItem], parameters: [String : Any]?, success: @escaping ClientSuccessBlock, _ failure: @escaping ClientFailureBlock) { + func executeRequest(path: String, method: String, queryItems: [URLQueryItem], bodyParameters: [String : Any]?, success: @escaping ClientSuccessBlock, failure: @escaping ClientFailureBlock) { if count == 0 { count += 1