From a2e878ce07fa1adbb79478a06946d766b5537d9e Mon Sep 17 00:00:00 2001 From: Ian Saultz <52051793+atierian@users.noreply.github.com> Date: Mon, 2 Oct 2023 16:16:46 -0400 Subject: [PATCH] fix(analytics): update error handling (#3261) --- .../Extensions/SdkError+Analytics.swift | 20 --- ...WSPinpoint+AnalyticsErrorConvertible.swift | 53 ++++++++ .../Utils/AnalyticsErrorConvertible.swift | 19 +++ .../Support/Utils/AnalyticsErrorHelper.swift | 20 ++- ...nTimeError+AnalyticsErrorConvertible.swift | 23 ++++ .../Analytics/ClientError+IsRetryable.swift | 29 ++++ .../Analytics/EventRecorder.swift | 35 ++--- .../Analytics/SdkError+IsRetryable.swift | 74 ----------- .../Context/PinpointContext.swift | 4 +- ...mmonRunTimeError+isConnectivityError.swift | 41 ++++++ .../PinpointClient+CredentialsProvider.swift | 8 +- .../Extensions/SdkError+Pinpoint.swift | 124 ------------------ .../Support/ModeledErrorDescribable.swift | 42 ++++++ .../Utils/PinpointRequestsRegistry.swift | 6 +- 14 files changed, 238 insertions(+), 260 deletions(-) delete mode 100644 AmplifyPlugins/Analytics/Sources/AWSPinpointAnalyticsPlugin/Support/Extensions/SdkError+Analytics.swift create mode 100644 AmplifyPlugins/Analytics/Sources/AWSPinpointAnalyticsPlugin/Support/Utils/AWSPinpoint+AnalyticsErrorConvertible.swift create mode 100644 AmplifyPlugins/Analytics/Sources/AWSPinpointAnalyticsPlugin/Support/Utils/AnalyticsErrorConvertible.swift create mode 100644 AmplifyPlugins/Analytics/Sources/AWSPinpointAnalyticsPlugin/Support/Utils/CommonRunTimeError+AnalyticsErrorConvertible.swift create mode 100644 AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/ClientError+IsRetryable.swift delete mode 100644 AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/SdkError+IsRetryable.swift create mode 100644 AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Extensions/CommonRunTimeError+isConnectivityError.swift delete mode 100644 AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Extensions/SdkError+Pinpoint.swift create mode 100644 AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Support/ModeledErrorDescribable.swift diff --git a/AmplifyPlugins/Analytics/Sources/AWSPinpointAnalyticsPlugin/Support/Extensions/SdkError+Analytics.swift b/AmplifyPlugins/Analytics/Sources/AWSPinpointAnalyticsPlugin/Support/Extensions/SdkError+Analytics.swift deleted file mode 100644 index 34725ba1c5..0000000000 --- a/AmplifyPlugins/Analytics/Sources/AWSPinpointAnalyticsPlugin/Support/Extensions/SdkError+Analytics.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -import Amplify -import ClientRuntime -import Foundation -@_spi(InternalAWSPinpoint) import InternalAWSPinpoint - -extension SdkError { - var analyticsError: AnalyticsError { - return .unknown( - isConnectivityError ? AWSPinpointErrorConstants.deviceOffline.errorDescription : errorDescription, - rootError ?? self - ) - } -} diff --git a/AmplifyPlugins/Analytics/Sources/AWSPinpointAnalyticsPlugin/Support/Utils/AWSPinpoint+AnalyticsErrorConvertible.swift b/AmplifyPlugins/Analytics/Sources/AWSPinpointAnalyticsPlugin/Support/Utils/AWSPinpoint+AnalyticsErrorConvertible.swift new file mode 100644 index 0000000000..42c5464f9e --- /dev/null +++ b/AmplifyPlugins/Analytics/Sources/AWSPinpointAnalyticsPlugin/Support/Utils/AWSPinpoint+AnalyticsErrorConvertible.swift @@ -0,0 +1,53 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import Foundation +import Amplify +import AWSPinpoint +import ClientRuntime + +extension AWSPinpoint.BadRequestException: AnalyticsErrorConvertible { + var analyticsError: AnalyticsError { + .unknown(properties.message ?? "", self) + } +} + +extension AWSPinpoint.ForbiddenException: AnalyticsErrorConvertible { + var analyticsError: AnalyticsError { + .unknown(properties.message ?? "", self) + } +} + +extension AWSPinpoint.InternalServerErrorException: AnalyticsErrorConvertible { + var analyticsError: AnalyticsError { + .unknown(properties.message ?? "", self) + } +} + +extension AWSPinpoint.MethodNotAllowedException: AnalyticsErrorConvertible { + var analyticsError: AnalyticsError { + .unknown(properties.message ?? "", self) + } +} + +extension AWSPinpoint.NotFoundException: AnalyticsErrorConvertible { + var analyticsError: AnalyticsError { + .unknown(properties.message ?? "", self) + } +} + +extension AWSPinpoint.PayloadTooLargeException: AnalyticsErrorConvertible { + var analyticsError: AnalyticsError { + .unknown(properties.message ?? "", self) + } +} + +extension AWSPinpoint.TooManyRequestsException: AnalyticsErrorConvertible { + var analyticsError: AnalyticsError { + .unknown(properties.message ?? "", self) + } +} diff --git a/AmplifyPlugins/Analytics/Sources/AWSPinpointAnalyticsPlugin/Support/Utils/AnalyticsErrorConvertible.swift b/AmplifyPlugins/Analytics/Sources/AWSPinpointAnalyticsPlugin/Support/Utils/AnalyticsErrorConvertible.swift new file mode 100644 index 0000000000..e21c889309 --- /dev/null +++ b/AmplifyPlugins/Analytics/Sources/AWSPinpointAnalyticsPlugin/Support/Utils/AnalyticsErrorConvertible.swift @@ -0,0 +1,19 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import Foundation +import Amplify + +protocol AnalyticsErrorConvertible { + var analyticsError: AnalyticsError { get } +} + +extension AnalyticsError: AnalyticsErrorConvertible { + var analyticsError: AnalyticsError { + self + } +} diff --git a/AmplifyPlugins/Analytics/Sources/AWSPinpointAnalyticsPlugin/Support/Utils/AnalyticsErrorHelper.swift b/AmplifyPlugins/Analytics/Sources/AWSPinpointAnalyticsPlugin/Support/Utils/AnalyticsErrorHelper.swift index 28af56447f..684289ca1b 100644 --- a/AmplifyPlugins/Analytics/Sources/AWSPinpointAnalyticsPlugin/Support/Utils/AnalyticsErrorHelper.swift +++ b/AmplifyPlugins/Analytics/Sources/AWSPinpointAnalyticsPlugin/Support/Utils/AnalyticsErrorHelper.swift @@ -5,22 +5,18 @@ // SPDX-License-Identifier: Apache-2.0 // -import Amplify -import AWSPinpoint -import ClientRuntime import Foundation +import Amplify +import AwsCommonRuntimeKit -class AnalyticsErrorHelper { +enum AnalyticsErrorHelper { static func getDefaultError(_ error: Error) -> AnalyticsError { - if let sdkError = error as? SdkError{ - return sdkError.analyticsError + switch error { + case let error as AnalyticsErrorConvertible: + return error.analyticsError + default: + return getDefaultError(error as NSError) } - - if let analyticsError = error as? AnalyticsError { - return analyticsError - } - - return getDefaultError(error as NSError) } static func getDefaultError(_ error: NSError) -> AnalyticsError { diff --git a/AmplifyPlugins/Analytics/Sources/AWSPinpointAnalyticsPlugin/Support/Utils/CommonRunTimeError+AnalyticsErrorConvertible.swift b/AmplifyPlugins/Analytics/Sources/AWSPinpointAnalyticsPlugin/Support/Utils/CommonRunTimeError+AnalyticsErrorConvertible.swift new file mode 100644 index 0000000000..b65918f472 --- /dev/null +++ b/AmplifyPlugins/Analytics/Sources/AWSPinpointAnalyticsPlugin/Support/Utils/CommonRunTimeError+AnalyticsErrorConvertible.swift @@ -0,0 +1,23 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import Foundation +import Amplify +@_spi(InternalAWSPinpoint) import InternalAWSPinpoint +import AwsCommonRuntimeKit + +extension CommonRunTimeError: AnalyticsErrorConvertible { + var analyticsError: AnalyticsError { + switch self { + case .crtError(let crtError): + let errorDescription = isConnectivityError + ? AWSPinpointErrorConstants.deviceOffline.errorDescription + : crtError.message + return .unknown(errorDescription, self) + } + } +} diff --git a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/ClientError+IsRetryable.swift b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/ClientError+IsRetryable.swift new file mode 100644 index 0000000000..77f1cb2c5e --- /dev/null +++ b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/ClientError+IsRetryable.swift @@ -0,0 +1,29 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import ClientRuntime +import Foundation + +extension ClientError { + // TODO: Should some of these really be retried? + var isRetryable: Bool { + switch self { + case .authError: + return true + case .dataNotFound: + return true + case .pathCreationFailed: + return true + case .queryItemCreationFailed: + return true + case .serializationFailed: + return false + case .unknownError: + return true + } + } +} diff --git a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/EventRecorder.swift b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/EventRecorder.swift index 39397cb0e5..5c2675cdbe 100644 --- a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/EventRecorder.swift +++ b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/EventRecorder.swift @@ -8,6 +8,7 @@ import Amplify import AWSPinpoint import ClientRuntime +import enum AwsCommonRuntimeKit.CommonRunTimeError import Foundation /// AnalyticsEventRecording saves and submits pinpoint events @@ -206,26 +207,21 @@ class EventRecorder: AnalyticsEventRecording { } private func isErrorRetryable(_ error: Error) -> Bool { - switch error { - case let clientError as ClientError: - return clientError.isRetryable - case let putEventsOutputError as PutEventsOutputError: - return putEventsOutputError.isRetryable - case let sdkPutEventsOutputError as SdkError: - return sdkPutEventsOutputError.isRetryable - case let sdkError as SdkError: - return sdkError.isRetryable - default: + guard case let modeledError as ModeledError = error else { return false } + return type(of: modeledError).isRetryable } private func errorDescription(_ error: Error) -> String { switch error { - case let sdkPutEventsOutputError as SdkError: - return sdkPutEventsOutputError.errorDescription - case let sdkError as SdkError: - return sdkError.errorDescription + case let error as ModeledErrorDescribable: + return error.errorDescription + case let error as CommonRunTimeError: + switch error { + case .crtError(let crtError): + return crtError.message + } default: return error.localizedDescription } @@ -233,15 +229,8 @@ class EventRecorder: AnalyticsEventRecording { private func isConnectivityError(_ error: Error) -> Bool { switch error { - case let clientError as ClientError: - if case .networkError(_) = clientError { - return true - } - return false - case let sdkPutEventsOutputError as SdkError: - return sdkPutEventsOutputError.isConnectivityError - case let sdkError as SdkError: - return sdkError.isConnectivityError + case let error as CommonRunTimeError: + return error.isConnectivityError case let error as NSError: let networkErrorCodes = [ NSURLErrorCannotFindHost, diff --git a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/SdkError+IsRetryable.swift b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/SdkError+IsRetryable.swift deleted file mode 100644 index 5db7bde825..0000000000 --- a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Analytics/SdkError+IsRetryable.swift +++ /dev/null @@ -1,74 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -import AWSPinpoint -import ClientRuntime -import Foundation - -@_spi(InternalAWSPinpoint) -public extension SdkError { - var isRetryable: Bool { - switch self { - case .service(let error, _): - return (error as? PutEventsOutputError)?.isRetryable == true - case .client(let error, _): - return error.isRetryable - default: - return true - } - } -} - -extension ClientError { - var isRetryable: Bool { - switch self { - case .authError: - return true - case .crtError: - return true - case .dataNotFound: - return true - case .deserializationFailed: - return false - case .networkError: - return true - case .pathCreationFailed: - return true - case .queryItemCreationFailed(_): - return true - case .retryError: - return true - case .serializationFailed: - return false - case .unknownError: - return true - } - } -} - -extension PutEventsOutputError { - var isRetryable: Bool { - switch self { - case .badRequestException(let exception): - return exception._retryable - case .forbiddenException(let exception): - return exception._retryable - case .internalServerErrorException(let exception): - return exception._retryable - case .methodNotAllowedException(let exception): - return exception._retryable - case .notFoundException(let exception): - return exception._retryable - case .payloadTooLargeException(let exception): - return exception._retryable - case .tooManyRequestsException(let exception): - return exception._retryable - case .unknown(let exception): - return exception._retryable - } - } -} diff --git a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Context/PinpointContext.swift b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Context/PinpointContext.swift index b64742422f..70303f278e 100644 --- a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Context/PinpointContext.swift +++ b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Context/PinpointContext.swift @@ -76,14 +76,14 @@ struct PinpointContextConfiguration { /// The Pinpoint region let region: String /// Used to retrieve the proper AWSCredentials when creating the PinpointCLient - let credentialsProvider: CredentialsProvider + let credentialsProvider: CredentialsProviding /// Indicates if the App is in Debug or Release build. Defaults to `false` /// Setting this flag to true will set the Endpoint Profile to have a channel type of "APNS_SANDBOX". let isDebug: Bool init(appId: String, region: String, - credentialsProvider: CredentialsProvider, + credentialsProvider: CredentialsProviding, isDebug: Bool = false) { self.appId = appId self.region = region diff --git a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Extensions/CommonRunTimeError+isConnectivityError.swift b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Extensions/CommonRunTimeError+isConnectivityError.swift new file mode 100644 index 0000000000..a0555c2da5 --- /dev/null +++ b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Extensions/CommonRunTimeError+isConnectivityError.swift @@ -0,0 +1,41 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import Amplify +import AwsCIo +import AwsCHttp +import AwsCommonRuntimeKit +import AWSPinpoint +import ClientRuntime +import Foundation + +@_spi(InternalAWSPinpoint) +extension CommonRunTimeError { + static let connectivityErrorCodes: Set = [ + AWS_ERROR_HTTP_CONNECTION_CLOSED.rawValue, + AWS_ERROR_HTTP_SERVER_CLOSED.rawValue, + AWS_IO_DNS_INVALID_NAME.rawValue, + AWS_IO_DNS_NO_ADDRESS_FOR_HOST.rawValue, + AWS_IO_DNS_QUERY_FAILED.rawValue, + AWS_IO_SOCKET_CONNECT_ABORTED.rawValue, + AWS_IO_SOCKET_CONNECTION_REFUSED.rawValue, + AWS_IO_SOCKET_CLOSED.rawValue, + AWS_IO_SOCKET_NETWORK_DOWN.rawValue, + AWS_IO_SOCKET_NO_ROUTE_TO_HOST.rawValue, + AWS_IO_SOCKET_NOT_CONNECTED.rawValue, + AWS_IO_SOCKET_TIMEOUT.rawValue, + AWS_IO_TLS_NEGOTIATION_TIMEOUT.rawValue, + UInt32(AWS_HTTP_STATUS_CODE_408_REQUEST_TIMEOUT.rawValue) + ] + + public var isConnectivityError: Bool { + switch self { + case .crtError(let error): + Self.connectivityErrorCodes.contains(UInt32(error.code)) + } + } +} diff --git a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Extensions/PinpointClient+CredentialsProvider.swift b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Extensions/PinpointClient+CredentialsProvider.swift index 447d092b61..2a2e1a30f9 100644 --- a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Extensions/PinpointClient+CredentialsProvider.swift +++ b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Extensions/PinpointClient+CredentialsProvider.swift @@ -11,11 +11,11 @@ import AWSPinpoint @_spi(FoundationClientEngine) import AWSPluginsCore extension PinpointClient { - convenience init(region: String, credentialsProvider: CredentialsProvider) throws { + convenience init(region: String, credentialsProvider: CredentialsProviding) throws { + // TODO: FrameworkMetadata Replacement let configuration = try PinpointClientConfiguration( - credentialsProvider: credentialsProvider, - frameworkMetadata: AmplifyAWSServiceConfiguration.frameworkMetaData(), - region: region + region: region, + credentialsProvider: credentialsProvider ) #if os(iOS) || os(macOS) // no-op #else diff --git a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Extensions/SdkError+Pinpoint.swift b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Extensions/SdkError+Pinpoint.swift deleted file mode 100644 index a24a602cff..0000000000 --- a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Extensions/SdkError+Pinpoint.swift +++ /dev/null @@ -1,124 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -import Amplify -import AwsCIo -import AwsCHttp -import AwsCommonRuntimeKit -import AWSPinpoint -import ClientRuntime -import Foundation - -@_spi(InternalAWSPinpoint) -extension SdkError { - private var clientError: ClientError? { - guard case .client(let clientError, _) = self else { - return nil - } - - return clientError - } - - private var commonRunTimeError: CommonRunTimeError? { - if case .crtError(let commonRunTimeError) = clientError { - return commonRunTimeError - } - - if case .retryError(let commonRunTimeError as CommonRunTimeError) = clientError { - return commonRunTimeError - } - - return nil - } - - private var crtError: CRTError? { - if case .crtError(let crtError) = commonRunTimeError { - return crtError - } - - return nil - } - - private var putEventsOutputError: PutEventsOutputError? { - guard case .retryError(let sdkError as SdkError) = clientError, - case .service(let putEventsError as PutEventsOutputError, _) = sdkError else { - return nil - } - - return putEventsError - } - - public var errorDescription: String { - guard let putEventsOutputError = putEventsOutputError else { - return crtError?.message ?? localizedDescription - } - - switch putEventsOutputError { - case .badRequestException(let exception as ServiceError), - .forbiddenException(let exception as ServiceError), - .internalServerErrorException(let exception as ServiceError), - .methodNotAllowedException(let exception as ServiceError), - .notFoundException(let exception as ServiceError), - .payloadTooLargeException(let exception as ServiceError), - .tooManyRequestsException(let exception as ServiceError), - .unknown(let exception as ServiceError): - return exception._message ?? localizedDescription - } - } - - public var rootError: Error? { - if putEventsOutputError != nil { - return putEventsOutputError - } - - if commonRunTimeError != nil { - return commonRunTimeError - } - - guard let clientError = clientError else { - return nil - } - - switch clientError { - case .networkError(let error), - .deserializationFailed(let error), - .retryError(let error): - return error - default: - return nil - } - } - - public var isConnectivityError: Bool { - if case .networkError(_) = clientError { - return true - } - - guard let crtError = crtError else { - return false - } - - let connectivityErrorCodes: [UInt32] = [ - AWS_ERROR_HTTP_CONNECTION_CLOSED.rawValue, - AWS_ERROR_HTTP_SERVER_CLOSED.rawValue, - AWS_IO_DNS_INVALID_NAME.rawValue, - AWS_IO_DNS_NO_ADDRESS_FOR_HOST.rawValue, - AWS_IO_DNS_QUERY_FAILED.rawValue, - AWS_IO_SOCKET_CONNECT_ABORTED.rawValue, - AWS_IO_SOCKET_CONNECTION_REFUSED.rawValue, - AWS_IO_SOCKET_CLOSED.rawValue, - AWS_IO_SOCKET_NETWORK_DOWN.rawValue, - AWS_IO_SOCKET_NO_ROUTE_TO_HOST.rawValue, - AWS_IO_SOCKET_NOT_CONNECTED.rawValue, - AWS_IO_SOCKET_TIMEOUT.rawValue, - AWS_IO_TLS_NEGOTIATION_TIMEOUT.rawValue, - UInt32(AWS_HTTP_STATUS_CODE_408_REQUEST_TIMEOUT.rawValue) - ] - - return connectivityErrorCodes.contains(where: { $0 == crtError.code }) - } -} diff --git a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Support/ModeledErrorDescribable.swift b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Support/ModeledErrorDescribable.swift new file mode 100644 index 0000000000..60608de459 --- /dev/null +++ b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Support/ModeledErrorDescribable.swift @@ -0,0 +1,42 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import Foundation +import Amplify +import AWSPinpoint + +protocol ModeledErrorDescribable { + var errorDescription: String { get } +} + +extension AWSPinpoint.BadRequestException: ModeledErrorDescribable { + var errorDescription: String { properties.message ?? "" } +} + +extension AWSPinpoint.ForbiddenException: ModeledErrorDescribable { + var errorDescription: String { properties.message ?? "" } +} + +extension AWSPinpoint.InternalServerErrorException: ModeledErrorDescribable { + var errorDescription: String { properties.message ?? "" } +} + +extension AWSPinpoint.MethodNotAllowedException: ModeledErrorDescribable { + var errorDescription: String { properties.message ?? "" } +} + +extension AWSPinpoint.NotFoundException: ModeledErrorDescribable { + var errorDescription: String { properties.message ?? "" } +} + +extension AWSPinpoint.PayloadTooLargeException: ModeledErrorDescribable { + var errorDescription: String { properties.message ?? "" } +} + +extension AWSPinpoint.TooManyRequestsException: ModeledErrorDescribable { + var errorDescription: String { properties.message ?? "" } +} diff --git a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Support/Utils/PinpointRequestsRegistry.swift b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Support/Utils/PinpointRequestsRegistry.swift index 7bcd61a0a5..d28826561e 100644 --- a/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Support/Utils/PinpointRequestsRegistry.swift +++ b/AmplifyPlugins/Internal/Sources/InternalAWSPinpoint/Support/Utils/PinpointRequestsRegistry.swift @@ -70,7 +70,11 @@ private struct CustomPinpointHttpClientEngine: HttpClientEngine { let currentUserAgent = headers.value(for: userAgentHeader) ?? "" headers.update(name: userAgentHeader, value: "\(currentUserAgent)\(userAgentSuffix)") - request.headers = headers + for header in headers.headers { + for value in header.value { + request.withHeader(name: header.name, value: value) + } + } await PinpointRequestsRegistry.shared.unregisterSources(for: pinpointApi) return try await httpClientEngine.execute(request: request)