Skip to content

Commit

Permalink
Removed serialAll option from concurrency settings (#63)
Browse files Browse the repository at this point in the history
  • Loading branch information
kober32 authored Jul 8, 2024
1 parent 1da222b commit fa81ba4
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 56 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ pod 'WultraPowerAuthNetworking'
|---|---|
| `1.0.x` - `1.2.x` | `1.7.x` |
| `1.3.x` | `1.8.x` |
| `1.4.x` | `1.8.x` |

### Xcode Compatibility

Expand Down Expand Up @@ -259,11 +260,10 @@ class MyResponseDelegateLogger: WPNResponseDelegate {

By default, the SDK is serializing all signed requests. This means that the requests signed with the PowerAuthSDK are put into the queue and executed one by one (meaning that the HTTP request is not made until the previous one is finished). Other requests will be parallel.

This behavior can be changed via `WPNNetworkingService.concurencyStrategy` with the following possible values:
This behavior can be changed via `WPNNetworkingService.concurrencyStrategy` with the following possible values:

- `serialSigned` - Default behavior. Only requests that need a PowerAuth signature will be put into the serial queue.
- `serialAll` - All requests will be put into a serial queue.
- `concurentAll` - All requests will be put into the concurrent queue. This behavior is not recommended unless you know exactly why you want this.
- `serialSigned` - Default behavior. Only requests that need a PowerAuth signature will be put into the serial queue that is shared with the `PowerAuthSDK` instance to ensure all signed requests are in proper order.
- `concurrentAll` - All requests will be put into the concurrent queue. This behavior is not recommended unless you know exactly why you want this.

<!-- begin box info -->
More about this topic can be found in the [PowerAuth documentation](https://developers.wultra.com/components/powerauth-mobile-sdk/develop/documentation/PowerAuth-SDK-for-iOS#request-synchronization).
Expand Down
4 changes: 4 additions & 0 deletions Sources/WultraPowerauthNetworking/WPNError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,12 @@ public class WPNError: Error {
/// Reason of the error.
public struct WPNErrorReason: RawRepresentable, Equatable, Hashable {

/// PowerAuth instance does not have a valid activation
public static let missingActivation = WPNErrorReason(rawValue: "missingActivation")
/// Unknown (unrecognized) error occured.
public static let unknown = WPNErrorReason(rawValue: "unknown")
/// The operation was canceled.
public static let canceled = WPNErrorReason(rawValue: "canceled")

public typealias RawValue = String
public var rawValue: RawValue
Expand Down
154 changes: 102 additions & 52 deletions Sources/WultraPowerauthNetworking/WPNNetworkingService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,25 @@ import Foundation
import PowerAuth2
import PowerAuthCore

/// Strategy that decides if request will be put in serial or concurent queue.
/// Strategy that decides if request will be put in serial or concurrent queue.
///
/// For serial queue, `PowerAuthSDK.executeOperation` is used.
///
/// More about this topic can be found in the
/// [PowerAuth documentation](https://developers.wultra.com/components/powerauth-mobile-sdk/develop/documentation/PowerAuth-SDK-for-iOS#request-synchronization)
public enum WPNRequestConcurencyStrategy {
/// All requests will be put into concurent queue.
public enum WPNRequestConcurrencyStrategy {
/// All requests will be put into concurrent queue.
///
/// We recommend not using this option unless you're managing theserialization of requests yourself.
///
/// More about this topic can be found in the
/// [PowerAuth documentation](https://developers.wultra.com/components/powerauth-mobile-sdk/develop/documentation/PowerAuth-SDK-for-iOS#request-synchronization)
case concurentAll
/// Only request that needs PowerAuth signature will be put into serial queue.
case concurrentAll
/// Only request that needs PowerAuth signature will be put into serial queue provided by the PowerAuth library.
///
/// Note that serial queue for signed requests is shared with the `PowerAuthSDK` instance
/// to ensure all signed requests are in proper order.
case serialSigned
/// All requests will be put into serial queue.
case serialAll
}

/// Networking service for dispatching PowerAuth signed requests.
Expand All @@ -50,10 +53,10 @@ public class WPNNetworkingService {
/// Response delegate is called on each received response
public weak var responseDelegate: WPNResponseDelegate?

/// Strategy that decides if request will be put in serial or concurent queue.
/// Strategy that decides if request will be put in serial or concurrent queue.
///
/// Default value is `serialSigned`
public var concurencyStrategy = WPNRequestConcurencyStrategy.serialSigned
public var concurrencyStrategy = WPNRequestConcurrencyStrategy.serialSigned

/// PowerAuth instance that will be used for this networking.
public let powerAuth: PowerAuthSDK
Expand All @@ -68,7 +71,12 @@ public class WPNNetworkingService {
/// - serviceName: Name of the service. Will be reflected in the OperationQueue name and logs.
/// - acceptLanguage: Language sent to the server for request localized response.
/// Compliant with standard RFC Accept-Language. Default value is "en".
public init(powerAuth: PowerAuthSDK, config: WPNConfig, serviceName: String, acceptLanguage: String = "en") {
public init(
powerAuth: PowerAuthSDK,
config: WPNConfig,
serviceName: String,
acceptLanguage: String = "en"
) {
self.acceptLanguage = acceptLanguage
self.powerAuth = powerAuth
self.httpClient = WPNHttpClient(sslValidation: config.sslValidation, timeout: config.timeoutIntervalForRequest)
Expand All @@ -88,20 +96,29 @@ public class WPNNetworkingService {
/// - completionQueue: Queue on wich the completion will be executed.
/// Default value is .main
/// - completion: Completion handler. This callback is executed on the queue defined in `completionQueue` parameter.
/// - Returns: Operation for observation or operation chaining.
/// - Returns: Operation for observation or operation chaining. This operation is placed into an internal queue - do not execute it on your own.
@discardableResult
public func post<Req: WPNRequestBase, Resp: WPNResponseBase, Endpoint: WPNEndpointBasic<Req, Resp>>(data: Req,
to endpoint: Endpoint,
with headers: [String: String]? = nil,
encryptedWith encryptor: PowerAuthCoreEciesEncryptor? = nil,
timeoutInterval: TimeInterval? = nil,
progressCallback: ((Double) -> Void)? = nil,
completionQueue: DispatchQueue = .main,
completion: @escaping Endpoint.Completion) -> Operation {
public func post<Req: WPNRequestBase, Resp: WPNResponseBase, Endpoint: WPNEndpointBasic<Req, Resp>>(
data: Req,
to endpoint: Endpoint,
with headers: [String: String]? = nil,
encryptedWith encryptor: PowerAuthCoreEciesEncryptor? = nil,
timeoutInterval: TimeInterval? = nil,
progressCallback: ((Double) -> Void)? = nil,
completionQueue: DispatchQueue = .main,
completion: @escaping Endpoint.Completion
) -> Operation {

let url = config.buildURL(endpoint.endpointURLPath)
let request = Endpoint.Request(url, requestData: data, encryptor: encryptor)
request.timeoutInterval = timeoutInterval
return post(request: request, headers: headers, progressCallback: progressCallback, completionQueue: completionQueue, completion: completion)
return post(
request: request,
headers: headers,
progressCallback: progressCallback,
completionQueue: completionQueue,
completion: completion
)
}

/// Sends signed request with provided authentication.
Expand All @@ -117,21 +134,30 @@ public class WPNNetworkingService {
/// - completionQueue: Queue on wich the completion will be executed.
/// Default value is .main
/// - completion: Completion handler. This callback is executed on the queue defined in `completionQueue` parameter.
/// - Returns: Operation for observation or operation chaining.
/// - Returns: Operation for observation or operation chaining. This operation is placed into an internal queue - do not execute it on your own.
@discardableResult
public func post<Req: WPNRequestBase, Resp: WPNResponseBase, Endpoint: WPNEndpointSigned<Req, Resp>>(data: Req,
signedWith auth: PowerAuthAuthentication,
to endpoint: Endpoint,
with headers: [String: String]? = nil,
encryptedWith encryptor: PowerAuthCoreEciesEncryptor? = nil,
timeoutInterval: TimeInterval? = nil,
progressCallback: ((Double) -> Void)? = nil,
completionQueue: DispatchQueue = .main,
completion: @escaping Endpoint.Completion) -> Operation {
public func post<Req: WPNRequestBase, Resp: WPNResponseBase, Endpoint: WPNEndpointSigned<Req, Resp>>(
data: Req,
signedWith auth: PowerAuthAuthentication,
to endpoint: Endpoint,
with headers: [String: String]? = nil,
encryptedWith encryptor: PowerAuthCoreEciesEncryptor? = nil,
timeoutInterval: TimeInterval? = nil,
progressCallback: ((Double) -> Void)? = nil,
completionQueue: DispatchQueue = .main,
completion: @escaping Endpoint.Completion
) -> Operation {

let url = config.buildURL(endpoint.endpointURLPath)
let request = Endpoint.Request(url, uriId: endpoint.uriId, auth: auth, requestData: data, encryptor: encryptor)
request.timeoutInterval = timeoutInterval
return post(request: request, headers: headers, progressCallback: progressCallback, completionQueue: completionQueue, completion: completion)
return post(
request: request,
headers: headers,
progressCallback: progressCallback,
completionQueue: completionQueue,
completion: completion
)
}

/// Sends signed request with provided authentication.
Expand All @@ -147,30 +173,42 @@ public class WPNNetworkingService {
/// - completionQueue: Queue on wich the completion will be executed.
/// Default value is .main
/// - completion: Completion handler. This callback is executed on the queue defined in `completionQueue` parameter.
/// - Returns: Operation for observation or operation chaining.
/// - Returns: Operation for observation or operation chaining. This operation is placed into an internal queue - do not execute it on your own.
@discardableResult
public func post<Req: WPNRequestBase, Resp: WPNResponseBase, Endpoint: WPNEndpointSignedWithToken<Req, Resp>>(data: Req,
signedWith auth: PowerAuthAuthentication,
to endpoint: Endpoint,
with headers: [String: String]? = nil,
encryptedWith encryptor: PowerAuthCoreEciesEncryptor? = nil,
timeoutInterval: TimeInterval? = nil,
progressCallback: ((Double) -> Void)? = nil,
completionQueue: DispatchQueue = .main,
completion: @escaping Endpoint.Completion) -> Operation {
public func post<Req: WPNRequestBase, Resp: WPNResponseBase, Endpoint: WPNEndpointSignedWithToken<Req, Resp>>(
data: Req,
signedWith auth: PowerAuthAuthentication,
to endpoint: Endpoint,
with headers: [String: String]? = nil,
encryptedWith encryptor: PowerAuthCoreEciesEncryptor? = nil,
timeoutInterval: TimeInterval? = nil,
progressCallback: ((Double) -> Void)? = nil,
completionQueue: DispatchQueue = .main,
completion: @escaping Endpoint.Completion
) -> Operation {

let url = config.buildURL(endpoint.endpointURLPath)
let request = Endpoint.Request(url, tokenName: endpoint.tokenName, auth: auth, requestData: data, encryptor: encryptor)
request.timeoutInterval = timeoutInterval
return post(request: request, headers: headers, progressCallback: progressCallback, completionQueue: completionQueue, completion: completion)
return post(
request: request,
headers: headers,
progressCallback: progressCallback,
completionQueue: completionQueue,
completion: completion
)
}

/// Adds a HTTP post request to the request queue.
@discardableResult
func post<Req: WPNRequestBase, Resp: WPNResponseBase, Endpoint: WPNEndpoint<Req, Resp>>(request: Endpoint.Request,
headers: [String: String]?,
progressCallback: ((Double) -> Void)?,
completionQueue: DispatchQueue,
completion: @escaping Endpoint.Completion) -> Operation {
func post<Req: WPNRequestBase, Resp: WPNResponseBase, Endpoint: WPNEndpoint<Req, Resp>>(
request: Endpoint.Request,
headers: [String: String]?,
progressCallback: ((Double) -> Void)?,
completionQueue: DispatchQueue,
completion: @escaping Endpoint.Completion
) -> Operation {

// Setup default headers
request.addHeaders(getDefaultHeaders())

Expand All @@ -187,16 +225,22 @@ public class WPNNetworkingService {
}
}

self.processRequest(request) { error in
self.processRequest(request) { [weak self] error in

if let error {
completion(nil, error)
return
}

guard let self, operation.isCancelled == false else {
completion(nil, .init(reason: .canceled))
return
}

self.httpClient.post(request: request, progressCallback: progressCallback, completion: { [weak self] data, urlResponse, error in

guard let self = self, operation.isCancelled == false else {
guard let self, operation.isCancelled == false else {
completion(nil, .init(reason: .canceled))
return
}

Expand Down Expand Up @@ -260,7 +304,7 @@ public class WPNNetworkingService {

op.completionQueue = completionQueue

if (concurencyStrategy == .serialSigned && request.needsSignature) || concurencyStrategy == .serialAll {
if concurrencyStrategy == .serialSigned && request.needsSignature {
// Add operation to the "signing" queue.
if !powerAuth.executeOperation(onSerialQueue: op) {
// Operation wont be added to the queue if there is a missing
Expand Down Expand Up @@ -289,7 +333,10 @@ public class WPNNetworkingService {
}

/// All necessary interactions with the PowerAuthSDK like request signing, token refresh and time synchronization.
private func processRequest<Req: WPNRequestBase, Resp: WPNResponseBase>(_ request: WPNHttpRequest<Req, Resp>, completion: @escaping (WPNError?) -> Void) {
private func processRequest<Req: WPNRequestBase, Resp: WPNResponseBase>(
_ request: WPNHttpRequest<Req, Resp>,
completion: @escaping (WPNError?) -> Void
) {
// global completion queue to ensure that in case of async call to the server
// we calculate the signature on the background queue
synchronizeTime(completionQueue: .global()) { error in
Expand Down Expand Up @@ -322,7 +369,10 @@ public class WPNNetworkingService {
}

/// Calculates a signature for request. The function must be called on background thread.
private func bgCalculateSignature<Req: WPNRequestBase, Resp: WPNResponseBase>(_ request: WPNHttpRequest<Req, Resp>, completion: @escaping (WPNError?) -> Void) {
private func bgCalculateSignature<Req: WPNRequestBase, Resp: WPNResponseBase>(
_ request: WPNHttpRequest<Req, Resp>,
completion: @escaping (WPNError?) -> Void
) {
do {
guard let data = request.requestData else {
completion(WPNError(reason: .network_invalidRequestObject))
Expand Down

0 comments on commit fa81ba4

Please sign in to comment.