add base network service
This commit is contained in:
parent
c29ff18758
commit
84b4d9dc86
1
Cartfile
1
Cartfile
|
|
@ -2,3 +2,4 @@ github "CocoaLumberjack/CocoaLumberjack" ~> 3.0.0
|
|||
github "ReactiveX/RxSwift" "3.0.1"
|
||||
github "RxSwiftCommunity/RxAlamofire" "3.0.1"
|
||||
github "Hearst-DD/ObjectMapper" ~> 2.1
|
||||
github "scalessec/Toast-Swift" ~> 2.0.0
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
github "Alamofire/Alamofire" "4.2.0"
|
||||
github "Alamofire/Alamofire" "4.3.0"
|
||||
github "CocoaLumberjack/CocoaLumberjack" "3.0.0"
|
||||
github "Hearst-DD/ObjectMapper" "2.2.1"
|
||||
github "Hearst-DD/ObjectMapper" "2.2.2"
|
||||
github "ReactiveX/RxSwift" "3.0.1"
|
||||
github "scalessec/Toast-Swift" "2.0.0"
|
||||
github "RxSwiftCommunity/RxAlamofire" "3.0.1"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = "LeadKit"
|
||||
s.version = "0.3.2"
|
||||
s.version = "0.4.0"
|
||||
s.summary = "iOS framework with a bunch of tools for rapid development"
|
||||
s.homepage = "https://github.com/TouchInstinct/LeadKit"
|
||||
s.license = "Apache License, Version 2.0"
|
||||
|
|
@ -14,4 +14,5 @@ Pod::Spec.new do |s|
|
|||
s.dependency "RxCocoa", '3.0.1'
|
||||
s.dependency "RxAlamofire", '3.0.0'
|
||||
s.dependency "ObjectMapper", '~> 2.1'
|
||||
s.dependency "Toast-Swift", '~> 2.0.0'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -21,6 +21,9 @@
|
|||
7827C9391DE4ADB2009DA4E6 /* RxSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7827C9331DE4ADB2009DA4E6 /* RxSwift.framework */; };
|
||||
7834236A1DB8D0E100A79643 /* StoryboardProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 783423691DB8D0E100A79643 /* StoryboardProtocol.swift */; };
|
||||
7837F60F1CBCF5C0000D74C1 /* EstimatedViewHeightProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7837F60E1CBCF5C0000D74C1 /* EstimatedViewHeightProtocol.swift */; };
|
||||
783AF06B1E41CE6C00EC5ADE /* Observable+ToastErrorLogging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 783AF06A1E41CE6C00EC5ADE /* Observable+ToastErrorLogging.swift */; };
|
||||
783AF06D1E41CF5B00EC5ADE /* NetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 783AF06C1E41CF5B00EC5ADE /* NetworkService.swift */; };
|
||||
783AF06F1E41D84A00EC5ADE /* ToastSwiftFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 783AF06E1E41D84A00EC5ADE /* ToastSwiftFramework.framework */; };
|
||||
786D78E81D53C378006B2CEA /* AlamofireRequest+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786D78E71D53C378006B2CEA /* AlamofireRequest+Extensions.swift */; };
|
||||
786D78EC1D53C46E006B2CEA /* AlamofireManager+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 786D78EB1D53C46E006B2CEA /* AlamofireManager+Extensions.swift */; };
|
||||
7873D14F1E1127BC001816EB /* LeadKitError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7873D14E1E1127BC001816EB /* LeadKitError.swift */; };
|
||||
|
|
@ -100,6 +103,9 @@
|
|||
7827C9331DE4ADB2009DA4E6 /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = ../../../Carthage/Build/iOS/RxSwift.framework; sourceTree = "<group>"; };
|
||||
783423691DB8D0E100A79643 /* StoryboardProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryboardProtocol.swift; sourceTree = "<group>"; };
|
||||
7837F60E1CBCF5C0000D74C1 /* EstimatedViewHeightProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EstimatedViewHeightProtocol.swift; sourceTree = "<group>"; };
|
||||
783AF06A1E41CE6C00EC5ADE /* Observable+ToastErrorLogging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Observable+ToastErrorLogging.swift"; sourceTree = "<group>"; };
|
||||
783AF06C1E41CF5B00EC5ADE /* NetworkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkService.swift; sourceTree = "<group>"; };
|
||||
783AF06E1E41D84A00EC5ADE /* ToastSwiftFramework.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ToastSwiftFramework.framework; path = ../../../Carthage/Build/iOS/ToastSwiftFramework.framework; sourceTree = "<group>"; };
|
||||
786D78E71D53C378006B2CEA /* AlamofireRequest+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AlamofireRequest+Extensions.swift"; sourceTree = "<group>"; };
|
||||
786D78EB1D53C46E006B2CEA /* AlamofireManager+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AlamofireManager+Extensions.swift"; sourceTree = "<group>"; };
|
||||
7873D14E1E1127BC001816EB /* LeadKitError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LeadKitError.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -163,6 +169,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7827C9361DE4ADB2009DA4E6 /* ObjectMapper.framework in Frameworks */,
|
||||
783AF06F1E41D84A00EC5ADE /* ToastSwiftFramework.framework in Frameworks */,
|
||||
7827C9351DE4ADB2009DA4E6 /* CocoaLumberjack.framework in Frameworks */,
|
||||
7827C9391DE4ADB2009DA4E6 /* RxSwift.framework in Frameworks */,
|
||||
7827C9341DE4ADB2009DA4E6 /* Alamofire.framework in Frameworks */,
|
||||
|
|
@ -191,6 +198,7 @@
|
|||
7827C9311DE4ADB2009DA4E6 /* RxAlamofire.framework */,
|
||||
7827C9321DE4ADB2009DA4E6 /* RxCocoa.framework */,
|
||||
7827C9331DE4ADB2009DA4E6 /* RxSwift.framework */,
|
||||
783AF06E1E41D84A00EC5ADE /* ToastSwiftFramework.framework */,
|
||||
);
|
||||
path = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -249,6 +257,14 @@
|
|||
path = Sequence;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
783AF0591E40824300EC5ADE /* Services */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
783AF06C1E41CF5B00EC5ADE /* NetworkService.swift */,
|
||||
);
|
||||
path = Services;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
786D78E61D53C355006B2CEA /* Alamofire */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -272,6 +288,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
787609211E1403830093CE36 /* Observable+DeferredJust.swift */,
|
||||
783AF06A1E41CE6C00EC5ADE /* Observable+ToastErrorLogging.swift */,
|
||||
);
|
||||
path = Observable;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -329,6 +346,7 @@
|
|||
78A74EAA1C6B401800FE9724 /* Classes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
783AF0591E40824300EC5ADE /* Services */,
|
||||
78B0FC7B1C6B2BAE00358B64 /* Logging */,
|
||||
78753E2A1DE58BED006BC0FB /* Cursors */,
|
||||
);
|
||||
|
|
@ -670,6 +688,7 @@
|
|||
"$(SRCROOT)/../Carthage/Build/iOS/Alamofire.framework",
|
||||
"$(SRCROOT)/../Carthage/Build/iOS/ObjectMapper.framework",
|
||||
"$(SRCROOT)/../Carthage/Build/iOS/RxAlamofire.framework",
|
||||
"$(SRCROOT)/../Carthage/Build/iOS/ToastSwiftFramework.framework",
|
||||
);
|
||||
name = "Carthage copy-frameworks";
|
||||
outputPaths = (
|
||||
|
|
@ -714,6 +733,7 @@
|
|||
78C36F7E1D801E3E00E7EBEA /* Double+Rounding.swift in Sources */,
|
||||
78CFEE551C5C45E500F50370 /* NibNameProtocol.swift in Sources */,
|
||||
787609221E1403830093CE36 /* Observable+DeferredJust.swift in Sources */,
|
||||
783AF06B1E41CE6C00EC5ADE /* Observable+ToastErrorLogging.swift in Sources */,
|
||||
78CFEE561C5C45E500F50370 /* ReuseIdentifierProtocol.swift in Sources */,
|
||||
78A0FCC81DC366A10070B5E1 /* StoryboardProtocol+Extensions.swift in Sources */,
|
||||
78B036411DA4D7060021D5CC /* UIImage+Extensions.swift in Sources */,
|
||||
|
|
@ -723,6 +743,7 @@
|
|||
78C36F811D8021DD00E7EBEA /* UIColor+Hex.swift in Sources */,
|
||||
78CFEE5B1C5C45E500F50370 /* ViewModelProtocol.swift in Sources */,
|
||||
EF5FB5691E0141610030E4BE /* UIView+Rotation.swift in Sources */,
|
||||
783AF06D1E41CF5B00EC5ADE /* NetworkService.swift in Sources */,
|
||||
780F56CC1E0D7ACA004530B6 /* ObservableMappable.swift in Sources */,
|
||||
780D23431DA412470084620D /* CGImage+Alpha.swift in Sources */,
|
||||
78CFEE5A1C5C45E500F50370 /* ViewHeightProtocol.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,119 @@
|
|||
//
|
||||
// Copyright (c) 2017 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
import Alamofire
|
||||
import ObjectMapper
|
||||
import RxAlamofire
|
||||
|
||||
/// Base network service implementation build on top of LeadKit extensions for Alamofire.
|
||||
/// Has an ability to automatically show / hide network activity indicator
|
||||
/// and shows errors in DEBUG mode
|
||||
open class NetworkService {
|
||||
|
||||
private let disposeBag = DisposeBag()
|
||||
private let requestCount = Variable<Int>(0)
|
||||
|
||||
public let sessionManager: Alamofire.SessionManager
|
||||
|
||||
/// Let netwrok service automatically show / hide activity indicator
|
||||
public func bindActivityIndicator() {
|
||||
return requestCount.asDriver()
|
||||
.map { $0 != 0 }
|
||||
.drive(UIApplication.shared.rx.isNetworkActivityIndicatorVisible)
|
||||
.addDisposableTo(disposeBag)
|
||||
}
|
||||
|
||||
/// Creates new instance of NetworkService with given Alamofire session manager
|
||||
///
|
||||
/// - Parameter sessionManager: Alamofire.SessionManager to use for requests
|
||||
public init(sessionManager: Alamofire.SessionManager) {
|
||||
self.sessionManager = sessionManager
|
||||
}
|
||||
|
||||
/// Perform reactive request to get mapped ObservableMappable model and http response
|
||||
///
|
||||
/// - Parameter parameters: api parameters to pass Alamofire
|
||||
/// - Returns: Observable of tuple containing (HTTPURLResponse, ObservableMappable)
|
||||
public func rxRequest<T: ObservableMappable>(with parameters: ApiRequestParameters)
|
||||
-> Observable<(response: HTTPURLResponse, model: T)> where T.ModelType == T {
|
||||
|
||||
return sessionManager.rx.responseObservableModel(requestParameters: parameters)
|
||||
.counterTracking(for: self)
|
||||
.showErrorsInToastInDebugMode()
|
||||
}
|
||||
|
||||
/// Perform reactive request to get mapped ImmutableMappable model and http response
|
||||
///
|
||||
/// - Parameter parameters: api parameters to pass Alamofire
|
||||
/// - Returns: Observable of tuple containing (HTTPURLResponse, ImmutableMappable)
|
||||
public func rxRequest<T: ImmutableMappable>(with parameters: ApiRequestParameters)
|
||||
-> Observable<(response: HTTPURLResponse, model: T)> {
|
||||
|
||||
return sessionManager.rx.responseModel(requestParameters: parameters)
|
||||
.counterTracking(for: self)
|
||||
.showErrorsInToastInDebugMode()
|
||||
}
|
||||
|
||||
/// Perform reactive request to get UIImage and http response
|
||||
///
|
||||
/// - Parameter url: An object adopting `URLConvertible`
|
||||
/// - Returns: Observable of tuple containing (HTTPURLResponse, UIImage?)
|
||||
public func rxLoadImage(url: String) -> Observable<(HTTPURLResponse, UIImage?)> {
|
||||
let request = RxAlamofire.requestData(.get, url, headers: [:])
|
||||
|
||||
return request
|
||||
.observeOn(ConcurrentDispatchQueueScheduler(qos: .background))
|
||||
.map { (response, data) -> (HTTPURLResponse, UIImage?) in
|
||||
(response, UIImage(data: data))
|
||||
}
|
||||
.counterTracking(for: self)
|
||||
.showErrorsInToastInDebugMode()
|
||||
}
|
||||
|
||||
fileprivate func increaseRequestCounter() {
|
||||
requestCount.value += 1
|
||||
}
|
||||
|
||||
fileprivate func decreaseRequestCounter() {
|
||||
requestCount.value -= 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public extension Observable {
|
||||
|
||||
/// Increase and descrease NetworkService request counter on subscribe and dispose
|
||||
/// (used to show / hide activity indicator)
|
||||
///
|
||||
/// - Parameter networkService: NetworkService to operate on it
|
||||
/// - Returns: The source sequence with the side-effecting behavior applied.
|
||||
func counterTracking(for networkService: NetworkService) -> Observable<Observable.E> {
|
||||
return `do`(onSubscribe: {
|
||||
networkService.increaseRequestCounter()
|
||||
}, onDispose: {
|
||||
networkService.decreaseRequestCounter()
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -45,7 +45,9 @@ public extension Reactive where Base: Alamofire.SessionManager {
|
|||
/// - Parameter mappingQueue: The dispatch queue to use for mapping
|
||||
/// - Returns: Observable with HTTP URL Response and target object
|
||||
func responseModel<T: ImmutableMappable>(requestParameters: ApiRequestParameters,
|
||||
mappingQueue: DispatchQueue = DispatchQueue.global()) -> Observable<(HTTPURLResponse, T)> {
|
||||
mappingQueue: DispatchQueue = DispatchQueue.global())
|
||||
-> Observable<(response: HTTPURLResponse, model: T)> {
|
||||
|
||||
return apiRequest(requestParameters: requestParameters)
|
||||
.flatMap { $0.rx.apiResponse(mappingQueue: mappingQueue) }
|
||||
}
|
||||
|
|
@ -56,8 +58,8 @@ public extension Reactive where Base: Alamofire.SessionManager {
|
|||
/// - Parameter mappingQueue: The dispatch queue to use for mapping
|
||||
/// - Returns: Observable with HTTP URL Response and target object
|
||||
func responseObservableModel<T: ObservableMappable>(requestParameters: ApiRequestParameters,
|
||||
mappingQueue: DispatchQueue = DispatchQueue.global()) -> Observable<(HTTPURLResponse, T)>
|
||||
where T.ModelType == T {
|
||||
mappingQueue: DispatchQueue = DispatchQueue.global())
|
||||
-> Observable<(response: HTTPURLResponse, model: T)> where T.ModelType == T {
|
||||
|
||||
return apiRequest(requestParameters: requestParameters)
|
||||
.flatMap { $0.rx.apiResponse(mappingQueue: mappingQueue) }
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ public extension Reactive where Base: DataRequest {
|
|||
/// - Parameter mappingQueue: The dispatch queue to use for mapping
|
||||
/// - Returns: Observable with HTTP URL Response and target object
|
||||
func apiResponse<T: ImmutableMappable>(mappingQueue: DispatchQueue = DispatchQueue.global())
|
||||
-> Observable<(HTTPURLResponse, T)> {
|
||||
-> Observable<(response: HTTPURLResponse, model: T)> {
|
||||
|
||||
return responseJSONOnQueue(mappingQueue)
|
||||
.map { resp, value in
|
||||
|
|
@ -47,10 +47,10 @@ public extension Reactive where Base: DataRequest {
|
|||
/// - Parameter mappingQueue: The dispatch queue to use for mapping
|
||||
/// - Returns: Observable with HTTP URL Response and target object
|
||||
func apiResponse<T: ObservableMappable>(mappingQueue: DispatchQueue = DispatchQueue.global())
|
||||
-> Observable<(HTTPURLResponse, T)> where T.ModelType == T {
|
||||
-> Observable<(response: HTTPURLResponse, model: T)> where T.ModelType == T {
|
||||
|
||||
return responseJSONOnQueue(mappingQueue)
|
||||
.flatMap { resp, value -> Observable<(HTTPURLResponse, T)> in
|
||||
.flatMap { resp, value -> Observable<(response: HTTPURLResponse, model: T)> in
|
||||
let json = try cast(value) as [String: Any]
|
||||
|
||||
return T.createFrom(map: Map(mappingType: .fromJSON, JSON: json))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// Copyright (c) 2017 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import RxSwift
|
||||
import Toast_Swift
|
||||
|
||||
public extension Observable {
|
||||
|
||||
/// Method which shows toast with localized description of error in DEBUG mode
|
||||
///
|
||||
/// - Returns: The source sequence with the side-effecting behavior applied.
|
||||
func showErrorsInToastInDebugMode() -> Observable<Observable.E> {
|
||||
return `do`(onError: { (error) in
|
||||
#if DEBUG
|
||||
UIApplication.shared.keyWindow?.makeToast(error.localizedDescription)
|
||||
#endif
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue