diff --git a/.gitignore b/.gitignore index 93c86d3..d16aced 100644 --- a/.gitignore +++ b/.gitignore @@ -34,4 +34,4 @@ Carthage/Build # Note: if you ignore the Pods directory, make sure to uncomment # `pod install` in .travis.yml # -# Pods/ +Example/Pods/ diff --git a/Example/Podfile b/Example/Podfile index f6cdfc5..1e08f5e 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -1,11 +1,8 @@ use_frameworks! - target 'RHNetwork_Example' do pod 'Network.swift', :path => '../' target 'RHNetwork_Tests' do inherit! :search_paths - - end end diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 30b885c..1f9d325 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -1,24 +1,24 @@ PODS: - - Alamofire (4.8.2) + - Alamofire (4.9.1) - Moya (13.0.1): - Moya/Core (= 13.0.1) - Moya/Core (13.0.1): - Alamofire (~> 4.1) - Result (~> 4.1) - - Network.swift (0.7.5): + - Network.swift (0.8.7): - Moya (~> 13.0) - - RxSwift (~> 5.0) + - RxSwift - RxSwiftExtensions.swift (~> 0.2) - Result (4.1.0) - - RxSwift (5.0.0) - - RxSwiftExtensions.swift (0.2.3): + - RxSwift (5.1.1) + - RxSwiftExtensions.swift (0.2.5): - RxSwift (~> 5.0) DEPENDENCIES: - Network.swift (from `../`) SPEC REPOS: - https://github.com/cocoapods/specs.git: + trunk: - Alamofire - Moya - Result @@ -30,13 +30,13 @@ EXTERNAL SOURCES: :path: "../" SPEC CHECKSUMS: - Alamofire: ae5c501addb7afdbb13687d7f2f722c78734c2d3 + Alamofire: 85e8a02c69d6020a0d734f6054870d7ecb75cf18 Moya: f4a4b80ff2f8a4ffc208dfb31cd91636622fee6e - Network.swift: 93a56afff15c680c84fe20e57a4805be9b06ff9b + Network.swift: 39f867d898b4e5b6bc9c3c93c668b15832561b5d Result: bd966fac789cc6c1563440b348ab2598cc24d5c7 - RxSwift: 8b0671caa829a763bbce7271095859121cbd895f - RxSwiftExtensions.swift: 7f955609c3d3e03bd9f731fc89b9725d711c5b3e + RxSwift: 81470a2074fa8780320ea5fe4102807cb7118178 + RxSwiftExtensions.swift: 2e288839301728e5de4de06bf143359ed75198c3 -PODFILE CHECKSUM: 46cedb2e0fda0c68bff76ee268c81d537ed747f6 +PODFILE CHECKSUM: 6ab97ae7f336ec2ae0d893569e6b4fdd4ecad3fc -COCOAPODS: 1.6.2 +COCOAPODS: 1.10.1 diff --git a/Example/RHNetwork.xcodeproj/project.pbxproj b/Example/RHNetwork.xcodeproj/project.pbxproj index a850c16..5f45dcc 100644 --- a/Example/RHNetwork.xcodeproj/project.pbxproj +++ b/Example/RHNetwork.xcodeproj/project.pbxproj @@ -169,7 +169,7 @@ 607FACCC1AFB9204008FA782 /* Sources */, 607FACCD1AFB9204008FA782 /* Frameworks */, 607FACCE1AFB9204008FA782 /* Resources */, - 51B91A22A39F119CB0866663 /* [CP] Embed Pods Frameworks */, + 129B3BB7DA7779FE8D62E709 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -261,29 +261,35 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 2F16F8D066214FE0086ACEDF /* [CP] Check Pods Manifest.lock */ = { + 129B3BB7DA7779FE8D62E709 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-RHNetwork_Example/Pods-RHNetwork_Example-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework", + "${BUILT_PRODUCTS_DIR}/Moya/Moya.framework", + "${BUILT_PRODUCTS_DIR}/Network.swift/Network.framework", + "${BUILT_PRODUCTS_DIR}/Result/Result.framework", + "${BUILT_PRODUCTS_DIR}/RxSwift/RxSwift.framework", + "${BUILT_PRODUCTS_DIR}/RxSwiftExtensions.swift/RxSwiftExtensions.framework", ); + name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RHNetwork_Example-checkManifestLockResult.txt", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Moya.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Network.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Result.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwift.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwiftExtensions.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RHNetwork_Example/Pods-RHNetwork_Example-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 51B91A22A39F119CB0866663 /* [CP] Embed Pods Frameworks */ = { + 2F16F8D066214FE0086ACEDF /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -291,28 +297,18 @@ inputFileListPaths = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RHNetwork_Example/Pods-RHNetwork_Example-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework", - "${BUILT_PRODUCTS_DIR}/Moya/Moya.framework", - "${BUILT_PRODUCTS_DIR}/Network.swift/Network.framework", - "${BUILT_PRODUCTS_DIR}/Result/Result.framework", - "${BUILT_PRODUCTS_DIR}/RxSwift/RxSwift.framework", - "${BUILT_PRODUCTS_DIR}/RxSwiftExtensions.swift/RxSwiftExtensions.framework", + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); - name = "[CP] Embed Pods Frameworks"; + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( ); outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Moya.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Network.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Result.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwift.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwiftExtensions.framework", + "$(DERIVED_FILE_DIR)/Pods-RHNetwork_Example-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RHNetwork_Example/Pods-RHNetwork_Example-frameworks.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; 9CEC66C4A061A89BDD31C31A /* [CP] Check Pods Manifest.lock */ = { @@ -493,10 +489,11 @@ INFOPLIST_FILE = RHNetwork/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MODULE_NAME = ExampleApp; + ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -508,10 +505,11 @@ INFOPLIST_FILE = RHNetwork/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MODULE_NAME = ExampleApp; + ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/Example/RHNetwork.xcworkspace/contents.xcworkspacedata b/Example/RHNetwork.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..9c21fec --- /dev/null +++ b/Example/RHNetwork.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Example/RHNetwork/AppDelegate.swift b/Example/RHNetwork/AppDelegate.swift index 3bda664..d87b3ec 100644 --- a/Example/RHNetwork/AppDelegate.swift +++ b/Example/RHNetwork/AppDelegate.swift @@ -14,7 +14,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } diff --git a/Example/Tests/Tests.swift b/Example/Tests/Tests.swift index 0b93ac3..511ec5d 100644 --- a/Example/Tests/Tests.swift +++ b/Example/Tests/Tests.swift @@ -1,5 +1,5 @@ import XCTest -import RHNetwork +import Network class Tests: XCTestCase { diff --git a/Network.swift.podspec b/Network.swift.podspec index dbcef21..6e01317 100644 --- a/Network.swift.podspec +++ b/Network.swift.podspec @@ -30,8 +30,8 @@ Pod::Spec.new do |s| s.source_files = 'Network/Classes/**/*.swift' - s.dependency 'Moya', '~>13.0' - s.dependency 'RxSwift', '~>5.0' - s.dependency 'RxSwiftExtensions.swift', '~>0.2' + s.dependency 'Moya', '~> 13.0' + s.dependency 'RxSwift' + s.dependency 'RxSwiftExtensions.swift', '~> 0.2' end diff --git a/Network/Classes/Network/CacheType.swift b/Network/Classes/Network/CacheType.swift index f7e6af2..7f841b0 100644 --- a/Network/Classes/Network/CacheType.swift +++ b/Network/Classes/Network/CacheType.swift @@ -4,7 +4,7 @@ // Created by 荣恒 on 2019/3/29. // Copyright © 2019 荣恒. All rights reserved. // - +import Foundation import Moya public protocol CacheType where Self : TargetType { @@ -41,3 +41,23 @@ public extension TargetType { var sampleData: Data { return Data() } } + + +extension Error { + func mapError() -> NetworkError { + if let error = self as? NetworkError { + return error + } + if let moyaError = self as? MoyaError { + switch moyaError { + case .requestMapping(let message): + return .error(value: message) + case .encodableMapping(let error): + return .network(value: error) + default: + break + } + } + return .error(value: self.localizedDescription) + } +} diff --git a/Network/Classes/Network/MoyaProvider+Rx.swift b/Network/Classes/Network/MoyaProvider+Rx.swift index 1d4c667..5ed2200 100755 --- a/Network/Classes/Network/MoyaProvider+Rx.swift +++ b/Network/Classes/Network/MoyaProvider+Rx.swift @@ -33,6 +33,18 @@ public extension Reactive where Base: MoyaProviderType { } } + /// 带 NetworkConfigure 的 请求 返回 + func responseRequest(_ token : Base.Target, + dataKey : String = NetworkConfigure.data, + codeKey : String = NetworkConfigure.code, + messageKey : String = NetworkConfigure.message, + successCode : Int = NetworkConfigure.success + ) -> Observable { + return request(token).flatMap({ response -> Observable in + return .just(response) + }) + } + /// 请求成功 func request(_ token : Base.Target, codeKey : String = NetworkConfigure.code, @@ -95,6 +107,72 @@ public extension Reactive where Base: MoyaProviderType { } +// MARK: - 扩展 Provider request 方法 +public extension Reactive where Base: MoyaProviderType { + + /// 返回结果为字典,返回为data的属性 如果是字典则返回字典 如果不为字典则返回nil + func hashRequest(_ token : Base.Target, + dataKey : String = NetworkConfigure.data, + codeKey : String = NetworkConfigure.code, + messageKey : String = NetworkConfigure.message, + successCode : Int = NetworkConfigure.success + ) -> Observable<[String: Any]?> { + + return request(token).flatMap({ response -> Observable<[String: Any]?> in + + guard let jsonDictionary = (try? response.mapJSON()) as? NSDictionary else { + let error = "无效的json格式" + return .error(NetworkError.error(value: error)) + } + guard let code = jsonDictionary.value(forKeyPath: codeKey) as? Int else { + let error = "服务器code解析错误\n\(responseDescribe(response) ?? "")" + return .error(NetworkError.error(value: error)) + } + guard code == successCode else { + handleServiceCode(code) + let message = (jsonDictionary.value(forKeyPath: messageKey) as? String) ?? "code不等于\(successCode)" + return .error(NetworkError.service(code: code, message: message)) + } + + if let object = jsonDictionary.value(forKeyPath: dataKey) as? [String: Any] { + return .just(object) + } + return .just(nil) + }) + } + + /// 返回结果为字符串,返回为dataKey的属性 可为空 + func stringRequest(_ token : Base.Target, + dataKey : String = NetworkConfigure.data, + codeKey : String = NetworkConfigure.code, + messageKey : String = NetworkConfigure.message, + successCode : Int = NetworkConfigure.success + ) -> Observable { + + return request(token).flatMap({ response -> Observable in + + guard let jsonDictionary = (try? response.mapJSON()) as? NSDictionary else { + let error = "无效的json格式" + return .error(NetworkError.error(value: error)) + } + guard let code = jsonDictionary.value(forKeyPath: codeKey) as? Int else { + let error = "服务器code解析错误\n\(responseDescribe(response) ?? "")" + return .error(NetworkError.error(value: error)) + } + guard code == successCode else { + handleServiceCode(code) + let message = (jsonDictionary.value(forKeyPath: messageKey) as? String) ?? "code不等于\(successCode)" + return .error(NetworkError.service(code: code, message: message)) + } + + if let object = jsonDictionary.value(forKeyPath: dataKey) as? String { + return .just(object) + } + return .just(nil) + }) + } +} + /// 解析JSON private func decoderJSON(_ type: T.Type, json: NSDictionary, forKeyPath keyPath: String) throws -> T { guard let jsonObject = json.value(forKeyPath: keyPath) else { diff --git a/Network/Classes/Network/NetworkRequest.swift b/Network/Classes/Network/NetworkRequest.swift index 83e58c0..ea2ba23 100644 --- a/Network/Classes/Network/NetworkRequest.swift +++ b/Network/Classes/Network/NetworkRequest.swift @@ -32,10 +32,14 @@ public func request( }) .shareOnce() + let networkError = error.asObservable().map { error -> NetworkError in + return error.mapError() + } + return ( result, isActivity.asObservable(), - error.asObservable().map({ $0 as? NetworkError }).filterNil() + networkError ) } @@ -56,10 +60,14 @@ public func network( }) .shareOnce() + let networkError = error.asObservable().map { error -> NetworkError in + return error.mapError() + } + return ( result, isActivity.asObservable(), - error.asObservable().map({ $0 as? NetworkError }).filterNil() + networkError ) } @@ -82,10 +90,14 @@ public func network( }) .shareOnce() + let networkError = error.asObservable().map { error -> NetworkError in + return error.mapError() + } + return ( result, isActivity.asObservable(), - error.asObservable().map({ $0 as? NetworkError }).filterNil() + networkError ) } diff --git a/Network/Classes/Network/Observable+Response.swift b/Network/Classes/Network/Observable+Response.swift index c3a9a1f..3469e96 100755 --- a/Network/Classes/Network/Observable+Response.swift +++ b/Network/Classes/Network/Observable+Response.swift @@ -9,25 +9,25 @@ import Moya #endif // MARK: - Extension for processing raw NSData generated by network access -extension ObservableType where E == Response { +extension ObservableType where Element == Response { /// Filters out responses that don't fall within the given range, generating errors when others are encountered. - public func filter(statusCodes: R) -> Observable where R.Bound == Int { + public func filter(statusCodes: R) -> Observable where R.Bound == Int { return flatMap { Observable.just(try $0.filter(statusCodes: statusCodes)) } } /// Filters out responses that has the specified `statusCode`. - public func filter(statusCode: Int) -> Observable { + public func filter(statusCode: Int) -> Observable { return flatMap { Observable.just(try $0.filter(statusCode: statusCode)) } } /// Filters out responses where `statusCode` falls within the range 200 - 299. - public func filterSuccessfulStatusCodes() -> Observable { + public func filterSuccessfulStatusCodes() -> Observable { return flatMap { Observable.just(try $0.filterSuccessfulStatusCodes()) } } /// Filters out responses where `statusCode` falls within the range 200 - 399 - public func filterSuccessfulStatusAndRedirectCodes() -> Observable { + public func filterSuccessfulStatusAndRedirectCodes() -> Observable { return flatMap { Observable.just(try $0.filterSuccessfulStatusAndRedirectCodes()) } } @@ -52,7 +52,7 @@ extension ObservableType where E == Response { } } -extension ObservableType where E == ProgressResponse { +extension ObservableType where Element == ProgressResponse { /** Filter completed progress response and maps to actual response