From a994d2fe220da39bf230c09668db1a574b367dca Mon Sep 17 00:00:00 2001 From: jguz-pubnub Date: Tue, 12 Dec 2023 10:34:02 +0100 Subject: [PATCH] feat(auto-retry): added possibility to retry more requests --- .../Sources/Membership+PubNub.swift | 7 +- PubNubSpace/Sources/Space+PubNub.swift | 5 +- PubNubUser/Sources/User+PubNub.swift | 6 +- Sources/PubNub/APIs/File+PubNub.swift | 12 +- .../Networking/Replaceables+PubNub.swift | 4 +- .../Request/Operators/AutomaticRetry.swift | 143 ++++++++++- Sources/PubNub/PubNub.swift | 230 ++++++++++++------ .../SubscriptionSession+Presence.swift | 2 +- .../Subscription/SubscriptionSession.swift | 8 +- 9 files changed, 319 insertions(+), 98 deletions(-) diff --git a/PubNubMembership/Sources/Membership+PubNub.swift b/PubNubMembership/Sources/Membership+PubNub.swift index 2c7f79f0..3f08a419 100644 --- a/PubNubMembership/Sources/Membership+PubNub.swift +++ b/PubNubMembership/Sources/Membership+PubNub.swift @@ -18,7 +18,6 @@ import PubNubUser public protocol PubNubMembershipInterface { /// A copy of the configuration object used for this session var configuration: PubNubConfiguration { get } - /// Session used for performing request/response REST calls var networkSession: SessionReplaceable { get } @@ -268,6 +267,7 @@ public extension PubNubMembershipInterface { (requestConfig.customSession ?? networkSession) .route( router, + requestOperator: configuration.automaticRetry?[.fetchMemberships], responseDecoder: FetchMultipleValueResponseDecoder(), responseQueue: requestConfig.responseQueue ) { result in @@ -320,6 +320,7 @@ public extension PubNubMembershipInterface { (requestConfig.customSession ?? networkSession) .route( router, + requestOperator: configuration.automaticRetry?[.fetchMemberships], responseDecoder: FetchMultipleValueResponseDecoder(), responseQueue: requestConfig.responseQueue ) { result in @@ -365,6 +366,7 @@ public extension PubNubMembershipInterface { (requestConfig.customSession ?? networkSession) .route( router, + requestOperator: configuration.automaticRetry?[.addMemberships], responseDecoder: FetchStatusResponseDecoder(), responseQueue: requestConfig.responseQueue ) { result in @@ -401,6 +403,7 @@ public extension PubNubMembershipInterface { (requestConfig.customSession ?? networkSession) .route( router, + requestOperator: configuration.automaticRetry?[.addMemberships], responseDecoder: FetchStatusResponseDecoder(), responseQueue: requestConfig.responseQueue ) { result in @@ -463,6 +466,7 @@ public extension PubNubMembershipInterface { (requestConfig.customSession ?? networkSession) .route( router, + requestOperator: configuration.automaticRetry?[.removeMemberships], responseDecoder: FetchStatusResponseDecoder(), responseQueue: requestConfig.responseQueue ) { result in @@ -499,6 +503,7 @@ public extension PubNubMembershipInterface { (requestConfig.customSession ?? networkSession) .route( router, + requestOperator: configuration.automaticRetry?[.removeMemberships], responseDecoder: FetchStatusResponseDecoder(), responseQueue: requestConfig.responseQueue ) { result in diff --git a/PubNubSpace/Sources/Space+PubNub.swift b/PubNubSpace/Sources/Space+PubNub.swift index 8954f8ae..23e28020 100644 --- a/PubNubSpace/Sources/Space+PubNub.swift +++ b/PubNubSpace/Sources/Space+PubNub.swift @@ -16,7 +16,6 @@ import PubNub public protocol PubNubSpaceInterface { /// A copy of the configuration object used for this session var configuration: PubNubConfiguration { get } - /// Session used for performing request/response REST calls var networkSession: SessionReplaceable { get } @@ -213,6 +212,7 @@ public extension PubNubSpaceInterface { (requestConfig.customSession ?? networkSession) .route( router, + requestOperator: configuration.automaticRetry?[.fetchSpaces], responseDecoder: FetchMultipleValueResponseDecoder(), responseQueue: requestConfig.responseQueue ) { result in @@ -237,6 +237,7 @@ public extension PubNubSpaceInterface { (requestConfig.customSession ?? networkSession) .route( router, + requestOperator: configuration.automaticRetry?[.fetchSpaces], responseDecoder: FetchSingleValueResponseDecoder(), responseQueue: requestConfig.responseQueue ) { result in @@ -273,6 +274,7 @@ public extension PubNubSpaceInterface { (requestConfig.customSession ?? networkSession) .route( router, + requestOperator: configuration.automaticRetry?[.createSpace], responseDecoder: FetchSingleValueResponseDecoder(), responseQueue: requestConfig.responseQueue ) { result in @@ -317,6 +319,7 @@ public extension PubNubSpaceInterface { (requestConfig.customSession ?? networkSession) .route( router, + requestOperator: configuration.automaticRetry?[.removeSpace], responseDecoder: FetchStatusResponseDecoder(), responseQueue: requestConfig.responseQueue ) { result in diff --git a/PubNubUser/Sources/User+PubNub.swift b/PubNubUser/Sources/User+PubNub.swift index 2ebf20a4..6724cd1f 100644 --- a/PubNubUser/Sources/User+PubNub.swift +++ b/PubNubUser/Sources/User+PubNub.swift @@ -9,14 +9,12 @@ // import Foundation - import PubNub /// Protocol interface to manage `PubNubUser` entities using closures public protocol PubNubUserInterface { /// A copy of the configuration object used for this session var configuration: PubNubConfiguration { get } - /// Session used for performing request/response REST calls var networkSession: SessionReplaceable { get } @@ -221,6 +219,7 @@ public extension PubNubUserInterface { (requestConfig.customSession ?? networkSession)? .route( router, + requestOperator: configuration.automaticRetry?[.fetchUsers], responseDecoder: FetchMultipleValueResponseDecoder(), responseQueue: requestConfig.responseQueue ) { result in @@ -248,6 +247,7 @@ public extension PubNubUserInterface { (requestConfig.customSession ?? networkSession) .route( router, + requestOperator: configuration.automaticRetry?[.fetchUsers], responseDecoder: FetchSingleValueResponseDecoder(), responseQueue: requestConfig.responseQueue ) { @@ -288,6 +288,7 @@ public extension PubNubUserInterface { (requestConfig.customSession ?? networkSession) .route( router, + requestOperator: configuration.automaticRetry?[.createUser], responseDecoder: FetchSingleValueResponseDecoder(), responseQueue: requestConfig.responseQueue ) { result in @@ -336,6 +337,7 @@ public extension PubNubUserInterface { (requestConfig.customSession ?? networkSession) .route( router, + requestOperator: configuration.automaticRetry?[.removeUser], responseDecoder: FetchStatusResponseDecoder(), responseQueue: requestConfig.responseQueue ) { result in diff --git a/Sources/PubNub/APIs/File+PubNub.swift b/Sources/PubNub/APIs/File+PubNub.swift index f99b1d14..329d44bd 100644 --- a/Sources/PubNub/APIs/File+PubNub.swift +++ b/Sources/PubNub/APIs/File+PubNub.swift @@ -29,6 +29,7 @@ public extension PubNub { ) { route( FileManagementRouter(.list(channel: channel, limit: limit, next: next), configuration: configuration), + requestOperator: configuration.automaticRetry?[.listFiles], responseDecoder: FileListResponseDecoder(), custom: requestConfig ) { result in @@ -60,6 +61,7 @@ public extension PubNub { ) { route( FileManagementRouter(.delete(channel: channel, fileId: fileId, filename: filename), configuration: configuration), + requestOperator: configuration.automaticRetry?[.removeFile], responseDecoder: FileGeneralSuccessResponseDecoder(), custom: requestConfig ) { result in @@ -137,6 +139,7 @@ public extension PubNub { .generateURL(channel: channel, body: .init(name: remoteFilename)), configuration: configuration ), + requestOperator: configuration.automaticRetry?[.generateFileUploadURL], responseDecoder: FileGenerateResponseDecoder(), custom: requestConfig ) { [configuration] result in @@ -225,9 +228,12 @@ public extension PubNub { configuration: configuration ) - route(router, - responseDecoder: PublishResponseDecoder(), - custom: request.customRequestConfig) { result in + route( + router, + requestOperator: configuration.automaticRetry?[.publishFile], + responseDecoder: PublishResponseDecoder(), + custom: request.customRequestConfig + ) { result in completion?(result.map { $0.payload.timetoken }) } } diff --git a/Sources/PubNub/Networking/Replaceables+PubNub.swift b/Sources/PubNub/Networking/Replaceables+PubNub.swift index ae597ec8..f86ed392 100644 --- a/Sources/PubNub/Networking/Replaceables+PubNub.swift +++ b/Sources/PubNub/Networking/Replaceables+PubNub.swift @@ -126,6 +126,7 @@ public protocol SessionReplaceable { func route( _ router: HTTPRouter, + requestOperator: RequestOperator?, responseDecoder: Decoder, responseQueue: DispatchQueue, completion: @escaping (Result, Error>) -> Void @@ -135,11 +136,12 @@ public protocol SessionReplaceable { public extension SessionReplaceable { func route( _ router: HTTPRouter, + requestOperator: RequestOperator? = nil, responseDecoder: Decoder, responseQueue: DispatchQueue = .main, completion: @escaping (Result, Error>) -> Void ) where Decoder: ResponseDecoder { - request(with: router, requestOperator: nil) + request(with: router, requestOperator: requestOperator) .validate() .response( on: responseQueue, diff --git a/Sources/PubNub/Networking/Request/Operators/AutomaticRetry.swift b/Sources/PubNub/Networking/Request/Operators/AutomaticRetry.swift index 0c433b87..50096e7a 100644 --- a/Sources/PubNub/Networking/Request/Operators/AutomaticRetry.swift +++ b/Sources/PubNub/Networking/Request/Operators/AutomaticRetry.swift @@ -17,17 +17,20 @@ public struct AutomaticRetry: RequestOperator, Hashable { /// No retry will be performed public static var none = AutomaticRetry(retryLimit: 1) /// Retry immediately twice on lost network connection - public static var connectionLost = AutomaticRetry(policy: .immediately, - retryableURLErrorCodes: [.networkConnectionLost]) + public static var connectionLost = AutomaticRetry( + policy: .immediately, + retryableURLErrorCodes: [.networkConnectionLost] + ) /// Exponential backoff twice when no internet connection is detected - public static var noInternet = AutomaticRetry(policy: .defaultExponential, - retryableURLErrorCodes: [.notConnectedToInternet]) + public static var noInternet = AutomaticRetry( + policy: .defaultExponential, + retryableURLErrorCodes: [.notConnectedToInternet] + ) /// Provides the action taken when a retry is to be performed public enum ReconnectionPolicy: Hashable { /// Exponential backoff with base/scale factor of 2, and a 300s max delay public static let defaultExponential: ReconnectionPolicy = .exponential(base: 2, scale: 2, maxDelay: 300) - /// Linear reconnect every 3 seconds public static let defaultLinear: ReconnectionPolicy = .linear(delay: 3) @@ -80,12 +83,52 @@ public struct AutomaticRetry: RequestOperator, Hashable { public let retryableHTTPStatusCodes: Set /// Collection of returned `URLError.Code` objects that will trigger a retry public let retryableURLErrorCodes: Set + /// The list of endpoints excluded from retrying + public let excluded: [AutomaticRetry.Endpoint] public init( retryLimit: UInt = 2, policy: ReconnectionPolicy = .defaultExponential, retryableHTTPStatusCodes: Set = [500], - retryableURLErrorCodes: Set = AutomaticRetry.defaultRetryableURLErrorCodes + retryableURLErrorCodes: Set = AutomaticRetry.defaultRetryableURLErrorCodes, + excluded endpoints: [AutomaticRetry.Endpoint] = [ + .addChannelsToGroup, + .removeChannelsFromGroup, + .listChannelsForGroup, + .listChannelGroups, + .removeChannelGroup, + .publish, + .fire, + .signal, + .time, + .whereNow, + .hereNow, + .setPresence, + .getPresence, + .fetchMessageActions, + .addMessageAction, + .removeMessageAction, + .fetchMessageHistory, + .deleteMessageHistory, + .messageCounts, + .fetchMemberships, + .addMemberships, + .removeMemberships, + .fetchUsers, + .createUser, + .removeUser, + .fetchSpaces, + .createSpace, + .removeSpace, + .listPushChannels, + .managePushChannels, + .listAPNSPushChannels, + .manageAPNSDevices, + .listFiles, + .generateFileUploadURL, + .publishFile, + .removeFile + ] ) { switch policy { case let .exponential(base, scale, max): @@ -117,6 +160,7 @@ public struct AutomaticRetry: RequestOperator, Hashable { self.retryLimit = retryLimit self.retryableHTTPStatusCodes = retryableHTTPStatusCodes self.retryableURLErrorCodes = retryableURLErrorCodes + self.excluded = endpoints } public func retry( @@ -138,11 +182,92 @@ public struct AutomaticRetry: RequestOperator, Hashable { return true } else if let errorCode = error.urlError?.code, retryableURLErrorCodes.contains(errorCode) { return true - } else if let errorCode = error.pubNubError?.underlying?.urlError?.code, - retryableURLErrorCodes.contains(errorCode) { + } else if let errorCode = error.pubNubError?.underlying?.urlError?.code, retryableURLErrorCodes.contains(errorCode) { return true } - return false } + + public subscript(endpoint: AutomaticRetry.Endpoint) -> RequestOperator? { + excluded.contains(endpoint) ? nil : self + } + + public enum Endpoint { + /// Adding a channel to the channel group + case addChannelsToGroup + /// Removing a channel from the channel group + case removeChannelsFromGroup + /// Listing all the channels of the channel group + case listChannelsForGroup + /// Listing all the channel groups + case listChannelGroups + /// Removing the channel group + case removeChannelGroup + /// Publishing a message to the channel + case publish + /// Publishing a message to PubNub Functions Event Handlers + case fire + /// Publish a message to PubNub Functions Event Handlers + case signal + /// Getting current `Timetoken` from System + case time + /// Subscribing to channels and/or channel groups + case subscribe + /// Informing Presence that a user is still active + case heartbeat + /// Obtaining information about the current list of channels a UUID is subscribed to + case whereNow + /// Obtaining information about the current state of a channel + case hereNow + /// Setting state dictionary pairs specific to a subscriber UUID + case setPresence + /// Getting state dictionary pairs from a specific subscriber uuid + case getPresence + /// Fetching a list of Message Actions for a channel + case fetchMessageActions + /// Add an Action to a Message + case addMessageAction + /// Removes a Message Action from a published Message + case removeMessageAction + /// Fetching historical messages of a channel + case fetchMessageHistory + /// Removing the messages from the history of a specific channel + case deleteMessageHistory + /// Returning the number of messages published for one or more channels + case messageCounts + /// Fetching all `PubNubMembership` linked to a specific `PubNubUser.id` + case fetchMemberships + /// Adding a `PubNubMembership` relationship between a `PubNubSpace` and one or more `PubNubUser` + case addMemberships + /// Removing the `PubNubMembership` relationship + case removeMemberships + /// Fetching one or all `PubNubUser` that exist on a keyset + case fetchUsers + /// Creating a new `PubNubUser` + case createUser + /// Removing a previously created `PubNubUser` (if it existed) + case removeUser + /// Fetching one or all `PubNubSpace` that exist on a keyset + case fetchSpaces + /// Creating a new `PubNubSpace` + case createSpace + /// Updating an existing`PubNubSpace` + case removeSpace + /// Getting channels on which push notification has been enabled using specified push token + case listPushChannels + /// Getting channels on which APNS push notification has been enabled using specified device token and topic + case listAPNSPushChannels + /// Adding/removing push notification functionality on provided set of channels + case managePushChannels + /// Adding/removing APNS push notification functionality on provided set of channels for a given topic + case manageAPNSDevices + /// Retrieve list of files uploaded to a channel + case listFiles + /// Generating a File Upload URL + case generateFileUploadURL + /// Publishing the `PubNubFile` representing the uploaded File + case publishFile + /// Removing file from specified `Channel` + case removeFile + } } diff --git a/Sources/PubNub/PubNub.swift b/Sources/PubNub/PubNub.swift index 9f05505e..9c8d3cf7 100644 --- a/Sources/PubNub/PubNub.swift +++ b/Sources/PubNub/PubNub.swift @@ -94,6 +94,7 @@ public class PubNub { func route( _ router: HTTPRouter, + requestOperator: RequestOperator? = nil, responseDecoder: Decoder, custom requestConfig: RequestConfiguration, completion: @escaping (Result, Error>) -> Void @@ -101,6 +102,7 @@ public class PubNub { (requestConfig.customSession ?? networkSession) .route( router, + requestOperator: requestOperator, responseDecoder: responseDecoder, responseQueue: requestConfig.responseQueue, completion: completion @@ -199,9 +201,12 @@ public extension PubNub { custom requestConfig: RequestConfiguration = RequestConfiguration(), completion: ((Result) -> Void)? ) { - route(TimeRouter(.time, configuration: requestConfig.customConfiguration ?? configuration), - responseDecoder: TimeResponseDecoder(), - custom: requestConfig) { result in + route( + TimeRouter(.time, configuration: requestConfig.customConfiguration ?? configuration), + requestOperator: configuration.automaticRetry?[.time], + responseDecoder: TimeResponseDecoder(), + custom: requestConfig + ) { result in completion?(result.map { $0.payload.timetoken }) } } @@ -242,27 +247,34 @@ public extension PubNub { let router: PublishRouter if shouldCompress { router = PublishRouter( - .compressedPublish(message: message.codableValue, - channel: channel, - shouldStore: shouldStore, - ttl: storeTTL, - meta: meta?.codableValue), + .compressedPublish( + message: message.codableValue, + channel: channel, + shouldStore: shouldStore, + ttl: storeTTL, + meta: meta?.codableValue + ), configuration: requestConfig.customConfiguration ?? configuration ) } else { router = PublishRouter( - .publish(message: message.codableValue, - channel: channel, - shouldStore: shouldStore, - ttl: storeTTL, - meta: meta?.codableValue), + .publish( + message: message.codableValue, + channel: channel, + shouldStore: shouldStore, + ttl: storeTTL, + meta: meta?.codableValue + ), configuration: requestConfig.customConfiguration ?? configuration ) } - route(router, - responseDecoder: PublishResponseDecoder(), - custom: requestConfig) { result in + route( + router, + requestOperator: configuration.automaticRetry?[.publish], + responseDecoder: PublishResponseDecoder(), + custom: requestConfig + ) { result in completion?(result.map { $0.payload.timetoken }) } } @@ -294,10 +306,15 @@ public extension PubNub { custom requestConfig: RequestConfiguration = RequestConfiguration(), completion: ((Result) -> Void)? ) { - route(PublishRouter(.fire(message: message.codableValue, channel: channel, meta: meta?.codableValue), - configuration: requestConfig.customConfiguration ?? configuration), - responseDecoder: PublishResponseDecoder(), - custom: requestConfig) { result in + route( + PublishRouter( + .fire(message: message.codableValue, channel: channel, meta: meta?.codableValue), + configuration: requestConfig.customConfiguration ?? configuration + ), + requestOperator: configuration.automaticRetry?[.fire], + responseDecoder: PublishResponseDecoder(), + custom: requestConfig + ) { result in completion?(result.map { $0.payload.timetoken }) } } @@ -318,10 +335,15 @@ public extension PubNub { custom requestConfig: RequestConfiguration = RequestConfiguration(), completion: ((Result) -> Void)? ) { - route(PublishRouter(.signal(message: message.codableValue, channel: channel), - configuration: requestConfig.customConfiguration ?? configuration), - responseDecoder: PublishResponseDecoder(), - custom: requestConfig) { result in + route( + PublishRouter( + .signal(message: message.codableValue, channel: channel), + configuration: requestConfig.customConfiguration ?? configuration + ), + requestOperator: configuration.automaticRetry?[.signal], + responseDecoder: PublishResponseDecoder(), + custom: requestConfig + ) { result in completion?(result.map { $0.payload.timetoken }) } } @@ -348,10 +370,12 @@ public extension PubNub { ) { subscription.filterExpression = filterOverride - subscription.subscribe(to: channels, - and: channelGroups, - at: SubscribeCursor(timetoken: timetoken), - withPresence: withPresence) + subscription.subscribe( + to: channels, + and: channelGroups, + at: SubscribeCursor(timetoken: timetoken), + withPresence: withPresence + ) } /// Unsubscribe from channels and/or channel groups @@ -445,9 +469,12 @@ public extension PubNub { configuration: requestConfig.customConfiguration ?? configuration ) - route(router, - responseDecoder: PresenceResponseDecoder>(), - custom: requestConfig) { result in + route( + router, + requestOperator: configuration.automaticRetry?[.setPresence], + responseDecoder: PresenceResponseDecoder>(), + custom: requestConfig + ) { result in completion?(result.map { $0.payload.payload }) } } @@ -472,9 +499,12 @@ public extension PubNub { configuration: requestConfig.customConfiguration ?? configuration ) - route(router, - responseDecoder: GetPresenceStateResponseDecoder(), - custom: requestConfig) { result in + route( + router, + requestOperator: configuration.automaticRetry?[.getPresence], + responseDecoder: GetPresenceStateResponseDecoder(), + custom: requestConfig + ) { result in completion?(result.map { (uuid: $0.payload.uuid, stateByChannel: $0.payload.channels) }) } } @@ -504,8 +534,10 @@ public extension PubNub { ) { let router: PresenceRouter if channels.isEmpty, groups.isEmpty { - router = PresenceRouter(.hereNowGlobal(includeUUIDs: includeUUIDs, includeState: includeState), - configuration: requestConfig.customConfiguration ?? configuration) + router = PresenceRouter( + .hereNowGlobal(includeUUIDs: includeUUIDs, includeState: includeState), + configuration: requestConfig.customConfiguration ?? configuration + ) } else { router = PresenceRouter( .hereNow(channels: channels, groups: groups, includeUUIDs: includeUUIDs, includeState: includeState), @@ -515,9 +547,12 @@ public extension PubNub { let decoder = HereNowResponseDecoder(channels: channels, groups: groups) - route(router, - responseDecoder: decoder, - custom: requestConfig) { result in + route( + router, + requestOperator: configuration.automaticRetry?[.hereNow], + responseDecoder: decoder, + custom: requestConfig + ) { result in completion?(result.map { $0.payload.asPubNubPresenceBase }) } } @@ -534,9 +569,12 @@ public extension PubNub { custom requestConfig: RequestConfiguration = RequestConfiguration(), completion: ((Result<[String: [String]], Error>) -> Void)? ) { - route(PresenceRouter(.whereNow(uuid: uuid), configuration: requestConfig.customConfiguration ?? configuration), - responseDecoder: PresenceResponseDecoder>(), - custom: requestConfig) { result in + route( + PresenceRouter(.whereNow(uuid: uuid), configuration: requestConfig.customConfiguration ?? configuration), + requestOperator: configuration.automaticRetry?[.whereNow], + responseDecoder: PresenceResponseDecoder>(), + custom: requestConfig + ) { result in completion?(result.map { [uuid: $0.payload.payload.channels] }) } } @@ -555,9 +593,12 @@ public extension PubNub { custom requestConfig: RequestConfiguration = RequestConfiguration(), completion: ((Result<[String], Error>) -> Void)? ) { - route(ChannelGroupsRouter(.channelGroups, configuration: requestConfig.customConfiguration ?? configuration), - responseDecoder: ChannelGroupResponseDecoder(), - custom: requestConfig) { result in + route( + ChannelGroupsRouter(.channelGroups, configuration: requestConfig.customConfiguration ?? configuration), + requestOperator: configuration.automaticRetry?[.listChannelGroups], + responseDecoder: ChannelGroupResponseDecoder(), + custom: requestConfig + ) { result in completion?(result.map { $0.payload.payload.groups }) } } @@ -580,6 +621,7 @@ public extension PubNub { .deleteGroup(group: channelGroup), configuration: requestConfig.customConfiguration ?? configuration ), + requestOperator: configuration.automaticRetry?[.removeChannelGroup], responseDecoder: GenericServiceResponseDecoder(), custom: requestConfig ) { result in @@ -604,6 +646,7 @@ public extension PubNub { .channelsForGroup(group: group), configuration: requestConfig.customConfiguration ?? configuration ), + requestOperator: configuration.automaticRetry?[.listChannelsForGroup], responseDecoder: ChannelGroupResponseDecoder(), custom: requestConfig ) { result in @@ -630,6 +673,7 @@ public extension PubNub { .addChannelsToGroup(group: group, channels: channels), configuration: requestConfig.customConfiguration ?? configuration ), + requestOperator: configuration.automaticRetry?[.addChannelsToGroup], responseDecoder: GenericServiceResponseDecoder(), custom: requestConfig ) { result in @@ -637,7 +681,7 @@ public extension PubNub { } } - /// Rremoves the channels from the channel group. + /// Removes the channels from the channel group. /// - Parameters: /// - channels: List of channels to remove from the group /// - from: The Channel Group to remove the list of channels from @@ -656,6 +700,7 @@ public extension PubNub { .removeChannelsForGroup(group: group, channels: channels), configuration: requestConfig.customConfiguration ?? configuration ), + requestOperator: configuration.automaticRetry?[.removeChannelsFromGroup], responseDecoder: GenericServiceResponseDecoder(), custom: requestConfig ) { result in @@ -686,6 +731,7 @@ public extension PubNub { .listPushChannels(pushToken: deviceToken, pushType: pushType), configuration: requestConfig.customConfiguration ?? configuration ), + requestOperator: configuration.automaticRetry?[.listPushChannels], responseDecoder: RegisteredPushChannelsResponseDecoder(), custom: requestConfig ) { result in @@ -716,9 +762,12 @@ public extension PubNub { configuration: requestConfig.customConfiguration ?? configuration ) - route(router, - responseDecoder: ModifyPushResponseDecoder(), - custom: requestConfig) { result in + route( + router, + requestOperator: configuration.automaticRetry?[.managePushChannels], + responseDecoder: ModifyPushResponseDecoder(), + custom: requestConfig + ) { result in completion?(result.map { (added: $0.payload.added, removed: $0.payload.removed) }) } } @@ -788,6 +837,7 @@ public extension PubNub { .removeAllPushChannels(pushToken: deviceToken, pushType: pushType), configuration: requestConfig.customConfiguration ?? configuration ), + requestOperator: configuration.automaticRetry?[.managePushChannels], responseDecoder: ModifyPushResponseDecoder(), custom: requestConfig ) { result in @@ -813,11 +863,10 @@ public extension PubNub { ) { route( PushRouter( - .manageAPNS( - pushToken: deviceToken, environment: environment, topic: topic, adding: [], removing: [] - ), + .manageAPNS(pushToken: deviceToken, environment: environment, topic: topic, adding: [], removing: []), configuration: requestConfig.customConfiguration ?? configuration ), + requestOperator: configuration.automaticRetry?[.listAPNSPushChannels], responseDecoder: RegisteredPushChannelsResponseDecoder(), custom: requestConfig ) { result in @@ -846,20 +895,28 @@ public extension PubNub { completion: ((Result<(added: [String], removed: [String]), Error>) -> Void)? ) { let router = PushRouter( - .manageAPNS(pushToken: token, environment: environment, - topic: topic, adding: additions, removing: removals), + .manageAPNS( + pushToken: token, environment: environment, topic: topic, + adding: additions, removing: removals + ), configuration: requestConfig.customConfiguration ?? configuration ) if removals.isEmpty, additions.isEmpty { completion?( - .failure(PubNubError(.missingRequiredParameter, - router: router, - additional: [ErrorDescription.missingChannelsAnyGroups]))) + .failure(PubNubError( + .missingRequiredParameter, + router: router, + additional: [ErrorDescription.missingChannelsAnyGroups] + )) + ) } else { - route(router, - responseDecoder: ModifyPushResponseDecoder(), - custom: requestConfig) { result in + route( + router, + requestOperator: configuration.automaticRetry?[.manageAPNSDevices], + responseDecoder: ModifyPushResponseDecoder(), + custom: requestConfig + ) { result in completion?(result.map { (added: $0.payload.added, removed: $0.payload.removed) }) } } @@ -931,10 +988,15 @@ public extension PubNub { custom requestConfig: RequestConfiguration = RequestConfiguration(), completion: ((Result) -> Void)? ) { - route(PushRouter(.removeAllAPNS(pushToken: deviceToken, environment: environment, topic: topic), - configuration: requestConfig.customConfiguration ?? configuration), - responseDecoder: ModifyPushResponseDecoder(), - custom: requestConfig) { result in + route( + PushRouter( + .removeAllAPNS(pushToken: deviceToken, environment: environment, topic: topic), + configuration: requestConfig.customConfiguration ?? configuration + ), + requestOperator: configuration.automaticRetry?[.manageAPNSDevices], + responseDecoder: ModifyPushResponseDecoder(), + custom: requestConfig + ) { result in completion?(result.map { _ in () }) } } @@ -1009,6 +1071,7 @@ public extension PubNub { route( router, + requestOperator: configuration.automaticRetry?[.fetchMessageHistory], responseDecoder: MessageHistoryResponseDecoder(), custom: requestConfig ) { result in @@ -1042,6 +1105,7 @@ public extension PubNub { .delete(channel: channel, start: start, end: end), configuration: requestConfig.customConfiguration ?? configuration ), + requestOperator: configuration.automaticRetry?[.deleteMessageHistory], responseDecoder: GenericServiceResponseDecoder(), custom: requestConfig ) { result in @@ -1066,9 +1130,12 @@ public extension PubNub { configuration: requestConfig.customConfiguration ?? configuration ) - route(router, - responseDecoder: MessageCountsResponseDecoder(), - custom: requestConfig) { result in + route( + router, + requestOperator: configuration.automaticRetry?[.messageCounts], + responseDecoder: MessageCountsResponseDecoder(), + custom: requestConfig + ) { result in completion?(result.map { $0.payload.channels }) } } @@ -1092,9 +1159,12 @@ public extension PubNub { configuration: requestConfig.customConfiguration ?? configuration ) - route(router, - responseDecoder: MessageCountsResponseDecoder(), - custom: requestConfig) { result in + route( + router, + requestOperator: configuration.automaticRetry?[.messageCounts], + responseDecoder: MessageCountsResponseDecoder(), + custom: requestConfig + ) { result in completion?(result.map { $0.payload.channels }) } } @@ -1122,6 +1192,7 @@ public extension PubNub { .fetch(channel: channel, start: page?.start, end: page?.end, limit: page?.limit), configuration: requestConfig.customConfiguration ?? configuration ), + requestOperator: configuration.automaticRetry?[.fetchMessageActions], responseDecoder: MessageActionsResponseDecoder(), custom: requestConfig ) { result in @@ -1162,12 +1233,14 @@ public extension PubNub { configuration: requestConfig.customConfiguration ?? configuration ) - route(router, - responseDecoder: MessageActionResponseDecoder(), - custom: requestConfig) { result in + route( + router, + requestOperator: configuration.automaticRetry?[.addMessageAction], + responseDecoder: MessageActionResponseDecoder(), + custom: requestConfig + ) { result in switch result { case let .success(response): - if let errorPayload = response.payload.error { let error = PubNubError( reason: errorPayload.message.pubnubReason, router: router, @@ -1205,9 +1278,12 @@ public extension PubNub { configuration: requestConfig.customConfiguration ?? configuration ) - route(router, - responseDecoder: DeleteResponseDecoder(), - custom: requestConfig) { result in + route( + router, + requestOperator: configuration.automaticRetry?[.removeMessageAction], + responseDecoder: DeleteResponseDecoder(), + custom: requestConfig + ) { result in switch result { case let .success(response): if let errorPayload = response.payload.error { diff --git a/Sources/PubNub/Subscription/SubscriptionSession+Presence.swift b/Sources/PubNub/Subscription/SubscriptionSession+Presence.swift index 8a752602..7128b8fc 100644 --- a/Sources/PubNub/Subscription/SubscriptionSession+Presence.swift +++ b/Sources/PubNub/Subscription/SubscriptionSession+Presence.swift @@ -55,7 +55,7 @@ extension SubscriptionSession { ) nonSubscribeSession - .request(with: router, requestOperator: configuration.automaticRetry) + .request(with: router, requestOperator: configuration.automaticRetry?[.heartbeat]) .validate() .response(on: .main, decoder: GenericServiceResponseDecoder()) { [weak self] result in switch result { diff --git a/Sources/PubNub/Subscription/SubscriptionSession.swift b/Sources/PubNub/Subscription/SubscriptionSession.swift index c8164aa6..ff715908 100644 --- a/Sources/PubNub/Subscription/SubscriptionSession.swift +++ b/Sources/PubNub/Subscription/SubscriptionSession.swift @@ -208,11 +208,13 @@ public class SubscriptionSession { stopSubscribeLoop(.longPollingRestart) // Will compre this in the error response to see if we need to restart - let nextSubscribe = longPollingSession - .request(with: router, requestOperator: configuration.automaticRetry) + let nextSubscribe = longPollingSession.request( + with: router, + requestOperator: configuration.automaticRetry?[.subscribe] + ) let currentSubscribeID = nextSubscribe.requestID + request = nextSubscribe - request? .validate() .response(on: .main, decoder: SubscribeDecoder()) { [weak self] result in