Skip to content

Commit

Permalink
Merge pull request #7 from MarekKojder/fix/sending-multiple-requests
Browse files Browse the repository at this point in the history
Hot fix for sending multiple requests
  • Loading branch information
MarekKojder committed Oct 15, 2019
2 parents 990beaa + 0837ba4 commit e50ddf6
Show file tree
Hide file tree
Showing 21 changed files with 1,069 additions and 1,042 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
## Upcoming
- Refactor and migration to Rx of APILayer

## [v2.1.0](https://github.com/MarekKojder/RxSwiftAPI/tree/2.1.0) (2019-10-15)
- Refactored core layer.
- Unified naming convention.
- Simplified running tasks managing.
- It's still experimental version.

## [v2.0.6](https://github.com/MarekKojder/RxSwiftAPI/tree/2.0.6) (2019-10-03)
- Experimental solution for `SessionService` and `Configuration`.

Expand Down
2 changes: 1 addition & 1 deletion RxSwiftAPI.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Pod::Spec.new do |s|
#

s.name = "RxSwiftAPI"
s.version = "2.0.6"
s.version = "2.1.0"
s.summary = "Simple reactive networking library based on URLSession with REST API support."
s.description = "RxSwiftAPI is simple networking library based on SwiftAPI, but improved ad uses RxSwift to allow using it in reactive applications."
s.homepage = "https://github.com/MarekKojder/RxSwiftAPI"
Expand Down
74 changes: 42 additions & 32 deletions RxSwiftAPI.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

44 changes: 0 additions & 44 deletions Sources/APILayer/Request/ApiRequest.swift

This file was deleted.

84 changes: 84 additions & 0 deletions Sources/APILayer/Request/ApiServiceTask.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//
// ApiServiceTask.swift
// RxSwiftAPI
//
// Created by Marek Kojder on 11/10/2019.
//

import Foundation

public extension ApiService.Task {

/**
Task Status
- suspended: task is not running yer or is paused,
- running: task is during execution,
- finished: task has finished.
*/
enum Status {
case suspended
case running
case finished
}
}

public extension ApiService {

///Class which allows to manage request and follow its progress.
final class Task {
private let task: SessionService.Task

///Current Status of task
public var status: Status {
switch task.status {
case .suspended:
return .suspended
case .running:
return .running
case .finishing,
.finished:
return .finished
}
}

///A representation of the overall task progress.
public var progress: Progress {
return task.progress
}

internal init(_ task: SessionService.Task) {
self.task = task
}
}
}

public extension ApiService.Task {

///Resumes the task, if it is suspended.
func resume() {
task.resume()
}

///Temporarily suspends a task.
func suspend() {
task.suspend()
}

///Cancels the task.
func cancel() {
task.cancel()
}
}

extension ApiService.Task: Equatable {
public static func == (lhs: ApiService.Task, rhs: ApiService.Task) -> Bool {
return lhs.task == rhs.task
}
}

extension ApiService.Task: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(task)
}
}
183 changes: 46 additions & 137 deletions Sources/APILayer/Service/ApiService.swift

Large diffs are not rendered by default.

67 changes: 29 additions & 38 deletions Sources/HTTPLayer/Service/RequestService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,15 @@
//

import Foundation
import RxSwift

typealias HttpRequestCompletionHandler = SessionServiceCompletionHandler

