diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 2310f611..50a4ea0e 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -2,7 +2,7 @@ name: Build and Test env: afterpay-scheme: Afterpay - DEVELOPER_DIR: /Applications/Xcode_15.0.1.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_14.3.1.app/Contents/Developer on: push: @@ -17,7 +17,7 @@ jobs: runs-on: macos-latest env: - destination: platform=iOS Simulator,name=iPhone 15,OS=17.4 + destination: platform=iOS Simulator,name=iPhone 14,OS=16.4 example-scheme: Example example-ui-test-scheme: ExampleUITests workspace: Afterpay.xcworkspace diff --git a/Example/ExampleUITests/ExampleUITests.swift b/Example/ExampleUITests/ExampleUITests.swift index b2c3392f..6335610e 100644 --- a/Example/ExampleUITests/ExampleUITests.swift +++ b/Example/ExampleUITests/ExampleUITests.swift @@ -43,7 +43,7 @@ final class ExampleUITests: XCTestCase { func testTokenlessWidgetAppears() throws { app.buttons["Tokenless…"].tap() - _ = app.webViews.staticTexts.firstMatch.waitForExistence(timeout: 30) + _ = app.webViews.staticTexts.firstMatch.waitForExistence(timeout: 60) let webViewText = app.webViews.staticTexts.firstMatch @@ -56,7 +56,8 @@ final class ExampleUITests: XCTestCase { textField.typeText("444") app.buttons["Update"].tap() - XCTAssertTrue(webViewText.label.contains(#"{"amount":"444","currency":"USD"}"#)) + XCTAssertTrue(webViewText.label.contains(#""amount":"444""#)) + XCTAssertTrue(webViewText.label.contains(#""currency":"USD""#)) } } diff --git a/Sources/Afterpay/Wrappers/ObjcWrapper.swift b/Sources/Afterpay/Wrappers/ObjcWrapper.swift index 1757ab22..8ae04bb1 100644 --- a/Sources/Afterpay/Wrappers/ObjcWrapper.swift +++ b/Sources/Afterpay/Wrappers/ObjcWrapper.swift @@ -9,6 +9,8 @@ import Foundation import UIKit +// swiftlint:disable file_length +// swiftlint:disable type_body_length @objc(APAfterpay) public final class ObjcWrapper: NSObject { @@ -168,6 +170,370 @@ public final class ObjcWrapper: NSObject { } } - // swiftlint:enable function_parameter_count + // Cash App + // swiftlint:disable type_name + + @objc static var cashAppClientId: String? { + Afterpay.cashAppClientId + } + + // Cash App signing + + @objc(APCashAppSigningResult) + public class CashAppSigningResult: NSObject { + internal override init() {} + + static func success(data: CashAppSigningData) -> CashAppSigningResultSuccess { + CashAppSigningResultSuccess(signingData: data) + } + + static func failed(reason: CashAppSigningFailedReason) -> CashAppSigningResultFailed { + CashAppSigningResultFailed(reason: reason) + } + } + + @objc(APCashAppSigningData) + public class CashAppSigningData: NSObject { + @objc public let jwt: String + @objc public let amount: UInt + @objc public let redirectUri: URL + @objc public let merchantId: String + @objc public let brandId: String + + init( + jwt: String, + amount: UInt, + redirectUri: URL, + merchantId: String, + brandId: String + ) { + self.jwt = jwt + self.amount = amount + self.redirectUri = redirectUri + self.merchantId = merchantId + self.brandId = brandId + } + } + + @objc(APCashAppSigningResultSuccess) + public class CashAppSigningResultSuccess: CashAppSigningResult { + @objc public let signingData: CashAppSigningData + + init(signingData: CashAppSigningData) { + self.signingData = signingData + } + } + + @objc(APCashAppSigningResultFailed) + public class CashAppSigningResultFailed: CashAppSigningResult { + @objc public let reason: CashAppSigningFailedReason + + init(reason: CashAppSigningFailedReason) { + self.reason = reason + } + } + + @objc(APCashAppSigningFailedReason) + public class CashAppSigningFailedReason: NSObject { + /// Should not be instantiated, instances should be of type CancellationReasonUserInitiated, + /// CancellationReasonNetworkError or CancellationReasonInvalidURL + internal override init() {} + + static func invalidAmount() -> CashAppSigningFailedReasonInvalidAmount { + CashAppSigningFailedReasonInvalidAmount(()) + } + + static func invalidRedirectUrl() -> CashAppSigningFailedReasonInvalidRedirectUrl { + CashAppSigningFailedReasonInvalidRedirectUrl(()) + } + + static func jwtDecodeNullError() -> CashAppSigningFailedReasonJwtDecodeNullError { + CashAppSigningFailedReasonJwtDecodeNullError(()) + } + + static func responseDecodeError() -> CashAppSigningFailedReasonResponseDecodeError { + CashAppSigningFailedReasonResponseDecodeError(()) + } + + static func jwtDecodeError(error: Error) -> CashAppSigningFailedReasonJwtDecodeError { + CashAppSigningFailedReasonJwtDecodeError(error) + } + + static func httpError(errorCode: Int) -> CashAppSigningFailedReasonHttpError { + CashAppSigningFailedReasonHttpError(errorCode) + } + + static func error(error: Error) -> CashAppSigningFailedReasonError { + CashAppSigningFailedReasonError(error) + } + } + + @objc(APCashAppSigningFailedReasonInvalidAmount) + public class CashAppSigningFailedReasonInvalidAmount: CashAppSigningFailedReason { + init(_ void: ()) {} + } + + @objc(APCashAppSigningFailedReasonInvalidRedirectUrl) + public class CashAppSigningFailedReasonInvalidRedirectUrl: CashAppSigningFailedReason { + init(_ void: ()) {} + } + + @objc(APCashAppSigningFailedReasonJwtDecodeNullError) + public class CashAppSigningFailedReasonJwtDecodeNullError: CashAppSigningFailedReason { + init(_ void: ()) {} + } + + @objc(APCashAppSigningFailedReasonResponseDecodeError) + public class CashAppSigningFailedReasonResponseDecodeError: CashAppSigningFailedReason { + init(_ void: ()) {} + } + + @objc(APCashAppSigningFailedReasonJwtDecodeError) + public class CashAppSigningFailedReasonJwtDecodeError: CashAppSigningFailedReason { + @objc public let error: Error + + init(_ error: Error) { + self.error = error + } + } + + @objc(APCashAppSigningFailedReasonHttpError) + public class CashAppSigningFailedReasonHttpError: CashAppSigningFailedReason { + @objc public let errorCode: Int + + init(_ errorCode: Int) { + self.errorCode = errorCode + } + } + + @objc(APCashAppSigningFailedReasonError) + public class CashAppSigningFailedReasonError: CashAppSigningFailedReason { + @objc public let error: Error + + init(_ error: Error) { + self.error = error + } + } + + @objc + public static func signCashAppOrderToken( + token: String, + completion: @escaping (CashAppSigningResult) -> Void + ) { + Afterpay.signCashAppOrderToken( + token, + completion: { result in + switch result { + case .success(let signingData): + let APSigningData = CashAppSigningData( + jwt: signingData.jwt, + amount: signingData.amount, + redirectUri: signingData.redirectUri, + merchantId: signingData.merchantId, + brandId: signingData.brandId + ) + + return completion(.success(data: APSigningData)) + case .failed(reason: .invalidAmount): + completion(.failed(reason: .invalidAmount())) + + case .failed(reason: .invalidRedirectUrl): + completion(.failed(reason: .invalidRedirectUrl())) + + case .failed(reason: .jwtDecodeNullError): + completion(.failed(reason: .jwtDecodeNullError())) + + case .failed(reason: .responseDecodeError): + completion(.failed(reason: .responseDecodeError())) + + case .failed(reason: .jwtDecodeError(error: let error)): + completion(.failed(reason: .jwtDecodeError(error: error))) + + case .failed(reason: .httpError(errorCode: let errorCode)): + completion(.failed(reason: .httpError(errorCode: errorCode))) + + case .failed(reason: .error(error: let error)): + completion(.failed(reason: .error(error: error))) + } + } + ) + } + + // Cash App signing end + + // Cash App validate order + + @objc(APCashAppValidationResultSuccess) + public class CashAppValidationResultSuccess: CashAppValidationResult { + @objc public let validationData: CashAppValidationData + + init(validationData: CashAppValidationData) { + self.validationData = validationData + } + } + + @objc(APCashAppValidationData) + public class CashAppValidationData: NSObject { + @objc public let cashAppTag: String + @objc public let status: String + @objc public let callbackBaseUrl: String + + init( + cashAppTag: String, + status: String, + callbackBaseUrl: String + ) { + self.cashAppTag = cashAppTag + self.status = status + self.callbackBaseUrl = callbackBaseUrl + } + } + + @objc(APCashAppValidationResult) + public class CashAppValidationResult: NSObject { + internal override init() {} + static func success(data: CashAppValidationData) -> CashAppValidationResultSuccess { + CashAppValidationResultSuccess(validationData: data) + } + + static func failed(reason: CashAppValidationFailedReason) -> CashAppValidationResultFailed { + CashAppValidationResultFailed(reason: reason) + } + } + + @objc(APCashAppValidationResultFailed) + public class CashAppValidationResultFailed: CashAppValidationResult { + @objc public let reason: CashAppValidationFailedReason + + init(reason: CashAppValidationFailedReason) { + self.reason = reason + } + } + + @objc(APCashAppValidationFailedReason) + public class CashAppValidationFailedReason: NSObject { + /// Should not be instantiated, instances should be of type CancellationReasonUserInitiated, + /// CancellationReasonNetworkError or CancellationReasonInvalidURL + internal override init() {} + + static func nilData() -> CashAppValidationFailedReasonNilData { + CashAppValidationFailedReasonNilData(()) + } + + static func responseDecodeError() -> CashAppValidationFailedReasonResponseDecodeError { + CashAppValidationFailedReasonResponseDecodeError(()) + } + + static func unknownError() -> CashAppValidationFailedReasonUnknownError { + CashAppValidationFailedReasonUnknownError(()) + } + + static func invalid() -> CashAppValidationFailedReasonInvalid { + CashAppValidationFailedReasonInvalid(()) + } + + static func httpError(errorCode: Int, message: String) -> CashAppValidationFailedReasonHttpError { + CashAppValidationFailedReasonHttpError(errorCode, message) + } + + static func error(error: Error) -> CashAppValidationFailedReasonError { + CashAppValidationFailedReasonError(error) + } + } + + @objc(APCashAppValidationFailedReasonNilData) + public class CashAppValidationFailedReasonNilData: CashAppValidationFailedReason { + init(_ void: ()) {} + } + + @objc(APCashAppValidationFailedReasonResponseDecodeError) + public class CashAppValidationFailedReasonResponseDecodeError: CashAppValidationFailedReason { + init(_ void: ()) {} + } + + @objc(APCashAppValidationFailedReasonUnknownError) + public class CashAppValidationFailedReasonUnknownError: CashAppValidationFailedReason { + init(_ void: ()) {} + } + + @objc(APCashAppValidationFailedReasonInvalid) + public class CashAppValidationFailedReasonInvalid: CashAppValidationFailedReason { + init(_ void: ()) {} + } + + @objc(APCashAppValidationFailedReasonHttpError) + public class CashAppValidationFailedReasonHttpError: CashAppValidationFailedReason { + @objc public let errorCode: Int + @objc public let message: String + + init(_ errorCode: Int, _ message: String) { + self.errorCode = errorCode + self.message = message + } + } + + @objc(APCashAppValidationFailedReasonError) + public class CashAppValidationFailedReasonError: CashAppValidationFailedReason { + @objc public let error: Error + + init(_ error: Error) { + self.error = error + } + } + + @objc + public static func validateCashAppOrder( + jwt: String, + customerId: String, + grantId: String, + completion: @escaping (CashAppValidationResult) -> Void + ) { + Afterpay.validateCashAppOrder( + jwt: jwt, + customerId: customerId, + grantId: grantId, + completion: { result in + switch result { + + case .success(data: let data): + let APvalidationData = CashAppValidationData( + cashAppTag: data.cashAppTag, + status: data.status, + callbackBaseUrl: data.callbackBaseUrl + ) + + completion(.success(data: APvalidationData)) + + case .failed(reason: .nilData): + completion(.failed(reason: .nilData())) + + case .failed(reason: .responseDecodeError): + completion(.failed(reason: .responseDecodeError())) + + case .failed(reason: .unknownError): + completion(.failed(reason: .unknownError())) + + case .failed(reason: .invalid): + completion(.failed(reason: .invalid())) + + case .failed(reason: .httpError(errorCode: let errorCode, message: let message)): + completion(.failed(reason: .httpError(errorCode: errorCode, message: message))) + + case .failed(reason: .error(error: let error)): + completion(.failed(reason: .error(error: error))) + } + } + ) + } + + // Cash App validate order end + + // swiftlint:enable type_name + // Cash App end + + // swiftlint:enable function_parameter_count + // swiftlint:enable type_body_length } + +// swiftlint:enable file_length