final class RequestService: NSObject {

typealias CompletionHandler = SessionService.CompletionHandler

private let fileManager: FileManager

//MARK: - Handling multiple sessions
private var sessions = [SessionService]()
private let sessionsQueue = DispatchQueue(label: "RxSwiftAPI.RequestService.sessionsQueue", attributes: .concurrent)

///Returns URLSession for given configuration. If session does not exist, it creates one.
private func activeSession(for configuration: Configuration) -> SessionService {
Expand All @@ -40,7 +38,7 @@ final class RequestService: NSObject {
}

deinit {
sessions.forEach { $0.invalidateAndCancel() }
invalidateAndCancel()
}
}

Expand All @@ -53,12 +51,17 @@ extension RequestService {
- Parameters:
- request: An HttpDataRequest object provides request-specific information such as the URL, HTTP method or body data.
- configuration: RequestService.Configuration indicates request configuration.
- completion: Block for hangling request completion.
- Returns: Task object which allows to follow progress and manage request.
- Throws: Error when Task could not be created.
HttpDataRequest may run only with foreground configuration.
*/
func sendHTTPRequest(_ request: HttpDataRequest, with configuration: Configuration = .foreground, progress: SessionServiceProgressHandler?, completion: @escaping HttpRequestCompletionHandler) {
func sendHTTP(request: HttpDataRequest, with configuration: Configuration, completion: @escaping CompletionHandler) throws -> SessionService.Task {
let session = activeSession(for: configuration)
session.data(request: request.urlRequest, progress: progress, completion: completion)
return try session.data(request: request.urlRequest, completion: completion)
}

/**
Expand All @@ -67,10 +70,15 @@ extension RequestService {
- Parameters:
- request: An HttpUploadRequest object provides request-specific information such as the URL, HTTP method or URL of the file to upload.
- configuration: RequestService.Configuration indicates upload request configuration.
- completion: Block for hangling request completion.
- Returns: Task object which allows to follow progress and manage request.
- Throws: Error when Task could not be created.
*/
func sendHTTPRequest(_ request: HttpUploadRequest, with configuration: Configuration = .background, progress: SessionServiceProgressHandler?, completion: @escaping HttpRequestCompletionHandler) {
func sendHTTP(request: HttpUploadRequest, with configuration: Configuration, completion: @escaping CompletionHandler) throws -> SessionService.Task {
let session = activeSession(for: configuration)
session.upload(request: request.urlRequest, file: request.resourceUrl, progress: progress, completion: completion)
return try session.upload(request: request.urlRequest, file: request.resourceUrl, completion: completion)
}

/**
Expand All @@ -79,42 +87,25 @@ extension RequestService {
- Parameters:
- request: An HttpUploadRequest object provides request-specific information such as the URL, HTTP method or URL of the place on disc for downloading file.
- configuration: RequestService.Configuration indicates download request configuration.
*/
func sendHTTPRequest(_ request: HttpDownloadRequest, with configuration: Configuration = .background, progress: SessionServiceProgressHandler?, completion: @escaping HttpRequestCompletionHandler) {
let session = activeSession(for: configuration)
session.download(request: request.urlRequest, progress: progress, completion: completion)
}

/**
Temporarily suspends given HTTP request.
- Parameter request: An HttpRequest to suspend.
*/
func suspend(_ request: HttpRequest) {
sessions.forEach { $0.suspend(request.urlRequest) }
}
- completion: Block for hangling request completion.
/**
Resumes given HTTP request, if it is suspended.
- Returns: Task object which allows to follow progress and manage request.
- Parameter request: An HttpRequest to resume.
- Throws: Error when Task could not be created.
*/
@available(iOS 9.0, OSX 10.11, *)
func resume(_ request: HttpRequest) {
sessions.forEach { $0.resume(request.urlRequest) }
}

/**
Cancels given HTTP request.
- Parameter request: An HttpRequest to cancel.
*/
func cancel(_ request: HttpRequest) {
sessions.forEach { $0.cancel(request.urlRequest) }
func sendHTTP(request: HttpDownloadRequest, with configuration: Configuration, completion: @escaping CompletionHandler) throws -> SessionService.Task {
let session = activeSession(for: configuration)
return try session.download(request: request.urlRequest, completion: completion)
}

///Cancels all currently running HTTP requests.
func cancelAllRequests() {
sessions.forEach { $0.cancelAllRequests() }
}

///Cancels all currently running HTTP requests.
func invalidateAndCancel() {
sessions.forEach { $0.invalidateAndCancel() }
sessions.removeAll()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import RxCocoa
class RxURLSessionDelegateProxy: DelegateProxy<RxURLSession, RxURLSessionDelegate> {

private weak var urlSession: RxURLSession?
fileprivate let downloadSubject = PublishSubject<(session: URLSession, task: URLSessionDownloadTask, location: URL)>()
fileprivate let downloadSubject = PublishSubject<(task: URLSessionDownloadTask, location: URL)>()

init(urlSession: ParentObject) {
self.urlSession = urlSession
Expand All @@ -32,7 +32,7 @@ extension RxURLSessionDelegateProxy: DelegateProxyType {}
extension RxURLSessionDelegateProxy: RxURLSessionDelegate {

public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
downloadSubject.on(.next((session, downloadTask, location)))
downloadSubject.on(.next((downloadTask, location)))
_forwardToDelegate?.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location)
}
}
Expand All @@ -48,61 +48,55 @@ extension Reactive where Base: RxURLSession {
}

//MARK: URLSessionDelegate
public var didBecomeInvalidWithError: Observable<(session: URLSession, error: Error?)> {
public var didBecomeInvalidWithError: Observable<Error?> {
return delegate.methodInvoked(#selector(RxURLSessionDelegate.urlSession(_:didBecomeInvalidWithError:))).map { parameters in
return (parameters[0] as! URLSession,
parameters[1] as? Error)
return (parameters[1] as? Error)
}
}

//MARK: URLSessionTaskDelegate
public var didSendBodyData: Observable<(session: URLSession, task: URLSessionTask, bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64)> {
public var didSendBodyData: Observable<(task: URLSessionTask, bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64)> {
return delegate.methodInvoked(#selector(RxURLSessionDelegate.urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:))).map { parameters in
return (parameters[0] as! URLSession,
parameters[1] as! URLSessionTask,
return (parameters[1] as! URLSessionTask,
parameters[2] as! Int64,
parameters[3] as! Int64,
parameters[4] as! Int64)
}
}

public var didCompleteWithError: Observable<(session: URLSession, task: URLSessionTask, error: Error?)> {
public var didCompleteWithError: Observable<(task: URLSessionTask, error: Error?)> {
return delegate.methodInvoked(#selector(RxURLSessionDelegate.urlSession(_:task:didCompleteWithError:))).map { parameters in
return (parameters[0] as! URLSession,
parameters[1] as! URLSessionTask,
return (parameters[1] as! URLSessionTask,
parameters[2] as? Error)
}
}

//MARK: URLSessionDataDelegate
private typealias DataTaskResponseHandler = @convention(block) (URLSession.ResponseDisposition) -> Void
public var didReceiveResponse: Observable<(session: URLSession, task: URLSessionDataTask, response: URLResponse, completion: (URLSession.ResponseDisposition) -> Void)> {

public var didReceiveResponse: Observable<(task: URLSessionDataTask, response: URLResponse, completion: (URLSession.ResponseDisposition) -> Void)> {
return delegate.methodInvoked(#selector(RxURLSessionDelegate.urlSession(_:dataTask:didReceive:completionHandler:))).map { parameters in
return (parameters[0] as! URLSession,
parameters[1] as! URLSessionDataTask,
return (parameters[1] as! URLSessionDataTask,
parameters[2] as! URLResponse,
unsafeBitCast(parameters[3] as AnyObject, to: DataTaskResponseHandler.self))
}
}

public var didReceiveData: Observable<(session: URLSession, task: URLSessionDataTask, response: Data)> {
public var didReceiveData: Observable<(task: URLSessionDataTask, response: Data)> {
return delegate.methodInvoked(#selector(RxURLSessionDelegate.urlSession(_:dataTask:didReceive:))).map { parameters in
return (parameters[0] as! URLSession,
parameters[1] as! URLSessionDataTask,
return (parameters[1] as! URLSessionDataTask,
parameters[2] as! Data)
}
}

//MARK: URLSessionDownloadDelegate
public var didFinishDownloading: Observable<(session: URLSession, task: URLSessionDownloadTask, location: URL)> {
public var didFinishDownloading: Observable<(task: URLSessionDownloadTask, location: URL)> {
return delegateProxy.downloadSubject
}

public var didWriteData: Observable<(session: URLSession, task: URLSessionDownloadTask, bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64)> {
public var didWriteData: Observable<(task: URLSessionDownloadTask, bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64)> {
return delegate.methodInvoked(#selector(RxURLSessionDelegate.urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:))).map { parameters in
return (parameters[0] as! URLSession,
parameters[1] as! URLSessionDownloadTask,
return (parameters[1] as! URLSessionDownloadTask,
parameters[2] as! Int64,
parameters[3] as! Int64,
parameters[4] as! Int64)
Expand Down

0 comments on commit e50ddf6

Please sign in to comment.