diff --git a/.swiftlint.yml b/.swiftlint.yml index 8f171b417ed..248bcc12359 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -37,6 +37,7 @@ disabled_rules: - reduce_boolean - unused_setter_value - notification_center_detachment + - private_outlet opt_in_rules: - colon @@ -54,7 +55,6 @@ opt_in_rules: - prohibited_super_call - redundant_nil_coalescing - return_arrow_whitespace - - single_test_class - sorted_imports - statement_position - trailing_newline @@ -65,11 +65,17 @@ opt_in_rules: # - weak_delegate # - discouraged_object_literal # - unowned_variable_capture + # - indentation_width + # - single_test_class excluded: + - Package.swift - Pods - Carthage - Tests/installation_tests + - vendor + - build* + - .build identifier_name: allowed_symbols: _ diff --git a/Example/AppClipExample/Project.swift b/Example/AppClipExample/Project.swift index f4bea4db0ac..e463874f969 100644 --- a/Example/AppClipExample/Project.swift +++ b/Example/AppClipExample/Project.swift @@ -126,7 +126,6 @@ let project = Project( "AppClipExampleClipUITests", ]), runAction: .runAction(executable: "AppClipExampleClip") - ) + ), ] ) - diff --git a/Example/AppClipExample/Shared/ApplePayModel.swift b/Example/AppClipExample/Shared/ApplePayModel.swift index eddd5487977..cb102fe3aae 100644 --- a/Example/AppClipExample/Shared/ApplePayModel.swift +++ b/Example/AppClipExample/Shared/ApplePayModel.swift @@ -6,23 +6,23 @@ // import Foundation -import StripeApplePay import PassKit +import StripeApplePay -class MyApplePayBackendModel : NSObject, ObservableObject, ApplePayContextDelegate { +class MyApplePayBackendModel: NSObject, ObservableObject, ApplePayContextDelegate { @Published var paymentStatus: STPApplePayContext.PaymentStatus? @Published var lastPaymentError: Error? func pay() { // Configure a payment request let pr = StripeAPI.paymentRequest(withMerchantIdentifier: "merchant.com.stripetest.appclipexample", country: "US", currency: "USD") - + // You'd generally want to configure at least `.postalAddress` here. // We don't require anything here, as we don't want to enter an address // in CI. pr.requiredShippingContactFields = [] pr.requiredBillingContactFields = [] - + // Configure shipping methods let firstClassShipping = PKShippingMethod(label: "First Class Mail", amount: NSDecimalNumber(string: "10.99")) firstClassShipping.detail = "Arrives in 3-5 days" @@ -30,19 +30,19 @@ class MyApplePayBackendModel : NSObject, ObservableObject, ApplePayContextDelega let rocketRidesShipping = PKShippingMethod(label: "Rocket Rides courier", amount: NSDecimalNumber(string: "10.99")) rocketRidesShipping.detail = "Arrives in 1-2 hours" rocketRidesShipping.identifier = "rocketrides" - pr.shippingMethods = [ + pr.shippingMethods = [ firstClassShipping, - rocketRidesShipping + rocketRidesShipping, ] - + // Build payment summary items // (You'll generally want to configure these based on the selected address and shipping method. pr.paymentSummaryItems = [ PKPaymentSummaryItem(label: "A very nice computer", amount: NSDecimalNumber(string: "19.99")), PKPaymentSummaryItem(label: "Shipping", amount: NSDecimalNumber(string: "10.99")), - PKPaymentSummaryItem(label: "Stripe Computer Shop", amount: NSDecimalNumber(string: "29.99")) + PKPaymentSummaryItem(label: "Stripe Computer Shop", amount: NSDecimalNumber(string: "29.99")), ] - + // Present the Apple Pay Context: let applePayContext = STPApplePayContext(paymentRequest: pr, delegate: self) applePayContext?.presentApplePay() @@ -50,12 +50,12 @@ class MyApplePayBackendModel : NSObject, ObservableObject, ApplePayContextDelega func applePayContext(_ context: STPApplePayContext, didCreatePaymentMethod paymentMethod: StripeAPI.PaymentMethod, paymentInformation: PKPayment, completion: @escaping STPIntentClientSecretCompletionBlock) { // When the Apple Pay sheet is confirmed, create a PaymentIntent on your backend from the provided PKPayment information. - BackendModel.shared.fetchPaymentIntent() { secret in + BackendModel.shared.fetchPaymentIntent { secret in if let clientSecret = secret { // Call the completion block with the PaymentIntent's client secret. completion(clientSecret, nil) } else { - completion(nil, NSError()) + completion(nil, NSError()) // swiftlint:disable:this discouraged_direct_init } } } diff --git a/Example/AppClipExample/Shared/BackendModel.swift b/Example/AppClipExample/Shared/BackendModel.swift index b7535ff0e70..409b952cef1 100644 --- a/Example/AppClipExample/Shared/BackendModel.swift +++ b/Example/AppClipExample/Shared/BackendModel.swift @@ -12,11 +12,11 @@ class BackendModel { // You can replace this with your own backend URL. // Visit https://glitch.com/edit/#!/stripe-integration-tester and click "remix". static let backendAPIURL = URL(string: "https://stripe-integration-tester.glitch.me")! - + static let returnURL = "stp-integration-tester://stripe-redirect" - + public static let shared = BackendModel() - + func fetchPaymentIntent(completion: @escaping (String?) -> Void) { let params = ["integration_method": "Apple Pay"] getAPI(method: "create_pi", params: params) { (json) in @@ -27,7 +27,7 @@ class BackendModel { completion(paymentIntentClientSecret) } } - + func loadPublishableKey(completion: @escaping (String) -> Void) { let params = ["integration_method": "Apple Pay"] getAPI(method: "get_pub_key", params: params) { (json) in @@ -38,22 +38,22 @@ class BackendModel { } } } - - private func getAPI(method: String, params: [String : Any] = [:], completion: @escaping ([String : Any]) -> Void) { + + private func getAPI(method: String, params: [String: Any] = [:], completion: @escaping ([String: Any]) -> Void) { var request = URLRequest(url: Self.backendAPIURL.appendingPathComponent(method)) request.httpMethod = "POST" - + request.httpBody = try! JSONSerialization.data(withJSONObject: params, options: .prettyPrinted) request.addValue("application/json", forHTTPHeaderField: "Content-Type") request.addValue("application/json", forHTTPHeaderField: "Accept") - - let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in + + let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, _, error) in guard let unwrappedData = data, - let json = try? JSONSerialization.jsonObject(with: unwrappedData, options: []) as? [String : Any] else { + let json = try? JSONSerialization.jsonObject(with: unwrappedData, options: []) as? [String: Any] else { if let data = data { print("\(String(decoding: data, as: UTF8.self))") } else { - print("\(error ?? NSError())") + print("\(error ?? NSError())") // swiftlint:disable:this discouraged_direct_init } return } diff --git a/Example/AppClipExample/Shared/ContentView.swift b/Example/AppClipExample/Shared/ContentView.swift index 9005906dd63..8c3a7a0e508 100644 --- a/Example/AppClipExample/Shared/ContentView.swift +++ b/Example/AppClipExample/Shared/ContentView.swift @@ -5,17 +5,17 @@ // Created by David Estes on 1/7/22. // -import SwiftUI import StripeApplePay +import SwiftUI struct ContentView: View { @StateObject var model = MyApplePayBackendModel() @State var ready = false - + var body: some View { if ready { VStack { - PaymentButton() { + PaymentButton { model.pay() } .padding() diff --git a/Example/AppClipExample/Shared/PaymentButton.swift b/Example/AppClipExample/Shared/PaymentButton.swift index ef8436b1206..fa69c5a3f5a 100644 --- a/Example/AppClipExample/Shared/PaymentButton.swift +++ b/Example/AppClipExample/Shared/PaymentButton.swift @@ -11,12 +11,12 @@ Abstract: A button that hosts PKPaymentButton from Apple's Fruta example app. */ -import SwiftUI import PassKit +import SwiftUI struct PaymentButton: View { var action: () -> Void - + var height: CGFloat { #if os(macOS) return 30 @@ -24,7 +24,7 @@ struct PaymentButton: View { return 45 #endif } - + var body: some View { Representable(action: action) .frame(minWidth: 100, maxWidth: 400) @@ -40,23 +40,23 @@ extension PaymentButton { #else typealias ViewRepresentable = NSViewRepresentable #endif - + struct Representable: ViewRepresentable { var action: () -> Void - + init(action: @escaping () -> Void) { self.action = action } - + func makeCoordinator() -> Coordinator { Coordinator(action: action) } - + #if os(iOS) func makeUIView(context: Context) -> UIView { context.coordinator.button } - + func updateUIView(_ rootView: UIView, context: Context) { context.coordinator.action = action } @@ -64,17 +64,17 @@ extension PaymentButton { func makeNSView(context: Context) -> NSView { context.coordinator.button } - + func updateNSView(_ rootView: NSView, context: Context) { context.coordinator.action = action } #endif } - + class Coordinator: NSObject { var action: () -> Void var button = PKPaymentButton(paymentButtonType: .buy, paymentButtonStyle: .automatic) - + init(action: @escaping () -> Void) { self.action = action super.init() @@ -85,7 +85,7 @@ extension PaymentButton { button.target = self #endif } - + @objc func callback(_ sender: Any) { action() diff --git a/Example/Basic Integration/Basic Integration/AppDelegate.swift b/Example/Basic Integration/Basic Integration/AppDelegate.swift index 3e4136e1025..63eabfba897 100644 --- a/Example/Basic Integration/Basic Integration/AppDelegate.swift +++ b/Example/Basic Integration/Basic Integration/AppDelegate.swift @@ -19,14 +19,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ) -> Bool { // Disable hardware keyboards in CI: #if targetEnvironment(simulator) - if (ProcessInfo.processInfo.environment["UITesting"] != nil) { + if ProcessInfo.processInfo.environment["UITesting"] != nil { let setHardwareLayout = NSSelectorFromString("setHardwareLayout:") UITextInputMode.activeInputModes .filter({ $0.responds(to: setHardwareLayout) }) .forEach { $0.perform(setHardwareLayout, with: nil) } } #endif - + let rootVC = BrowseProductsViewController() let navigationController = UINavigationController(rootViewController: rootVC) let window = UIWindow(frame: UIScreen.main.bounds) @@ -54,7 +54,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // This method is where you handle URL opens if you are using univeral link URLs (eg "https://example.com/stripe_ios_callback") func application( - _ application: UIApplication, continue userActivity: NSUserActivity, + _ application: UIApplication, + continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void ) -> Bool { if userActivity.activityType == NSUserActivityTypeBrowsingWeb { diff --git a/Example/Basic Integration/Basic Integration/BrowseProductsViewController.swift b/Example/Basic Integration/Basic Integration/BrowseProductsViewController.swift index 9aec4b9bc2e..b86d8b81adc 100644 --- a/Example/Basic Integration/Basic Integration/BrowseProductsViewController.swift +++ b/Example/Basic Integration/Basic Integration/BrowseProductsViewController.swift @@ -221,7 +221,8 @@ extension BrowseProductsViewController: UICollectionViewDelegateFlowLayout { // MARK: - UICollectionViewDelegateFlowLayout func collectionView( - _ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, + _ collectionView: UICollectionView, + layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath ) -> CGSize { let width = view.frame.size.width * 0.45 diff --git a/Example/Basic Integration/Basic Integration/CheckoutViewController.swift b/Example/Basic Integration/Basic Integration/CheckoutViewController.swift index 752200c97fb..90a88cecd96 100644 --- a/Example/Basic Integration/Basic Integration/CheckoutViewController.swift +++ b/Example/Basic Integration/Basic Integration/CheckoutViewController.swift @@ -20,7 +20,7 @@ class CheckoutViewController: UIViewController { // https://github.com/stripe/example-mobile-backend/tree/v18.1.0, click "Deploy to Heroku", and follow // the instructions (don't worry, it's free). Replace nil on the line below with your // Heroku URL (it looks like https://blazing-sunrise-1234.herokuapp.com ). - var backendBaseURL: String? = nil + var backendBaseURL: String? // 3) Optionally, to enable Apple Pay, follow the instructions at https://stripe.com/docs/apple-pay // to create an Apple Merchant ID. Replace nil on the line below with it (it looks like merchant.com.yourappname). @@ -280,7 +280,8 @@ extension CheckoutViewController: STPPaymentContextDelegate { } } func paymentContext( - _ paymentContext: STPPaymentContext, didCreatePaymentResult paymentResult: STPPaymentResult, + _ paymentContext: STPPaymentContext, + didCreatePaymentResult paymentResult: STPPaymentResult, completion: @escaping STPPaymentStatusBlock ) { // Create the PaymentIntent on the backend @@ -314,7 +315,6 @@ extension CheckoutViewController: STPPaymentContextDelegate { // A real app should retry this request if it was a network error. print("Failed to create a Payment Intent: \(error)") completion(.error, error) - break } } } @@ -393,7 +393,8 @@ extension CheckoutViewController: STPPaymentContextDelegate { // Note: this delegate method is optional. If you do not need to collect a // shipping method from your user, you should not implement this method. func paymentContext( - _ paymentContext: STPPaymentContext, didUpdateShippingAddress address: STPAddress, + _ paymentContext: STPPaymentContext, + didUpdateShippingAddress address: STPAddress, completion: @escaping STPShippingMethodsCompletionBlock ) { let upsGround = PKShippingMethod() diff --git a/Example/Basic Integration/Basic Integration/MyAPIClient.swift b/Example/Basic Integration/Basic Integration/MyAPIClient.swift index 91cfb484336..2ae85db5a41 100644 --- a/Example/Basic Integration/Basic Integration/MyAPIClient.swift +++ b/Example/Basic Integration/Basic Integration/MyAPIClient.swift @@ -33,7 +33,9 @@ class MyAPIClient: NSObject, STPCustomerEphemeralKeyProvider { } func createPaymentIntent( - products: [Product], shippingMethod: PKShippingMethod?, country: String? = nil, + products: [Product], + shippingMethod: PKShippingMethod?, + country: String? = nil, completion: @escaping ((Result) -> Void) ) { let url = self.baseURL.appendingPathComponent("create_payment_intent") @@ -41,7 +43,7 @@ class MyAPIClient: NSObject, STPCustomerEphemeralKeyProvider { "metadata": [ // example-mobile-backend allows passing metadata through to Stripe "payment_request_id": "B3E611D1-5FA1-4410-9CEC-00958A5126CB" - ] + ], ] params["products"] = products.map({ (p) -> String in return p.emoji diff --git a/Example/Basic Integration/BasicIntegrationUITests/BasicIntegrationUITests.swift b/Example/Basic Integration/BasicIntegrationUITests/BasicIntegrationUITests.swift index f7a3c9c695d..4c4060d33ea 100644 --- a/Example/Basic Integration/BasicIntegrationUITests/BasicIntegrationUITests.swift +++ b/Example/Basic Integration/BasicIntegrationUITests/BasicIntegrationUITests.swift @@ -19,7 +19,7 @@ extension XCUIElement { class BasicIntegrationUITests: XCTestCase { var app: XCUIApplication! - + override func setUp() { // In UI tests it is usually best to stop immediately when a failure occurs. continueAfterFailure = false @@ -244,7 +244,7 @@ class BasicIntegrationUITests: XCTestCase { class FrenchAndBelizeBasicIntegrationUITests: XCTestCase { var app: XCUIApplication! - + override func setUp() { // In UI tests it is usually best to stop immediately when a failure occurs. continueAfterFailure = false diff --git a/Example/Basic Integration/Project.swift b/Example/Basic Integration/Project.swift index 5927a0985a5..3bd2df319ef 100644 --- a/Example/Basic Integration/Project.swift +++ b/Example/Basic Integration/Project.swift @@ -69,6 +69,6 @@ let project = Project( ]), testAction: .targets(["BasicIntegrationUITests"]), runAction: .runAction(executable: "Basic Integration") - ) + ), ] ) diff --git a/Example/CardImageVerification Example/CardImageVerification Example/APIClient.swift b/Example/CardImageVerification Example/CardImageVerification Example/APIClient.swift index 541a5a33e9a..3dcf53ee228 100644 --- a/Example/CardImageVerification Example/CardImageVerification Example/APIClient.swift +++ b/Example/CardImageVerification Example/CardImageVerification Example/APIClient.swift @@ -19,7 +19,7 @@ struct APIClient { urlRequest.httpMethod = httpMethod urlRequest.setValue("application/json", forHTTPHeaderField: "Content-type") - URLSession.shared.dataTask(with: urlRequest) { data, response, error in + URLSession.shared.dataTask(with: urlRequest) { data, _, error in DispatchQueue.main.async { guard error == nil, diff --git a/Example/CardImageVerification Example/CardImageVerification Example/AppDelegate.swift b/Example/CardImageVerification Example/CardImageVerification Example/AppDelegate.swift index 601e6399f5c..86a5307b619 100644 --- a/Example/CardImageVerification Example/CardImageVerification Example/AppDelegate.swift +++ b/Example/CardImageVerification Example/CardImageVerification Example/AppDelegate.swift @@ -16,4 +16,3 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return true } } - diff --git a/Example/CardImageVerification Example/CardImageVerification Example/View Controllers/VerificationCardInputViewController.swift b/Example/CardImageVerification Example/CardImageVerification Example/View Controllers/VerificationCardInputViewController.swift index 6865fd24673..a6d8f372673 100644 --- a/Example/CardImageVerification Example/CardImageVerification Example/View Controllers/VerificationCardInputViewController.swift +++ b/Example/CardImageVerification Example/CardImageVerification Example/View Controllers/VerificationCardInputViewController.swift @@ -13,7 +13,7 @@ class VerificationCardInputViewController: UIViewController { @IBOutlet weak var verifyResultLabel: UILabel! @IBOutlet weak var iinTextField: UITextField! @IBOutlet weak var lastFourTextField: UITextField! - + override func viewDidLoad() { super.viewDidLoad() setUpViews() @@ -80,4 +80,3 @@ extension VerificationCardInputViewController: UITextFieldDelegate { return shouldNotResign } } - diff --git a/Example/CardImageVerification Example/CardImageVerification Example/View Controllers/VerificationExplanationViewController.swift b/Example/CardImageVerification Example/CardImageVerification Example/View Controllers/VerificationExplanationViewController.swift index d813009797c..c969fd5766a 100644 --- a/Example/CardImageVerification Example/CardImageVerification Example/View Controllers/VerificationExplanationViewController.swift +++ b/Example/CardImageVerification Example/CardImageVerification Example/View Controllers/VerificationExplanationViewController.swift @@ -5,10 +5,10 @@ // Created by Jaime Park on 11/17/21. // -import UIKit @_spi(STP) import StripeCardScan +import UIKit -///TODO(jaimepark) Internal structs. Find better place +/// TODO(jaimepark) Internal structs. Find better place private struct CIVIntentDetails { let id: String let clientSecret: String @@ -66,7 +66,7 @@ private extension VerificationExplanationViewController { func displayAlert(title: String, message: String) { let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) - let OKAction = UIAlertAction(title: "OK", style: .default, handler: { _ in + let OKAction = UIAlertAction(title: "OK", style: .default, handler: { _ in DispatchQueue.main.async { self.navigationController?.popViewController(animated: true) } @@ -86,7 +86,7 @@ private extension VerificationExplanationViewController { if let last4 = expectedCardViewModel?.last4, !last4.isEmpty { requestJson["expected_card[last4]"] = last4 } - + if let iin = expectedCardViewModel?.iin, !iin.isEmpty { requestJson["expected_card[iin]"] = iin } diff --git a/Example/CardImageVerification Example/CardImageVerification Example/View Controllers/ViewController.swift b/Example/CardImageVerification Example/CardImageVerification Example/View Controllers/ViewController.swift index 02347647ba2..a5f84c20301 100644 --- a/Example/CardImageVerification Example/CardImageVerification Example/View Controllers/ViewController.swift +++ b/Example/CardImageVerification Example/CardImageVerification Example/View Controllers/ViewController.swift @@ -5,13 +5,13 @@ // Created by Jaime Park on 11/17/21. // -import UIKit import StripeCardScan +import UIKit class ViewController: UIViewController { func displayAlert(title: String, message: String) { let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) - let OKAction = UIAlertAction(title: "OK", style: .default, handler: { _ in + let OKAction = UIAlertAction(title: "OK", style: .default, handler: { _ in DispatchQueue.main.async { self.navigationController?.popViewController(animated: true) } @@ -43,4 +43,3 @@ class ViewController: UIViewController { } } } - diff --git a/Example/CardImageVerification Example/CardImageVerification Example/Views/CTAButton.swift b/Example/CardImageVerification Example/CardImageVerification Example/Views/CTAButton.swift index 27ba12f7540..0c3d4ba5644 100644 --- a/Example/CardImageVerification Example/CardImageVerification Example/Views/CTAButton.swift +++ b/Example/CardImageVerification Example/CardImageVerification Example/Views/CTAButton.swift @@ -8,7 +8,7 @@ import Foundation import UIKit -///TODO(jaimepark): Make CTAButton with activity indicator an encapsulated object instead of extending uibutton. +/// TODO(jaimepark): Make CTAButton with activity indicator an encapsulated object instead of extending uibutton. extension UIButton { func updateButtonState(isLoading: Bool) { self.isEnabled = !isLoading diff --git a/Example/CardImageVerification Example/Project.swift b/Example/CardImageVerification Example/Project.swift index 5ce62682b33..0b025efd7e9 100644 --- a/Example/CardImageVerification Example/Project.swift +++ b/Example/CardImageVerification Example/Project.swift @@ -59,7 +59,7 @@ let project = Project( "//StripeCardScan/StripeCardScanTests/Resources/synthetic_test_image.jpg", ], basedOnDependencyAnalysis: false - ) + ), ], dependencies: [ .target(name: "CardImageVerification Example"), @@ -78,6 +78,6 @@ let project = Project( ]), testAction: .targets(["CardImageVerification ExampleUITests"]), runAction: .runAction(executable: "CardImageVerification Example") - ) + ), ] ) diff --git a/Example/FinancialConnections Example/FinancialConnections Example/AppDelegate.swift b/Example/FinancialConnections Example/FinancialConnections Example/AppDelegate.swift index 1146625e183..016cd2da8f5 100644 --- a/Example/FinancialConnections Example/FinancialConnections Example/AppDelegate.swift +++ b/Example/FinancialConnections Example/FinancialConnections Example/AppDelegate.swift @@ -5,8 +5,8 @@ // Created by Vardges Avetisyan on 11/12/21. // -import UIKit import StripeCore +import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { @@ -21,7 +21,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // This method handles opening custom URL schemes (for example, "your-app://stripe-redirect") func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { let stripeHandled = StripeAPI.handleURLCallback(with: url) - if (stripeHandled) { + if stripeHandled { return true } else { // This was not a Stripe url – handle the URL normally as you would @@ -34,7 +34,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { if userActivity.activityType == NSUserActivityTypeBrowsingWeb { if let url = userActivity.webpageURL { let stripeHandled = StripeAPI.handleURLCallback(with: url) - if (stripeHandled) { + if stripeHandled { return true } else { // This was not a Stripe url – handle the URL normally as you would @@ -44,4 +44,3 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return false } } - diff --git a/Example/FinancialConnections Example/FinancialConnections Example/CollectBankAccountTokenViewController.swift b/Example/FinancialConnections Example/FinancialConnections Example/CollectBankAccountTokenViewController.swift index cfbdb409903..c7c7a8864cf 100644 --- a/Example/FinancialConnections Example/FinancialConnections Example/CollectBankAccountTokenViewController.swift +++ b/Example/FinancialConnections Example/FinancialConnections Example/CollectBankAccountTokenViewController.swift @@ -5,8 +5,8 @@ // Created by Vardges Avetisyan on 11/12/21. // -import UIKit import StripeFinancialConnections +import UIKit class CollectBankAccountTokenViewController: UIViewController { @@ -42,7 +42,7 @@ class CollectBankAccountTokenViewController: UIViewController { var urlRequest = URLRequest(url: url) urlRequest.httpMethod = "POST" - let task = session.dataTask(with: urlRequest) { [weak self] data, response, error in + let task = session.dataTask(with: urlRequest) { [weak self] data, _, error in DispatchQueue.main.async { [weak self] in guard let self = self else { return } @@ -115,7 +115,7 @@ class CollectBankAccountTokenViewController: UIViewController { private func displayAlert(_ message: String) { let alertController = UIAlertController(title: "", message: message, preferredStyle: .alert) - let OKAction = UIAlertAction(title: "OK", style: .default) { (action) in + let OKAction = UIAlertAction(title: "OK", style: .default) { (_) in alertController.dismiss(animated: true) { self.dismiss(animated: true, completion: nil) } diff --git a/Example/FinancialConnections Example/FinancialConnections Example/ConnectAccountViewController.swift b/Example/FinancialConnections Example/FinancialConnections Example/ConnectAccountViewController.swift index bb54f6afafc..173436b2749 100644 --- a/Example/FinancialConnections Example/FinancialConnections Example/ConnectAccountViewController.swift +++ b/Example/FinancialConnections Example/FinancialConnections Example/ConnectAccountViewController.swift @@ -5,33 +5,33 @@ // Created by Vardges Avetisyan on 11/12/21. // -import UIKit import StripeFinancialConnections +import UIKit class ConnectAccountViewController: UIViewController { // MARK: - Constants - + let baseURL = "https://stripe-mobile-connections-example.glitch.me" let financialConnectionsEndpoint = "/create_session" // MARK: - IBOutlets - + @IBOutlet weak var connectAccountButton: UIButton! @IBOutlet weak var activityIndicator: UIActivityIndicatorView! // MARK: - Properties - + private var financialConnectionsSheet: FinancialConnectionsSheet? - + // MARK: - IBActions - + @IBAction func didTapConnectAccount(_ sender: Any) { requestFinancialConnectionsSession() } // MARK: - Helpers - + private func requestFinancialConnectionsSession() { // Disable the button while we make the request updateButtonState(isLoading: true) @@ -43,7 +43,7 @@ class ConnectAccountViewController: UIViewController { var urlRequest = URLRequest(url: url) urlRequest.httpMethod = "POST" - let task = session.dataTask(with: urlRequest) { [weak self] data, response, error in + let task = session.dataTask(with: urlRequest) { [weak self] data, _, error in DispatchQueue.main.async { [weak self] in guard let self = self else { return } @@ -63,7 +63,7 @@ class ConnectAccountViewController: UIViewController { } task.resume() } - + private func startFinancialConnections(responseJson: [String: String]) { guard let clientSecret = responseJson["client_secret"] else { assertionFailure("Did not receive a valid client secret.") @@ -97,7 +97,7 @@ class ConnectAccountViewController: UIViewController { // Re-enable button updateButtonState(isLoading: false) } - + private func updateButtonState(isLoading: Bool) { // Re-enable button connectAccountButton.isEnabled = !isLoading @@ -107,10 +107,10 @@ class ConnectAccountViewController: UIViewController { activityIndicator.stopAnimating() } } - + private func displayAlert(_ message: String) { let alertController = UIAlertController(title: "", message: message, preferredStyle: .alert) - let OKAction = UIAlertAction(title: "OK", style: .default) { (action) in + let OKAction = UIAlertAction(title: "OK", style: .default) { (_) in alertController.dismiss(animated: true) { self.dismiss(animated: true, completion: nil) } diff --git a/Example/FinancialConnections Example/FinancialConnections Example/ExampleListViewController.swift b/Example/FinancialConnections Example/FinancialConnections Example/ExampleListViewController.swift index e56d972d703..d67aa1b319b 100644 --- a/Example/FinancialConnections Example/FinancialConnections Example/ExampleListViewController.swift +++ b/Example/FinancialConnections Example/FinancialConnections Example/ExampleListViewController.swift @@ -69,7 +69,6 @@ class ExampleListViewController: UITableViewController { navigationController?.pushViewController(viewController, animated: true) } - } // MARK: - Constants @@ -80,7 +79,6 @@ extension ExampleListViewController { } } - // MARK: - Version Info extension ExampleListViewController { diff --git a/Example/FinancialConnections Example/FinancialConnections Example/Playground/PlaygroundMainView.swift b/Example/FinancialConnections Example/FinancialConnections Example/Playground/PlaygroundMainView.swift index e212f196be2..facfd1f4ad3 100644 --- a/Example/FinancialConnections Example/FinancialConnections Example/Playground/PlaygroundMainView.swift +++ b/Example/FinancialConnections Example/FinancialConnections Example/Playground/PlaygroundMainView.swift @@ -9,9 +9,9 @@ import SwiftUI struct PlaygroundMainView: View { - + @StateObject var viewModel = PlaygroundMainViewModel() - + var body: some View { ZStack { VStack { @@ -30,7 +30,7 @@ struct PlaygroundMainView: View { .font(.caption) .italic() } - + VStack(alignment: .leading, spacing: 4) { Text("How do you want it to look like?") .font(.headline) @@ -55,7 +55,7 @@ struct PlaygroundMainView: View { } else { Toggle("Enable Test Mode", isOn: $viewModel.enableTestMode) } - + Button(action: viewModel.didSelectClearCaches) { Text("Clear Caches") } @@ -73,7 +73,7 @@ struct PlaygroundMainView: View { .buttonStyle(.plain) }.padding() } - + if viewModel.isLoading { ZStack { Color(UIColor.systemGray) @@ -95,5 +95,3 @@ struct PlaygroundMainView_Previews: PreviewProvider { PlaygroundMainView() } } - - diff --git a/Example/FinancialConnections Example/FinancialConnections Example/Playground/PlaygroundMainViewModel.swift b/Example/FinancialConnections Example/FinancialConnections Example/Playground/PlaygroundMainViewModel.swift index 2081ac59f27..da2d3acc932 100644 --- a/Example/FinancialConnections Example/FinancialConnections Example/Playground/PlaygroundMainViewModel.swift +++ b/Example/FinancialConnections Example/FinancialConnections Example/Playground/PlaygroundMainViewModel.swift @@ -7,15 +7,15 @@ // import Foundation -import UIKit import StripeFinancialConnections +import UIKit final class PlaygroundMainViewModel: ObservableObject { - + enum Flow: String, CaseIterable, Identifiable { case data case payments - + var id: String { return rawValue } @@ -25,12 +25,12 @@ final class PlaygroundMainViewModel: ObservableObject { PlaygroundUserDefaults.flow = flow.rawValue } } - + enum NativeSelection: String, CaseIterable, Identifiable { case automatic case web case native - + var id: String { return rawValue } @@ -58,15 +58,15 @@ final class PlaygroundMainViewModel: ObservableObject { PlaygroundUserDefaults.enableAppToApp = enableAppToApp } } - + @Published var enableTestMode: Bool = PlaygroundUserDefaults.enableTestMode { didSet { PlaygroundUserDefaults.enableTestMode = enableTestMode } } - + @Published var isLoading: Bool = false - + init() { self.nativeSelection = { if let enableNative = PlaygroundUserDefaults.enableNative { @@ -76,11 +76,11 @@ final class PlaygroundMainViewModel: ObservableObject { } }() } - + func didSelectShow() { setup() } - + private func setup() { isLoading = true SetupPlayground( @@ -115,7 +115,7 @@ final class PlaygroundMainViewModel: ObservableObject { self?.isLoading = false } } - + func didSelectClearCaches() { URLSession.shared.reset(completionHandler: {}) } @@ -125,16 +125,16 @@ private func SetupPlayground( enableAppToApp: Bool, enableTestMode: Bool, flow: String, - completionHandler: @escaping ([String:String]?) -> Void + completionHandler: @escaping ([String: String]?) -> Void ) { let baseURL = "https://financial-connections-playground-ios.glitch.me" let endpoint = "/setup_playground" let url = URL(string: baseURL + endpoint)! - + var urlRequest = URLRequest(url: url) urlRequest.httpMethod = "POST" urlRequest.httpBody = { - var requestBody: [String:Any] = [:] + var requestBody: [String: Any] = [:] requestBody["enable_test_mode"] = enableTestMode requestBody["enable_app_to_app"] = enableAppToApp requestBody["flow"] = flow @@ -145,7 +145,7 @@ private func SetupPlayground( }() urlRequest.setValue("application/json", forHTTPHeaderField: "Accept") urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") - + URLSession.shared .dataTask( with: urlRequest @@ -170,7 +170,7 @@ private func SetupPlayground( } private func PresentFinancialConnectionsSheet( - setupPlaygroundResponseJSON: [String:String], + setupPlaygroundResponseJSON: [String: String], completionHandler: @escaping (FinancialConnectionsSheet.Result) -> Void ) { guard let clientSecret = setupPlaygroundResponseJSON["client_secret"] else { @@ -179,7 +179,7 @@ private func PresentFinancialConnectionsSheet( guard let publishableKey = setupPlaygroundResponseJSON["publishable_key"] else { fatalError("Did not receive a valid publishable key.") } - + STPAPIClient.shared.publishableKey = publishableKey let financialConnectionsSheet = FinancialConnectionsSheet( diff --git a/Example/FinancialConnections Example/FinancialConnections Example/Playground/PlaygroundViewController.swift b/Example/FinancialConnections Example/FinancialConnections Example/Playground/PlaygroundViewController.swift index f9ea17e13a7..0d898d10fc5 100644 --- a/Example/FinancialConnections Example/FinancialConnections Example/Playground/PlaygroundViewController.swift +++ b/Example/FinancialConnections Example/FinancialConnections Example/Playground/PlaygroundViewController.swift @@ -6,19 +6,19 @@ // import Foundation -import UIKit import SwiftUI +import UIKit final class PlaygroundViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // create let hostingController = UIHostingController(rootView: PlaygroundMainView()) - + // add to subview view.addSubview(hostingController.view) hostingController.didMove(toParent: self) - + // layout hostingController.view.frame = view.bounds hostingController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight] diff --git a/Example/FinancialConnections Example/FinancialConnections Example/Playground/helpers/PlaygroundUserDefaults.swift b/Example/FinancialConnections Example/FinancialConnections Example/Playground/helpers/PlaygroundUserDefaults.swift index d489f36234f..50d658db7d1 100644 --- a/Example/FinancialConnections Example/FinancialConnections Example/Playground/helpers/PlaygroundUserDefaults.swift +++ b/Example/FinancialConnections Example/FinancialConnections Example/Playground/helpers/PlaygroundUserDefaults.swift @@ -9,13 +9,13 @@ import Foundation final class PlaygroundUserDefaults { - + @UserDefault( key: "FINANCIAL_CONNECTIONS_EXAMPLE_APP_FLOW", defaultValue: PlaygroundMainViewModel.Flow.data.rawValue ) static var flow: String - + @UserDefault( key: "FINANCIAL_CONNECTIONS_EXAMPLE_APP_ENABLE_NATIVE", defaultValue: nil @@ -58,7 +58,7 @@ struct UserDefault { } } -fileprivate protocol OptionalValue { +private protocol OptionalValue { var isNil: Bool { get } } diff --git a/Example/FinancialConnections Example/FinancialConnections Example/Playground/helpers/UIAlertController+Extensions.swift b/Example/FinancialConnections Example/FinancialConnections Example/Playground/helpers/UIAlertController+Extensions.swift index 24b6cb62d04..0a391be4611 100644 --- a/Example/FinancialConnections Example/FinancialConnections Example/Playground/helpers/UIAlertController+Extensions.swift +++ b/Example/FinancialConnections Example/FinancialConnections Example/Playground/helpers/UIAlertController+Extensions.swift @@ -10,7 +10,7 @@ import Foundation import UIKit extension UIAlertController { - + static func showAlert( title: String? = nil, message: String? = nil diff --git a/Example/FinancialConnections Example/FinancialConnections Example/Playground/helpers/UIViewController+Extensions.swift b/Example/FinancialConnections Example/FinancialConnections Example/Playground/helpers/UIViewController+Extensions.swift index b3b8ff922a4..024ba289bcd 100644 --- a/Example/FinancialConnections Example/FinancialConnections Example/Playground/helpers/UIViewController+Extensions.swift +++ b/Example/FinancialConnections Example/FinancialConnections Example/Playground/helpers/UIViewController+Extensions.swift @@ -25,7 +25,7 @@ extension UIViewController { } extension UIApplication { - + @available(iOSApplicationExtension, unavailable) fileprivate var customKeyWindow: UIWindow? { let foregroundActiveWindow = connectedScenes @@ -33,11 +33,11 @@ extension UIApplication { .first(where: { $0 is UIWindowScene }) .flatMap({ ($0 as? UIWindowScene) })?.windows .first(where: \.isKeyWindow) - + if let foregroundActiveWindow = foregroundActiveWindow { return foregroundActiveWindow } - + // There are scenarios (ex. presenting from a notification) when // no scenes are `foregroundActive` so here we ignore the parameter return connectedScenes diff --git a/Example/FinancialConnections Example/FinancialConnectionsUITests/FinancialConnectionsUITests.swift b/Example/FinancialConnections Example/FinancialConnectionsUITests/FinancialConnectionsUITests.swift index 6c1ae049230..f98d7f46eb0 100644 --- a/Example/FinancialConnections Example/FinancialConnectionsUITests/FinancialConnectionsUITests.swift +++ b/Example/FinancialConnections Example/FinancialConnectionsUITests/FinancialConnectionsUITests.swift @@ -26,141 +26,141 @@ final class FinancialConnectionsUITests: XCTestCase { func testDataTestModeOAuthNativeAuthFlow() throws { let app = XCUIApplication() app.launch() - + let playgroundCell = app.tables.staticTexts["Playground"] XCTAssertTrue(playgroundCell.waitForExistence(timeout: 60.0)) playgroundCell.tap() - + let dataSegmentPickerButton = app.collectionViews.buttons["Data"] XCTAssertTrue(dataSegmentPickerButton.waitForExistence(timeout: 60.0)) dataSegmentPickerButton.tap() - + let nativeSegmentPickerButton = app.collectionViews.buttons["Native"] XCTAssertTrue(nativeSegmentPickerButton.waitForExistence(timeout: 60.0)) nativeSegmentPickerButton.tap() - + let enableTestModeSwitch = app.collectionViews.switches["Enable Test Mode"] XCTAssertTrue(enableTestModeSwitch.waitForExistence(timeout: 60.0)) if (enableTestModeSwitch.value as? String) == "0" { enableTestModeSwitch.tap() } - + let showAuthFlowButton = app.buttons["Show Auth Flow"] XCTAssertTrue(showAuthFlowButton.waitForExistence(timeout: 60.0)) showAuthFlowButton.tap() - + let consentAgreeButton = app.buttons["Agree"] XCTAssertTrue(consentAgreeButton.waitForExistence(timeout: 120.0)) // glitch app can take time to lload consentAgreeButton.tap() - + let featuredLegacyTestInstitution = app.collectionViews.staticTexts["Test OAuth Institution"] XCTAssertTrue(featuredLegacyTestInstitution.waitForExistence(timeout: 60.0)) featuredLegacyTestInstitution.tap() - + let prepaneContinueButton = app.buttons["Continue"] XCTAssertTrue(prepaneContinueButton.waitForExistence(timeout: 60.0)) prepaneContinueButton.tap() - + let accountPickerLinkAccountsButton = app.buttons["Link accounts"] XCTAssertTrue(accountPickerLinkAccountsButton.waitForExistence(timeout: 120.0)) // wait for accounts to fetch accountPickerLinkAccountsButton.tap() - + let successPaneDoneButton = app.buttons["Done"] XCTAssertTrue(successPaneDoneButton.waitForExistence(timeout: 120.0)) // wait for accounts to link successPaneDoneButton.tap() - + let playgroundSuccessAlert = app.alerts["Success"] XCTAssertTrue(playgroundSuccessAlert.waitForExistence(timeout: 60.0)) - + // ensure alert body contains "Stripe Bank" (AKA one bank is linked) XCTAssert(playgroundSuccessAlert.staticTexts.containing(NSPredicate(format: "label CONTAINS 'StripeBank'")).firstMatch.exists) } - + func testPaymentTestModeLegacyNativeAuthFlow() throws { let app = XCUIApplication() app.launch() - + let playgroundCell = app.tables.staticTexts["Playground"] XCTAssertTrue(playgroundCell.waitForExistence(timeout: 60.0)) playgroundCell.tap() - + let dataSegmentPickerButton = app.collectionViews.buttons["Payments"] XCTAssertTrue(dataSegmentPickerButton.waitForExistence(timeout: 60.0)) dataSegmentPickerButton.tap() - + let nativeSegmentPickerButton = app.collectionViews.buttons["Native"] XCTAssertTrue(nativeSegmentPickerButton.waitForExistence(timeout: 60.0)) nativeSegmentPickerButton.tap() - + let enableTestModeSwitch = app.collectionViews.switches["Enable Test Mode"] XCTAssertTrue(enableTestModeSwitch.waitForExistence(timeout: 60.0)) if (enableTestModeSwitch.value as? String) == "0" { enableTestModeSwitch.tap() } - + let showAuthFlowButton = app.buttons["Show Auth Flow"] XCTAssertTrue(showAuthFlowButton.waitForExistence(timeout: 60.0)) showAuthFlowButton.tap() - + let consentAgreeButton = app.buttons["Agree"] XCTAssertTrue(consentAgreeButton.waitForExistence(timeout: 120.0)) // glitch app can take time to lload consentAgreeButton.tap() - + let featuredLegacyTestInstitution = app.collectionViews.staticTexts["Test Institution"] XCTAssertTrue(featuredLegacyTestInstitution.waitForExistence(timeout: 60.0)) featuredLegacyTestInstitution.tap() - + let successAccountRow = app.scrollViews.staticTexts["Success"] XCTAssertTrue(successAccountRow.waitForExistence(timeout: 60.0)) successAccountRow.tap() - + let accountPickerLinkAccountButton = app.buttons["Link account"] XCTAssertTrue(accountPickerLinkAccountButton.waitForExistence(timeout: 120.0)) // wait for accounts to fetch accountPickerLinkAccountButton.tap() - + let successPaneDoneButton = app.buttons["Done"] XCTAssertTrue(successPaneDoneButton.waitForExistence(timeout: 120.0)) // wait for accounts to link successPaneDoneButton.tap() - + let playgroundSuccessAlert = app.alerts["Success"] XCTAssertTrue(playgroundSuccessAlert.waitForExistence(timeout: 60.0)) - + // ensure alert body contains "Stripe Bank" (AKA one bank is linked) XCTAssert(playgroundSuccessAlert.staticTexts.containing(NSPredicate(format: "label CONTAINS 'StripeBank'")).firstMatch.exists) } - + // note that this does NOT complete the Auth Flow, but its a decent check on // whether live mode is ~working func testDataLiveModeOAuthNativeAuthFlow() throws { let app = XCUIApplication() app.launch() - + let playgroundCell = app.tables.staticTexts["Playground"] XCTAssertTrue(playgroundCell.waitForExistence(timeout: 60.0)) playgroundCell.tap() - + let dataSegmentPickerButton = app.collectionViews.buttons["Data"] XCTAssertTrue(dataSegmentPickerButton.waitForExistence(timeout: 60.0)) dataSegmentPickerButton.tap() - + let nativeSegmentPickerButton = app.collectionViews.buttons["Native"] XCTAssertTrue(nativeSegmentPickerButton.waitForExistence(timeout: 60.0)) nativeSegmentPickerButton.tap() - + let enableTestModeSwitch = app.collectionViews.switches["Enable Test Mode"] XCTAssertTrue(enableTestModeSwitch.waitForExistence(timeout: 60.0)) if (enableTestModeSwitch.value as? String) == "1" { enableTestModeSwitch.tap() } - + let showAuthFlowButton = app.buttons["Show Auth Flow"] XCTAssertTrue(showAuthFlowButton.waitForExistence(timeout: 60.0)) showAuthFlowButton.tap() - + let consentAgreeButton = app.buttons["Agree"] XCTAssertTrue(consentAgreeButton.waitForExistence(timeout: 120.0)) // glitch app can take time to lload consentAgreeButton.tap() - + // find + tap an institution; we add extra institutions in case // they don't get featured let institutionButton: XCUIElement? @@ -190,29 +190,29 @@ final class FinancialConnectionsUITests: XCTestCase { return } institutionButton.tap() - + let prepaneContinueButton = app.buttons["Continue"] XCTAssertTrue(prepaneContinueButton.waitForExistence(timeout: 60.0)) prepaneContinueButton.tap() - + // check that the WebView loaded let institutionWebViewText = app.webViews .staticTexts .containing(NSPredicate(format: "label CONTAINS '\(institutionTextInWebView)'")) .firstMatch XCTAssertTrue(institutionWebViewText.waitForExistence(timeout: 120.0)) - + let secureWebViewCancelButton = app.buttons["Cancel"] XCTAssertTrue(secureWebViewCancelButton.waitForExistence(timeout: 60.0)) secureWebViewCancelButton.tap() - + let navigationBarCloseButton = app.navigationBars.buttons["close"] XCTAssertTrue(navigationBarCloseButton.waitForExistence(timeout: 60.0)) navigationBarCloseButton.tap() - + let cancelAlert = app.alerts["Are you sure you want to cancel?"] XCTAssertTrue(cancelAlert.waitForExistence(timeout: 60.0)) - + let cancelAlertButon = app.alerts.buttons["Yes, cancel"] XCTAssertTrue(cancelAlertButon.waitForExistence(timeout: 60.0)) cancelAlertButon.tap() @@ -220,39 +220,39 @@ final class FinancialConnectionsUITests: XCTestCase { let playgroundCancelAlert = app.alerts["Cancelled"] XCTAssertTrue(playgroundCancelAlert.waitForExistence(timeout: 60.0)) } - + // note that this does NOT complete the Auth Flow, but its a decent check on // whether live mode is ~working func testDataLiveModeOAuthWebAuthFlow() throws { let app = XCUIApplication() app.launch() - + let playgroundCell = app.tables.staticTexts["Playground"] XCTAssertTrue(playgroundCell.waitForExistence(timeout: 60.0)) playgroundCell.tap() - + let dataSegmentPickerButton = app.collectionViews.buttons["Data"] XCTAssertTrue(dataSegmentPickerButton.waitForExistence(timeout: 60.0)) dataSegmentPickerButton.tap() - + let nativeSegmentPickerButton = app.collectionViews.buttons["Web"] XCTAssertTrue(nativeSegmentPickerButton.waitForExistence(timeout: 60.0)) nativeSegmentPickerButton.tap() - + let enableTestModeSwitch = app.collectionViews.switches["Enable Test Mode"] XCTAssertTrue(enableTestModeSwitch.waitForExistence(timeout: 60.0)) if (enableTestModeSwitch.value as? String) == "1" { enableTestModeSwitch.tap() } - + let showAuthFlowButton = app.buttons["Show Auth Flow"] XCTAssertTrue(showAuthFlowButton.waitForExistence(timeout: 60.0)) showAuthFlowButton.tap() - + let consentAgreeButton = app.webViews.buttons["Agree"] XCTAssertTrue(consentAgreeButton.waitForExistence(timeout: 120.0)) // glitch app can take time to load consentAgreeButton.tap() - + // find + tap an institution; we add extra institutions in case // they don't get featured let institutionButton: XCUIElement? @@ -282,22 +282,22 @@ final class FinancialConnectionsUITests: XCTestCase { return } institutionButton.tap() - + let prepaneContinueButton = app.webViews.buttons["Continue"] XCTAssertTrue(prepaneContinueButton.waitForExistence(timeout: 60.0)) prepaneContinueButton.tap() - + // check that the WebView loaded let institutionWebViewText = app.webViews .staticTexts .containing(NSPredicate(format: "label CONTAINS '\(institutionTextInWebView)'")) .firstMatch XCTAssertTrue(institutionWebViewText.waitForExistence(timeout: 120.0)) - + let secureWebViewCancelButton = app.buttons["Cancel"] XCTAssertTrue(secureWebViewCancelButton.waitForExistence(timeout: 60.0)) secureWebViewCancelButton.tap() - + let playgroundCancelAlert = app.alerts["Cancelled"] XCTAssertTrue(playgroundCancelAlert.waitForExistence(timeout: 60.0)) } @@ -310,7 +310,7 @@ extension XCTestCase { } extension XCUIElement { - + fileprivate func wait( until expression: @escaping (XCUIElement) -> Bool, timeout: TimeInterval diff --git a/Example/FinancialConnections Example/Project.swift b/Example/FinancialConnections Example/Project.swift index 77380334e9f..f182710b162 100644 --- a/Example/FinancialConnections Example/Project.swift +++ b/Example/FinancialConnections Example/Project.swift @@ -61,7 +61,7 @@ let project = Project( settings: .stripeTargetSettings( baseXcconfigFilePath: "BuildConfigurations/FinancialConnectionsUITests" ) - ) + ), ], schemes: [ Scheme( @@ -79,6 +79,6 @@ let project = Project( expandVariableFromTarget: "FinancialConnections Example" ), runAction: .runAction(executable: "FinancialConnections Example") - ) + ), ] ) diff --git a/Example/IdentityVerification Example/IdentityVerification Example/AppDelegate.swift b/Example/IdentityVerification Example/IdentityVerification Example/AppDelegate.swift index 621ad2f5c93..b324b3c35bb 100644 --- a/Example/IdentityVerification Example/IdentityVerification Example/AppDelegate.swift +++ b/Example/IdentityVerification Example/IdentityVerification Example/AppDelegate.swift @@ -10,8 +10,6 @@ import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { - - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true @@ -31,6 +29,4 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Use this method to release any resources that were specific to the discarded scenes, as they will not return. } - } - diff --git a/Example/IdentityVerification Example/IdentityVerification Example/ExampleVerificationViewController.swift b/Example/IdentityVerification Example/IdentityVerification Example/ExampleVerificationViewController.swift index 152dfeb861d..06454875e6c 100644 --- a/Example/IdentityVerification Example/IdentityVerification Example/ExampleVerificationViewController.swift +++ b/Example/IdentityVerification Example/IdentityVerification Example/ExampleVerificationViewController.swift @@ -5,8 +5,8 @@ // Created by Mel Ludowise on 3/3/21. // -import UIKit import StripeIdentity +import UIKit @available(iOS 14.3, *) class ExampleVerificationViewController: UIViewController { @@ -45,7 +45,7 @@ class ExampleVerificationViewController: UIViewController { urlRequest.httpMethod = "POST" urlRequest.setValue("application/json", forHTTPHeaderField: "Content-type") - let task = session.dataTask(with: urlRequest) { [weak self] data, response, error in + let task = session.dataTask(with: urlRequest) { [weak self] data, _, error in DispatchQueue.main.async { [weak self] in // Re-enable button self?.updateButtonState(isLoading: false) @@ -98,7 +98,7 @@ class ExampleVerificationViewController: UIViewController { func displayAlert(_ message: String) { let alertController = UIAlertController(title: "", message: message, preferredStyle: .alert) - let OKAction = UIAlertAction(title: "OK", style: .default) { (action) in + let OKAction = UIAlertAction(title: "OK", style: .default) { (_) in alertController.dismiss(animated: true) { self.dismiss(animated: true, completion: nil) } @@ -107,7 +107,6 @@ class ExampleVerificationViewController: UIViewController { present(alertController, animated: true, completion: nil) } - // MARK: - Customize navigation bar override func viewWillAppear(_ animated: Bool) { diff --git a/Example/IdentityVerification Example/IdentityVerification Example/ExampleVerificationViewControllerNativeUI.swift b/Example/IdentityVerification Example/IdentityVerification Example/ExampleVerificationViewControllerNativeUI.swift index d2428818f4e..88428d5da5b 100644 --- a/Example/IdentityVerification Example/IdentityVerification Example/ExampleVerificationViewControllerNativeUI.swift +++ b/Example/IdentityVerification Example/IdentityVerification Example/ExampleVerificationViewControllerNativeUI.swift @@ -5,8 +5,8 @@ // Created by Mel Ludowise on 3/3/21. // -import UIKit import StripeIdentity +import UIKit /** Example view controller that presents an IdentityVerificationSheet using native @@ -54,7 +54,7 @@ class ExampleVerificationViewControllerNativeUI: UIViewController { urlRequest.httpMethod = "POST" urlRequest.setValue("application/json", forHTTPHeaderField: "Content-type") - let task = session.dataTask(with: urlRequest) { [weak self] data, response, error in + let task = session.dataTask(with: urlRequest) { [weak self] data, _, error in DispatchQueue.main.async { [weak self] in // Re-enable button self?.updateButtonState(isLoading: false) @@ -117,7 +117,7 @@ class ExampleVerificationViewControllerNativeUI: UIViewController { func displayAlert(_ message: String) { let alertController = UIAlertController(title: "", message: message, preferredStyle: .alert) - let OKAction = UIAlertAction(title: "OK", style: .default) { (action) in + let OKAction = UIAlertAction(title: "OK", style: .default) { (_) in alertController.dismiss(animated: true) { self.dismiss(animated: true, completion: nil) } @@ -126,7 +126,6 @@ class ExampleVerificationViewControllerNativeUI: UIViewController { present(alertController, animated: true, completion: nil) } - // MARK: - Customize fonts and colors override func viewWillAppear(_ animated: Bool) { diff --git a/Example/IdentityVerification Example/IdentityVerification Example/PlaygroundViewController.swift b/Example/IdentityVerification Example/IdentityVerification Example/PlaygroundViewController.swift index 466e3f893fd..7beea14ded1 100644 --- a/Example/IdentityVerification Example/IdentityVerification Example/PlaygroundViewController.swift +++ b/Example/IdentityVerification Example/IdentityVerification Example/PlaygroundViewController.swift @@ -108,8 +108,8 @@ class PlaygroundViewController: UIViewController { "allowed_types": documentAllowedTypes.map { $0.rawValue }, "require_id_number": requireIDNumberSwitch.isOn, "require_live_capture": requireLiveCaptureSwitch.isOn, - "require_matching_selfie": requireSelfieSwitch.isOn - ] + "require_matching_selfie": requireSelfieSwitch.isOn, + ], ] requestDict["options"] = options } @@ -120,7 +120,7 @@ class PlaygroundViewController: UIViewController { urlRequest.setValue("application/json", forHTTPHeaderField: "Content-type") urlRequest.httpBody = requestJson - let task = session.dataTask(with: urlRequest) { [weak self] data, response, error in + let task = session.dataTask(with: urlRequest) { [weak self] data, _, error in DispatchQueue.main.async { [weak self] in // Re-enable button self?.updateButtonState(isLoading: false) @@ -217,7 +217,7 @@ class PlaygroundViewController: UIViewController { textField.inputView = UIView() } } - let OKAction = UIAlertAction(title: "OK", style: .default) { (action) in + let OKAction = UIAlertAction(title: "OK", style: .default) { (_) in alertController.dismiss(animated: true) { self.dismiss(animated: true, completion: nil) } @@ -238,7 +238,6 @@ class PlaygroundViewController: UIViewController { #endif } - @IBAction func didChangeVerificationType(_ sender: Any) { switch verificationType { case .document: diff --git a/Example/IdentityVerification Example/IdentityVerification Example/SceneDelegate.swift b/Example/IdentityVerification Example/IdentityVerification Example/SceneDelegate.swift index 984155a537b..73625d3b380 100644 --- a/Example/IdentityVerification Example/IdentityVerification Example/SceneDelegate.swift +++ b/Example/IdentityVerification Example/IdentityVerification Example/SceneDelegate.swift @@ -15,7 +15,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). - guard let _ = (scene as? UIWindowScene) else { return } + guard (scene as? UIWindowScene) != nil else { return } } func sceneDidDisconnect(_ scene: UIScene) { @@ -46,6 +46,4 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // to restore the scene back to its current state. } - } - diff --git a/Example/IdentityVerification Example/Project.swift b/Example/IdentityVerification Example/Project.swift index ecc07447f90..56e75fb233d 100644 --- a/Example/IdentityVerification Example/Project.swift +++ b/Example/IdentityVerification Example/Project.swift @@ -47,6 +47,6 @@ let project = Project( name: "IdentityVerification Example", buildAction: .buildAction(targets: ["IdentityVerification Example"]), runAction: .runAction(executable: "IdentityVerification Example") - ) + ), ] ) diff --git a/Example/Non-Card Payment Examples/Non-Card Payment Examples/AffirmExampleViewController.swift b/Example/Non-Card Payment Examples/Non-Card Payment Examples/AffirmExampleViewController.swift index 1d949b08714..7a895ea11e0 100644 --- a/Example/Non-Card Payment Examples/Non-Card Payment Examples/AffirmExampleViewController.swift +++ b/Example/Non-Card Payment Examples/Non-Card Payment Examples/AffirmExampleViewController.swift @@ -65,7 +65,7 @@ extension AffirmExampleViewController { @objc func pay() { // 1. Create an Affirm PaymentIntent MyAPIClient.shared().createPaymentIntent( - completion: { (result, clientSecret, error) in + completion: { (_, clientSecret, error) in guard let clientSecret = clientSecret else { self.delegate?.exampleViewController(self, didFinishWithError: error) return @@ -86,11 +86,11 @@ extension AffirmExampleViewController { metadata: [:]) paymentIntentParams.returnURL = "payments-example://safepay/" paymentIntentParams.shipping = shippingDetailsParam - + // 3. Confirm payment STPPaymentHandler.shared().confirmPayment( paymentIntentParams, with: self - ) { (status, intent, error) in + ) { (status, _, error) in switch status { case .canceled: self.delegate?.exampleViewController( diff --git a/Example/Non-Card Payment Examples/Non-Card Payment Examples/AfterpayClearpayExampleViewController.swift b/Example/Non-Card Payment Examples/Non-Card Payment Examples/AfterpayClearpayExampleViewController.swift index dae1a14da99..6c762af3e3b 100644 --- a/Example/Non-Card Payment Examples/Non-Card Payment Examples/AfterpayClearpayExampleViewController.swift +++ b/Example/Non-Card Payment Examples/Non-Card Payment Examples/AfterpayClearpayExampleViewController.swift @@ -66,7 +66,7 @@ extension AfterpayClearpayExampleViewController { @objc func pay() { // 1. Create an Afterpay Clearpay PaymentIntent MyAPIClient.shared().createPaymentIntent( - completion: { (result, clientSecret, error) in + completion: { (_, clientSecret, error) in guard let clientSecret = clientSecret else { self.delegate?.exampleViewController(self, didFinishWithError: error) return @@ -100,7 +100,7 @@ extension AfterpayClearpayExampleViewController { STPPaymentHandler.shared().confirmPayment( withParams: paymentIntentParams, authenticationContext: self - ) { (status, intent, error) in + ) { (status, _, error) in switch status { case .canceled: self.delegate?.exampleViewController( diff --git a/Example/Non-Card Payment Examples/Non-Card Payment Examples/BoletoExampleViewController.swift b/Example/Non-Card Payment Examples/Non-Card Payment Examples/BoletoExampleViewController.swift index 471bcb76e4b..24d12941fb1 100644 --- a/Example/Non-Card Payment Examples/Non-Card Payment Examples/BoletoExampleViewController.swift +++ b/Example/Non-Card Payment Examples/Non-Card Payment Examples/BoletoExampleViewController.swift @@ -6,8 +6,8 @@ // Copyright © 2021 Stripe. All rights reserved. // -import UIKit import Stripe +import UIKit class BoletoExampleViewController: UIViewController { @@ -111,7 +111,7 @@ class BoletoExampleViewController: UIViewController { ("Address", addressField), ("City", cityField), ("State", stateField), - ("Postal code", postalCodeField) + ("Postal code", postalCodeField), ] private let scrollView: UIScrollView = { @@ -182,7 +182,7 @@ class BoletoExampleViewController: UIViewController { stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor), stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor), stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor), - stackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor) + stackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor), ]) for (title, field) in allFields { @@ -200,7 +200,7 @@ class BoletoExampleViewController: UIViewController { NSLayoutConstraint.activate([ fieldContainer.leadingAnchor.constraint(equalTo: stackView.layoutMarginsGuide.leadingAnchor), - fieldContainer.trailingAnchor.constraint(equalTo: stackView.layoutMarginsGuide.trailingAnchor) + fieldContainer.trailingAnchor.constraint(equalTo: stackView.layoutMarginsGuide.trailingAnchor), ]) } diff --git a/Example/Non-Card Payment Examples/Non-Card Payment Examples/KlarnaExampleViewController.swift b/Example/Non-Card Payment Examples/Non-Card Payment Examples/KlarnaExampleViewController.swift index e59f3e1e812..390c2657c29 100644 --- a/Example/Non-Card Payment Examples/Non-Card Payment Examples/KlarnaExampleViewController.swift +++ b/Example/Non-Card Payment Examples/Non-Card Payment Examples/KlarnaExampleViewController.swift @@ -65,7 +65,7 @@ extension KlarnaExampleViewController { @objc func pay() { // 1. Create an Klarna PaymentIntent MyAPIClient.shared().createPaymentIntent( - completion: { (result, clientSecret, error) in + completion: { (_, clientSecret, error) in guard let clientSecret = clientSecret else { self.delegate?.exampleViewController(self, didFinishWithError: error) return @@ -89,7 +89,7 @@ extension KlarnaExampleViewController { STPPaymentHandler.shared().confirmPayment( paymentIntentParams, with: self - ) { (status, intent, error) in + ) { (status, _, error) in switch status { case .canceled: self.delegate?.exampleViewController( diff --git a/Example/Non-Card Payment Examples/Non-Card Payment Examples/KlarnaSourcesExampleViewController.swift b/Example/Non-Card Payment Examples/Non-Card Payment Examples/KlarnaSourcesExampleViewController.swift index 001b60fd6a4..68211943ed4 100644 --- a/Example/Non-Card Payment Examples/Non-Card Payment Examples/KlarnaSourcesExampleViewController.swift +++ b/Example/Non-Card Payment Examples/Non-Card Payment Examples/KlarnaSourcesExampleViewController.swift @@ -142,7 +142,7 @@ extension KlarnaSourcesExampleViewController { // Klarna will request additional information from the customer during checkout. let items = [ STPKlarnaLineItem( - itemType: .SKU, itemDescription: "Mysterious Item", quantity: 1, totalAmount: 10000) + itemType: .SKU, itemDescription: "Mysterious Item", quantity: 1, totalAmount: 10000), ] let sourceParams = STPSourceParams.klarnaParams( diff --git a/Example/Non-Card Payment Examples/Non-Card Payment Examples/USBankAccountConnectionsExampleViewController.swift b/Example/Non-Card Payment Examples/Non-Card Payment Examples/USBankAccountConnectionsExampleViewController.swift index 9682d0ed660..d2679db951f 100644 --- a/Example/Non-Card Payment Examples/Non-Card Payment Examples/USBankAccountConnectionsExampleViewController.swift +++ b/Example/Non-Card Payment Examples/Non-Card Payment Examples/USBankAccountConnectionsExampleViewController.swift @@ -8,9 +8,9 @@ import UIKit +import StripeApplePay import StripeFinancialConnections import StripePayments -import StripeApplePay class USBankAccountFinancialConnectionsExampleViewController: UIViewController { @objc weak var delegate: ExampleViewControllerDelegate? @@ -26,7 +26,7 @@ class USBankAccountFinancialConnectionsExampleViewController: UIViewController { } let bankAccountCollector = STPBankAccountCollector() - var clientSecret: String? = nil + var clientSecret: String? // UI lazy var nameField: UITextField = { @@ -117,7 +117,6 @@ class USBankAccountFinancialConnectionsExampleViewController: UIViewController { mandateLabel.leadingAnchor.constraint(equalToSystemSpacingAfter: view.safeAreaLayoutGuide.leadingAnchor, multiplier: 1), view.safeAreaLayoutGuide.trailingAnchor.constraint(equalToSystemSpacingAfter: mandateLabel.trailingAnchor, multiplier: 1), - payButton.centerXAnchor.constraint(equalTo: view.centerXAnchor), payButton.topAnchor.constraint(equalToSystemSpacingBelow: mandateLabel.bottomAnchor, multiplier: 1), @@ -146,7 +145,7 @@ class USBankAccountFinancialConnectionsExampleViewController: UIViewController { inProgress = true // 1. Create a US Bank Account PaymentIntent MyAPIClient.shared().createPaymentIntent( - completion: { [self] (result, clientSecret, error) in + completion: { [self] (_, clientSecret, error) in guard let clientSecret = clientSecret else { self.delegate?.exampleViewController(self, didFinishWithError: error) return @@ -196,7 +195,7 @@ extension USBankAccountFinancialConnectionsExampleViewController { paymentIntentParams.returnURL = "payments-example://stripe/" STPPaymentHandler.shared().confirmPayment( paymentIntentParams, with: self - ) { (status, intent, error) in + ) { (status, _, error) in switch status { case .canceled: self.delegate?.exampleViewController( diff --git a/Example/Non-Card Payment Examples/Non-Card Payment Examples/USBankAccountExampleViewController.swift b/Example/Non-Card Payment Examples/Non-Card Payment Examples/USBankAccountExampleViewController.swift index eb2539e80c7..ec89676b43e 100644 --- a/Example/Non-Card Payment Examples/Non-Card Payment Examples/USBankAccountExampleViewController.swift +++ b/Example/Non-Card Payment Examples/Non-Card Payment Examples/USBankAccountExampleViewController.swift @@ -53,12 +53,14 @@ class USBankAccountExampleViewController: UIViewController { lazy var accountTypeSelector: UISegmentedControl = UISegmentedControl(items: ["checking", "savings"]) lazy var accountHolderTypeSelector: UISegmentedControl = UISegmentedControl(items: ["individual", "company"]) lazy var fieldsStackView: UIStackView = { - let stackView = UIStackView(arrangedSubviews: [nameField, - emailField, - accountNumberField, - routingNumberField, - accountTypeSelector, - accountHolderTypeSelector]) + let stackView = UIStackView(arrangedSubviews: [ + nameField, + emailField, + accountNumberField, + routingNumberField, + accountTypeSelector, + accountHolderTypeSelector, + ]) stackView.axis = .vertical stackView.alignment = .leading stackView.spacing = 4 @@ -84,13 +86,13 @@ class USBankAccountExampleViewController: UIViewController { } fieldsStackView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(fieldsStackView) - + let mandateLabel = UILabel() mandateLabel.numberOfLines = 0 mandateLabel.font = .preferredFont(forTextStyle: .caption1) mandateLabel.text = """ By clicking Pay with US Bank Account, you authorize Non-Card Payment Examples to debit the bank account specified above for any amount owed for charges arising from your use of Non-Card Payment Examples’ services and/or purchase of products from Non-Card Payment Examples, pursuant to Non-Card Payment Examples’ website and terms, until this authorization is revoked. You may amend or cancel this authorization at any time by providing notice to Non-Card Payment Examples with 30 (thirty) days notice. - + If you use Non-Card Payment Examples’ services or purchase additional products periodically pursuant to Non-Card Payment Examples’ terms, you authorize Non-Card Payment Examples to debit your bank account periodically. Payments that fall outside of the regular debits authorized above will only be debited after your authorization is obtained. """ mandateLabel.translatesAutoresizingMaskIntoConstraints = false @@ -100,17 +102,16 @@ class USBankAccountExampleViewController: UIViewController { fieldsStackView.topAnchor.constraint(equalToSystemSpacingBelow: view.safeAreaLayoutGuide.topAnchor, multiplier: 1), fieldsStackView.leadingAnchor.constraint(equalToSystemSpacingAfter: view.safeAreaLayoutGuide.leadingAnchor, multiplier: 1), view.safeAreaLayoutGuide.trailingAnchor.constraint(equalToSystemSpacingAfter: fieldsStackView.trailingAnchor, multiplier: 1), - + nameField.widthAnchor.constraint(equalTo: fieldsStackView.widthAnchor), emailField.widthAnchor.constraint(equalTo: fieldsStackView.widthAnchor), accountNumberField.widthAnchor.constraint(equalTo: fieldsStackView.widthAnchor), routingNumberField.widthAnchor.constraint(equalTo: fieldsStackView.widthAnchor), - + mandateLabel.topAnchor.constraint(equalToSystemSpacingBelow: fieldsStackView.bottomAnchor, multiplier: 1), mandateLabel.leadingAnchor.constraint(equalToSystemSpacingAfter: view.safeAreaLayoutGuide.leadingAnchor, multiplier: 1), view.safeAreaLayoutGuide.trailingAnchor.constraint(equalToSystemSpacingAfter: mandateLabel.trailingAnchor, multiplier: 1), - payButton.centerXAnchor.constraint(equalTo: view.centerXAnchor), payButton.topAnchor.constraint(equalToSystemSpacingBelow: mandateLabel.bottomAnchor, multiplier: 1), @@ -136,7 +137,7 @@ extension USBankAccountExampleViewController { @objc func pay() { // 1. Create a US Bank Account PaymentIntent MyAPIClient.shared().createPaymentIntent( - completion: { [self] (result, clientSecret, error) in + completion: { [self] (_, clientSecret, error) in guard let clientSecret = clientSecret else { self.delegate?.exampleViewController(self, didFinishWithError: error) return @@ -147,22 +148,22 @@ extension USBankAccountExampleViewController { usBankAccountParams.accountHolderType = accountHolderTypeSelector.titleForSegment(at: max(accountHolderTypeSelector.selectedSegmentIndex, 0)) == "individual" ? .individual : .company usBankAccountParams.accountNumber = accountNumberField.text usBankAccountParams.routingNumber = routingNumberField.text - + let billingDetails = STPPaymentMethodBillingDetails() billingDetails.name = nameField.text billingDetails.email = emailField.text - + let paymentMethodParams = STPPaymentMethodParams(usBankAccount: usBankAccountParams, billingDetails: billingDetails, metadata: nil) let paymentIntentParams = STPPaymentIntentParams(clientSecret: clientSecret) paymentIntentParams.paymentMethodParams = paymentMethodParams paymentIntentParams.returnURL = "payments-example://stripe/" - + // 3. Confirm payment STPPaymentHandler.shared().confirmPayment( paymentIntentParams, with: self - ) { (status, intent, error) in + ) { (status, _, error) in switch status { case .canceled: self.delegate?.exampleViewController( diff --git a/Example/Non-Card Payment Examples/Project.swift b/Example/Non-Card Payment Examples/Project.swift index 49ac4624406..ecc38696ba4 100644 --- a/Example/Non-Card Payment Examples/Project.swift +++ b/Example/Non-Card Payment Examples/Project.swift @@ -56,14 +56,14 @@ let project = Project( additionalFiles: [ "Non-Card Payment Examples/*.h", ] - ) + ), ], schemes: [ Scheme( name: "Non-Card Payment Examples", buildAction: .buildAction(targets: ["Non-Card Payment Examples"]), runAction: .runAction(executable: "Non-Card Payment Examples") - ) + ), ], additionalFiles: "README.md" ) diff --git a/Example/PaymentSheet Example/PaymentSheet Example/AppDelegate.swift b/Example/PaymentSheet Example/PaymentSheet Example/AppDelegate.swift index 4a1e1d7ddec..4b03a6403ed 100644 --- a/Example/PaymentSheet Example/PaymentSheet Example/AppDelegate.swift +++ b/Example/PaymentSheet Example/PaymentSheet Example/AppDelegate.swift @@ -19,7 +19,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ) -> Bool { // Override point for customization after application launch. #if targetEnvironment(simulator) - if (ProcessInfo.processInfo.environment["UITesting"] != nil) { + if ProcessInfo.processInfo.environment["UITesting"] != nil { // Disable hardware keyboards in CI: let setHardwareLayout = NSSelectorFromString("setHardwareLayout:") UITextInputMode.activeInputModes @@ -32,7 +32,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { PaymentSheetTestPlayground.paymentSheetPlaygroundSettings = PaymentSheetPlaygroundSettings.defaultValues() } #endif - + return true } diff --git a/Example/PaymentSheet Example/PaymentSheet Example/AppearancePlaygroundView.swift b/Example/PaymentSheet Example/PaymentSheet Example/AppearancePlaygroundView.swift index 3b7bad034ec..92ec33971ca 100644 --- a/Example/PaymentSheet Example/PaymentSheet Example/AppearancePlaygroundView.swift +++ b/Example/PaymentSheet Example/PaymentSheet Example/AppearancePlaygroundView.swift @@ -6,152 +6,152 @@ // Copyright © 2022 stripe-ios. All rights reserved. // -import SwiftUI @_spi(STP) import StripePaymentSheet +import SwiftUI @available(iOS 14.0, *) struct AppearancePlaygroundView: View { @State var appearance: PaymentSheet.Appearance - var doneAction: ((PaymentSheet.Appearance) -> Void) = {_ in } - + var doneAction: ((PaymentSheet.Appearance) -> Void) = { _ in } + init(appearance: PaymentSheet.Appearance, doneAction: @escaping ((PaymentSheet.Appearance) -> Void)) { _appearance = State.init(initialValue: appearance) self.doneAction = doneAction } - + var body: some View { let primaryColorBinding = Binding( get: { Color(self.appearance.colors.primary) }, set: { self.appearance.colors.primary = UIColor($0) } ) - + let backgroundColorBinding = Binding( get: { Color(self.appearance.colors.background) }, set: { self.appearance.colors.background = UIColor($0) } ) - + let componentBackgroundColorBinding = Binding( get: { Color(self.appearance.colors.componentBackground) }, set: { self.appearance.colors.componentBackground = UIColor($0) } ) - + let componentBorderColorBinding = Binding( get: { Color(self.appearance.colors.componentBorder) }, set: { self.appearance.colors.componentBorder = UIColor($0) } ) - + let componentDividerColorBinding = Binding( get: { Color(self.appearance.colors.componentDivider) }, set: { self.appearance.colors.componentDivider = UIColor($0) } ) - + let textColorBinding = Binding( get: { Color(self.appearance.colors.text) }, set: { self.appearance.colors.text = UIColor($0) } ) - + let textSecondaryColorBinding = Binding( get: { Color(self.appearance.colors.textSecondary) }, set: { self.appearance.colors.textSecondary = UIColor($0) } ) - + let componentBackgroundTextColorBinding = Binding( get: { Color(self.appearance.colors.componentText) }, set: { self.appearance.colors.componentText = UIColor($0) } ) - + let placeholderTextColorBinding = Binding( get: { Color(self.appearance.colors.componentPlaceholderText) }, set: { self.appearance.colors.componentPlaceholderText = UIColor($0) } ) - + let iconColorBinding = Binding( get: { Color(self.appearance.colors.icon) }, set: { self.appearance.colors.icon = UIColor($0) } ) - + let dangerColorBinding = Binding( get: { Color(self.appearance.colors.danger) }, set: { self.appearance.colors.danger = UIColor($0) } ) - + let cornerRadiusBinding = Binding( get: { self.appearance.cornerRadius }, set: { self.appearance.cornerRadius = $0 } ) - + let componentBorderWidthBinding = Binding( get: { self.appearance.borderWidth }, set: { self.appearance.borderWidth = $0 } ) - + let componentShadowColorBinding = Binding( get: { Color(self.appearance.shadow.color) }, set: { self.appearance.shadow.color = UIColor($0) } ) - + let componentShadowAlphaBinding = Binding( get: { self.appearance.shadow.opacity }, set: { self.appearance.shadow.opacity = $0 } ) - + let componentShadowOffsetXBinding = Binding( get: { self.appearance.shadow.offset.width }, set: { self.appearance.shadow.offset.width = $0 } ) - + let componentShadowOffsetYBinding = Binding( get: { self.appearance.shadow.offset.height }, set: { self.appearance.shadow.offset.height = $0 } ) - + let componentShadowRadiusBinding = Binding( get: { self.appearance.shadow.radius }, set: { self.appearance.shadow.radius = $0 } ) - + let sizeScaleFactorBinding = Binding( get: { self.appearance.font.sizeScaleFactor }, set: { self.appearance.font.sizeScaleFactor = $0 } ) - + let regularFontBinding = Binding( get: { self.appearance.font.base.fontDescriptor.postscriptName }, set: { self.appearance.font.base = UIFont(name: $0, size: 12.0)! } ) - + // MARK: Primary button bindings - + let primaryButtonBackgroundColorBinding = Binding( get: { Color(self.appearance.primaryButton.backgroundColor ?? self.appearance.colors.primary) }, set: { self.appearance.primaryButton.backgroundColor = UIColor($0) } ) - + let primaryButtonTextColorBinding = Binding( get: { Color(self.appearance.primaryButton.textColor ?? UIColor.white) }, set: { self.appearance.primaryButton.textColor = UIColor($0) } ) - + let primaryButtonBorderColorBinding = Binding( get: { Color(self.appearance.primaryButton.borderColor) }, set: { self.appearance.primaryButton.borderColor = UIColor($0) } ) - + let primaryButtonCornerRadiusBinding = Binding( get: { self.appearance.primaryButton.cornerRadius ?? appearance.cornerRadius }, set: { self.appearance.primaryButton.cornerRadius = $0 } ) - + let primaryButtonCornerBorderWidth = Binding( - get: { self.appearance.primaryButton.borderWidth}, + get: { self.appearance.primaryButton.borderWidth }, set: { self.appearance.primaryButton.borderWidth = $0 } ) - + let primaryButtonFontBinding = Binding( get: { self.appearance.primaryButton.font?.fontDescriptor.postscriptName ?? UIFont.systemFont(ofSize: 16, weight: .medium).fontDescriptor.postscriptName }, set: { self.appearance.primaryButton.font = UIFont(name: $0, size: 16.0)! } ) - + let primaryButtonShadowColorBinding = Binding( get: { Color(self.appearance.primaryButton.shadow?.color ?? PaymentSheet.Appearance.Shadow().color) }, set: { @@ -159,7 +159,7 @@ struct AppearancePlaygroundView: View { self.appearance.primaryButton.shadow?.color = UIColor($0) } ) - + let primaryButtonShadowAlphaBinding = Binding( get: { self.appearance.primaryButton.shadow?.opacity ?? PaymentSheet.Appearance.Shadow().opacity }, set: { @@ -167,7 +167,7 @@ struct AppearancePlaygroundView: View { self.appearance.primaryButton.shadow?.opacity = $0 } ) - + let primaryButtonShadowOffsetXBinding = Binding( get: { self.appearance.primaryButton.shadow?.offset.width ?? PaymentSheet.Appearance.Shadow().offset.width }, set: { @@ -175,7 +175,7 @@ struct AppearancePlaygroundView: View { self.appearance.primaryButton.shadow?.offset.width = $0 } ) - + let primaryButtonShadowOffsetYBinding = Binding( get: { self.appearance.primaryButton.shadow?.offset.height ?? PaymentSheet.Appearance.Shadow().offset.height }, set: { @@ -183,7 +183,7 @@ struct AppearancePlaygroundView: View { self.appearance.primaryButton.shadow?.offset.height = $0 } ) - + let primaryButtonShadowRadiusBinding = Binding( get: { self.appearance.primaryButton.shadow?.radius ?? PaymentSheet.Appearance.Shadow().radius }, set: { @@ -191,10 +191,9 @@ struct AppearancePlaygroundView: View { self.appearance.primaryButton.shadow?.radius = $0 } ) - - + let regularFonts = ["AvenirNext-Regular", "PingFangHK-Regular", "ChalkboardSE-Light"] - + NavigationView { List { Section(header: Text("Colors")) { @@ -209,19 +208,19 @@ struct AppearancePlaygroundView: View { ColorPicker("componentText", selection: componentBackgroundTextColorBinding) ColorPicker("componentPlaceholderText", selection: placeholderTextColorBinding) ColorPicker("icon", selection: iconColorBinding) - + } // https://stackoverflow.com/questions/61178868/swiftui-random-extra-argument-in-call-error ColorPicker("danger", selection: dangerColorBinding) } - + Section(header: Text("Miscellaneous")) { Stepper("cornerRadius: \(Int(appearance.cornerRadius))", value: cornerRadiusBinding, in: 0...30) Stepper("componentBorderWidth: \(Int(appearance.borderWidth))", value: componentBorderWidthBinding, in: 0...30) VStack { Text("componentShadow") ColorPicker("color", selection: componentShadowColorBinding) - + HStack { Text(String(format: "alpha: %.2f", appearance.shadow.opacity)) Slider(value: componentShadowAlphaBinding, in: 0...1, step: 0.05) @@ -231,7 +230,7 @@ struct AppearancePlaygroundView: View { value: componentShadowOffsetXBinding, in: 0...20) Stepper("offset.y: \(Int(appearance.shadow.offset.height))", value: componentShadowOffsetYBinding, in: 0...20) - + HStack { Text(String(format: "radius: %.1f", appearance.shadow.radius)) Slider(value: componentShadowRadiusBinding, in: 0...10, step: 0.5) @@ -249,7 +248,7 @@ struct AppearancePlaygroundView: View { } } } - + Section(header: Text("Primary Button")) { DisclosureGroup { ColorPicker("backgroundColor", selection: primaryButtonBackgroundColorBinding) @@ -266,7 +265,7 @@ struct AppearancePlaygroundView: View { VStack { Text("shadow") ColorPicker("color", selection: primaryButtonShadowColorBinding) - + HStack { Text(String(format: "alpha: %.2f", appearance.primaryButton.shadow?.opacity ?? PaymentSheet.Appearance.Shadow().opacity)) Slider(value: primaryButtonShadowAlphaBinding, in: 0...1, step: 0.05) @@ -276,7 +275,7 @@ struct AppearancePlaygroundView: View { value: primaryButtonShadowOffsetXBinding, in: 0...20) Stepper("offset.y: \(Int(appearance.primaryButton.shadow?.offset.height ?? PaymentSheet.Appearance.Shadow().offset.height))", value: primaryButtonShadowOffsetYBinding, in: 0...20) - + HStack { Text(String(format: "radius: %.1f", appearance.primaryButton.shadow?.radius ?? PaymentSheet.Appearance.Shadow().radius)) Slider(value: primaryButtonShadowRadiusBinding, in: 0...10, step: 0.5) @@ -286,14 +285,14 @@ struct AppearancePlaygroundView: View { Text("Primary Button") } } - + Button { appearance = PaymentSheet.Appearance() doneAction(appearance) } label: { Text("Reset Appearance") } - + }.navigationTitle("Appearance") .toolbar { Button("Done") { @@ -307,7 +306,7 @@ struct AppearancePlaygroundView: View { struct AppearancePlaygroundView_Previews: PreviewProvider { static var previews: some View { if #available(iOS 14.0, *) { - AppearancePlaygroundView(appearance: PaymentSheet.Appearance(), doneAction: {_ in }) + AppearancePlaygroundView(appearance: PaymentSheet.Appearance(), doneAction: { _ in }) } } } diff --git a/Example/PaymentSheet Example/PaymentSheet Example/EndpointSelectorViewController.swift b/Example/PaymentSheet Example/PaymentSheet Example/EndpointSelectorViewController.swift index e313bd6c1cc..758f147fabe 100644 --- a/Example/PaymentSheet Example/PaymentSheet Example/EndpointSelectorViewController.swift +++ b/Example/PaymentSheet Example/PaymentSheet Example/EndpointSelectorViewController.swift @@ -7,8 +7,8 @@ import Foundation import UIKit protocol EndpointSelectorViewControllerDelegate: AnyObject { - func selected(endpoint: String) -> Void - func cancelTapped() -> Void + func selected(endpoint: String) + func cancelTapped() } class EndpointSelectorViewController: UITableViewController { @@ -124,7 +124,7 @@ extension EndpointSelectorViewController { extension EndpointSelectorViewController { func loadEndpointSelector(endpoint: String) { let request = URLRequest(url: URL(string: endpoint)!) - let session = URLSession.shared.dataTask(with: request) { data, response, error in + let session = URLSession.shared.dataTask(with: request) { data, _, error in guard error == nil, let data = data, let specs = self.deserializeResponse(data: data) else { diff --git a/Example/PaymentSheet Example/PaymentSheet Example/ExampleCheckoutViewController.swift b/Example/PaymentSheet Example/PaymentSheet Example/ExampleCheckoutViewController.swift index f00a0374e94..683c9587ae8 100644 --- a/Example/PaymentSheet Example/PaymentSheet Example/ExampleCheckoutViewController.swift +++ b/Example/PaymentSheet Example/PaymentSheet Example/ExampleCheckoutViewController.swift @@ -26,7 +26,7 @@ class ExampleCheckoutViewController: UIViewController { request.httpMethod = "POST" let task = URLSession.shared.dataTask( with: request, - completionHandler: { [weak self] (data, response, error) in + completionHandler: { [weak self] (data, _, _) in guard let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any], @@ -82,7 +82,7 @@ class ExampleCheckoutViewController: UIViewController { func displayAlert(_ message: String) { let alertController = UIAlertController(title: "", message: message, preferredStyle: .alert) - let OKAction = UIAlertAction(title: "OK", style: .default) { (action) in + let OKAction = UIAlertAction(title: "OK", style: .default) { (_) in alertController.dismiss(animated: true) { self.navigationController?.popViewController(animated: true) } diff --git a/Example/PaymentSheet Example/PaymentSheet Example/ExampleCustomCheckoutViewController.swift b/Example/PaymentSheet Example/PaymentSheet Example/ExampleCustomCheckoutViewController.swift index f9c0e56df0d..a1bb2b25f40 100644 --- a/Example/PaymentSheet Example/PaymentSheet Example/ExampleCustomCheckoutViewController.swift +++ b/Example/PaymentSheet Example/PaymentSheet Example/ExampleCustomCheckoutViewController.swift @@ -22,7 +22,7 @@ class ExampleCustomCheckoutViewController: UIViewController { buyButton.addTarget(self, action: #selector(didTapCheckoutButton), for: .touchUpInside) buyButton.isEnabled = false - + paymentMethodButton.addTarget(self, action: #selector(didTapPaymentMethodButton), for: .touchUpInside) paymentMethodButton.isEnabled = false @@ -31,7 +31,7 @@ class ExampleCustomCheckoutViewController: UIViewController { request.httpMethod = "POST" let task = URLSession.shared.dataTask( with: request, - completionHandler: { [weak self] (data, response, error) in + completionHandler: { [weak self] (data, _, error) in guard let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any], @@ -120,7 +120,7 @@ class ExampleCustomCheckoutViewController: UIViewController { func displayAlert(_ message: String) { let alertController = UIAlertController(title: "", message: message, preferredStyle: .alert) - let OKAction = UIAlertAction(title: "OK", style: .default) { (action) in + let OKAction = UIAlertAction(title: "OK", style: .default) { (_) in alertController.dismiss(animated: true) { self.navigationController?.popViewController(animated: true) } diff --git a/Example/PaymentSheet Example/PaymentSheet Example/ExampleSwiftUICustomPaymentFlow.swift b/Example/PaymentSheet Example/PaymentSheet Example/ExampleSwiftUICustomPaymentFlow.swift index 744ef0620c3..647ff45652c 100644 --- a/Example/PaymentSheet Example/PaymentSheet Example/ExampleSwiftUICustomPaymentFlow.swift +++ b/Example/PaymentSheet Example/PaymentSheet Example/ExampleSwiftUICustomPaymentFlow.swift @@ -61,7 +61,7 @@ class MyCustomBackendModel: ObservableObject { request.httpMethod = "POST" let task = URLSession.shared.dataTask( with: request, - completionHandler: { (data, response, error) in + completionHandler: { (data, _, error) in guard let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any], diff --git a/Example/PaymentSheet Example/PaymentSheet Example/ExampleSwiftUIPaymentSheet.swift b/Example/PaymentSheet Example/PaymentSheet Example/ExampleSwiftUIPaymentSheet.swift index 9f9530f442f..e5d4001a314 100644 --- a/Example/PaymentSheet Example/PaymentSheet Example/ExampleSwiftUIPaymentSheet.swift +++ b/Example/PaymentSheet Example/PaymentSheet Example/ExampleSwiftUIPaymentSheet.swift @@ -43,7 +43,7 @@ class MyBackendModel: ObservableObject { request.httpMethod = "POST" let task = URLSession.shared.dataTask( with: request, - completionHandler: { (data, response, error) in + completionHandler: { (data, _, _) in guard let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any], diff --git a/Example/PaymentSheet Example/PaymentSheet Example/PaymentSheetTestPlayground.swift b/Example/PaymentSheet Example/PaymentSheet Example/PaymentSheetTestPlayground.swift index cc7c267583b..a00bcc09d2c 100644 --- a/Example/PaymentSheet Example/PaymentSheet Example/PaymentSheetTestPlayground.swift +++ b/Example/PaymentSheet Example/PaymentSheet Example/PaymentSheetTestPlayground.swift @@ -9,17 +9,17 @@ // an example of what you should do in a real app! // Note: Do not import Stripe using `@_spi(STP)` in production. // This exposes internal functionality which may cause unexpected behavior if used directly. -import StripePaymentSheet import Contacts -import UIKit -import SwiftUI import PassKit +import StripePaymentSheet +import SwiftUI +import UIKit class PaymentSheetTestPlayground: UIViewController { static let endpointSelectorEndpoint = "https://stripe-mobile-payment-sheet-test-playground-v6.glitch.me/endpoints" static let defaultCheckoutEndpoint = "https://stripe-mobile-payment-sheet-test-playground-v6.glitch.me/checkout" - static var paymentSheetPlaygroundSettings: PaymentSheetPlaygroundSettings? = nil + static var paymentSheetPlaygroundSettings: PaymentSheetPlaygroundSettings? // Configuration @IBOutlet weak var customerModeSelector: UISegmentedControl! @@ -72,7 +72,7 @@ class PaymentSheetTestPlayground: UIViewController { case paymentWithSetup = "payment_with_setup" case setup } - + enum ShippingMode { case on case onWithDefaults @@ -89,7 +89,7 @@ class PaymentSheetTestPlayground: UIViewController { return .returning } } - + var shouldSetDefaultBillingAddress: Bool { return defaultBillingAddressSelector.selectedSegmentIndex == 0 } @@ -112,7 +112,7 @@ class PaymentSheetTestPlayground: UIViewController { billing.startDate = Date() billing.endDate = Date().addingTimeInterval(60 * 60 * 24 * 365) billing.intervalUnit = .month - + request.recurringPaymentRequest = PKRecurringPaymentRequest(paymentDescription: "Recurring", regularBilling: billing, managementURL: URL(string: "https://my-backend.example.com/customer-portal")!) @@ -185,7 +185,7 @@ class PaymentSheetTestPlayground: UIViewController { return .setup } } - + var shippingMode: ShippingMode { switch shippingInfoSelector.selectedSegmentIndex { case 0: return .on @@ -248,7 +248,7 @@ class PaymentSheetTestPlayground: UIViewController { } var addressDetails: AddressViewController.AddressDetails? - + var clientSecret: String? var ephemeralKey: String? var customerID: String? @@ -256,11 +256,11 @@ class PaymentSheetTestPlayground: UIViewController { var paymentSheetFlowController: PaymentSheet.FlowController? var addressViewController: AddressViewController? var appearance = PaymentSheet.Appearance.default - + func makeAlertController() -> UIAlertController { let alertController = UIAlertController( title: "Complete", message: "Completed", preferredStyle: .alert) - let OKAction = UIAlertAction(title: "OK", style: .default) { (action) in + let OKAction = UIAlertAction(title: "OK", style: .default) { (_) in alertController.dismiss(animated: true, completion: nil) } alertController.addAction(OKAction) @@ -272,10 +272,10 @@ class PaymentSheetTestPlayground: UIViewController { // Enable experimental payment methods. // PaymentSheet.supportedPaymentMethods += [.link] - + checkoutButton.addTarget(self, action: #selector(didTapCheckoutButton), for: .touchUpInside) checkoutButton.isEnabled = false - + shippingAddressButton.addTarget(self, action: #selector(didTapShippingAddressButton), for: .touchUpInside) shippingAddressButton.titleLabel?.adjustsFontSizeToFitWidth = true shippingAddressButton.titleLabel?.textAlignment = .right @@ -350,7 +350,7 @@ class PaymentSheetTestPlayground: UIViewController { self.updateButtons() } } - + @objc func didTapShippingAddressButton() { present(UINavigationController(rootViewController: addressViewController!), animated: true) @@ -436,14 +436,14 @@ extension PaymentSheetTestPlayground { return "returning" } }() - + let body = [ "customer": customer, "currency": currency.rawValue, "merchant_country_code": merchantCountryCode.rawValue, "mode": intentMode.rawValue, "automatic_payment_methods": automaticPaymentMethodsSelector.selectedSegmentIndex == 0, - "use_link": linkSelector.selectedSegmentIndex == 0 + "use_link": linkSelector.selectedSegmentIndex == 0, // "set_shipping_address": true // Uncomment to make server vend PI with shipping address populated ] as [String: Any] let json = try! JSONSerialization.data(withJSONObject: body, options: []) @@ -451,7 +451,7 @@ extension PaymentSheetTestPlayground { urlRequest.httpMethod = "POST" urlRequest.httpBody = json urlRequest.setValue("application/json", forHTTPHeaderField: "Content-type") - let task = session.dataTask(with: urlRequest) { data, response, error in + let task = session.dataTask(with: urlRequest) { data, _, error in guard error == nil, let data = data, @@ -552,14 +552,14 @@ extension PaymentSheetTestPlayground: AddressViewControllerDelegate { // MARK: - EndpointSelectorViewControllerDelegate extension PaymentSheetTestPlayground: EndpointSelectorViewControllerDelegate { - func selected(endpoint: String) -> Void { + func selected(endpoint: String) { checkoutEndpoint = endpoint serializeSettingsToNSUserDefaults() loadBackend() self.navigationController?.dismiss(animated: true) } - func cancelTapped() -> Void { + func cancelTapped() { self.navigationController?.dismiss(animated: true) } } @@ -567,7 +567,7 @@ extension PaymentSheetTestPlayground: EndpointSelectorViewControllerDelegate { // MARK: - Helpers extension PaymentSheetTestPlayground { - func serializeSettingsToNSUserDefaults() -> Void { + func serializeSettingsToNSUserDefaults() { let settings = PaymentSheetPlaygroundSettings( modeSelectorValue: modeSelector.selectedSegmentIndex, customerModeSelectorValue: customerModeSelector.selectedSegmentIndex, diff --git a/Example/PaymentSheet Example/PaymentSheet Example/SceneDelegate.swift b/Example/PaymentSheet Example/PaymentSheet Example/SceneDelegate.swift index 46d37a8b546..ac997a56498 100644 --- a/Example/PaymentSheet Example/PaymentSheet Example/SceneDelegate.swift +++ b/Example/PaymentSheet Example/PaymentSheet Example/SceneDelegate.swift @@ -32,7 +32,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } func scene( - _ scene: UIScene, willConnectTo session: UISceneSession, + _ scene: UIScene, + willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions ) { // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. @@ -43,7 +44,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { window?.overrideUserInterfaceStyle = .dark } #endif - guard let _ = (scene as? UIWindowScene) else { return } + guard (scene as? UIWindowScene) != nil else { return } } func sceneDidDisconnect(_ scene: UIScene) { diff --git a/Example/PaymentSheet Example/PaymentSheetLocalizationScreenshotGenerator/PaymentSheetLocalizationScreenshotGenerator.swift b/Example/PaymentSheet Example/PaymentSheetLocalizationScreenshotGenerator/PaymentSheetLocalizationScreenshotGenerator.swift index 1f20c361208..e259b118ac7 100644 --- a/Example/PaymentSheet Example/PaymentSheetLocalizationScreenshotGenerator/PaymentSheetLocalizationScreenshotGenerator.swift +++ b/Example/PaymentSheet Example/PaymentSheetLocalizationScreenshotGenerator/PaymentSheetLocalizationScreenshotGenerator.swift @@ -19,28 +19,27 @@ class PaymentSheetLocalizationScreenshotGenerator: XCTestCase { // In UI tests it is usually best to stop immediately when a failure occurs. continueAfterFailure = false - app = XCUIApplication() app.launchEnvironment = ["UITesting": "true"] app.launch() } - + func waitToAppear(_ target: XCUIElement?) { _ = target?.waitForExistence(timeout: 60) } - + func saveScreenshot(_ name: String) { let attachment = XCTAttachment(screenshot: app.windows.firstMatch.screenshot()) attachment.lifetime = .keepAlways attachment.name = name add(attachment) } - + func scrollToPaymentMethodCell(_ cell: String) { let paymentMethodTypeCollectionView = app.collectionViews["PaymentMethodTypeCollectionView"] waitToAppear(paymentMethodTypeCollectionView) let targetCell = paymentMethodTypeCollectionView.cells[cell] - + // This is not particularly efficient or robust but it's working // Unfortunately UICollectionViews are setup for KVO so we can't query // contentOffset or contentSize here @@ -69,7 +68,7 @@ class PaymentSheetLocalizationScreenshotGenerator: XCTestCase { app.segmentedControls["mode_selector"].buttons["Pay"].tap() // PaymentIntent app.segmentedControls["automatic_payment_methods_selector"].buttons["off"].tap() // disable automatic payment methods app.buttons["Reload PaymentSheet"].tap() - + let checkout = app.buttons["Checkout (Complete)"] expectation( for: NSPredicate(format: "enabled == true"), @@ -151,7 +150,6 @@ class PaymentSheetLocalizationScreenshotGenerator: XCTestCase { cvcField.tap() saveScreenshot("card_incomplete_exp_date") - numberField.clearText() expField.clearText() cvcField.tap() @@ -172,11 +170,11 @@ class PaymentSheetLocalizationScreenshotGenerator: XCTestCase { idealCell.tap() // hacky to double tap but fixes transition if software keyboard is enabled saveScreenshot("ideal_entry") } - + do { let bancontactCell = app.cells["bancontact"] scrollToPaymentMethodCell("bancontact") - + bancontactCell.tap() bancontactCell.tap() // hacky to double tap but fixes transition if software keyboard is enabled saveScreenshot("bancontact_entry") @@ -228,8 +226,7 @@ class PaymentSheetLocalizationScreenshotGenerator: XCTestCase { // app.cells.containing(.button, identifier: "Remove").firstMatch.buttons["Remove"].tap() // saveScreenshot("removing_payment_method_confirmation") // } - - + } } @@ -238,7 +235,7 @@ extension XCUIElement { guard let stringValue = value as? String, !stringValue.isEmpty else { return } - + // offset tap location a bit so cursor is at end of string let offsetTapLocation = coordinate(withNormalizedOffset: CGVector(dx: 0.6, dy: 0.6)) offsetTapLocation.tap() diff --git a/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheet+AddressTests.swift b/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheet+AddressTests.swift index 9b623206e62..959d7e7e8aa 100644 --- a/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheet+AddressTests.swift +++ b/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheet+AddressTests.swift @@ -19,31 +19,31 @@ class PaymentSheet_AddressTests: XCTestCase { } func testManualAddressEntry() throws { - loadPlayground(app, settings: ["shipping":"on"]) + loadPlayground(app, settings: ["shipping": "on"]) let shippingButton = app.buttons["Shipping address"] XCTAssertTrue(shippingButton.waitForExistence(timeout: 4.0)) shippingButton.tap() - + // The Save Address button should be disabled let saveAddressButton = app.buttons["Save address"] XCTAssertFalse(saveAddressButton.isEnabled) - + app.textFields["Full name"].tap() app.textFields["Full name"].typeText("Jane Doe") - + // Tapping the address field should go to autocomplete app.textFields["Address"].waitForExistenceAndTap() app.buttons["Enter address manually"].waitForExistenceAndTap() - + // Tapping the address line 1 field should now just let us enter the field manually app.textFields["Address line 1"].waitForExistenceAndTap() app.typeText("510 Townsend St") - + // Tapping autocomplete button in line 1 field should take us to autocomplete with the line 1 already entered in the search field app.buttons["autocomplete_affordance"].tap() XCTAssertEqual(app.textFields["Address"].value as! String, "510 Townsend St") app.buttons["Enter address manually"].waitForExistenceAndTap() - + // Continue entering address manually... app.textFields["Address line 2"].tap() app.typeText("Apt 152") @@ -58,13 +58,13 @@ class PaymentSheet_AddressTests: XCTestCase { app.typeText("94102") app.textFields["Phone"].tap() app.textFields["Phone"].typeText("5555555555") - + XCTAssertTrue(saveAddressButton.isEnabled) saveAddressButton.tap() - + // The merchant app should get back the expected address XCTAssertEqual(shippingButton.label, "Jane Doe, 510 Townsend St, Apt 152, San Francisco CA 94102, US, +15555555555") - + // Opening the shipping address back up... shippingButton.tap() // ...and editing ZIP to be invalid... @@ -79,96 +79,96 @@ class PaymentSheet_AddressTests: XCTestCase { // The merchant app should get back nil XCTAssertEqual(shippingButton.label, "Add") } - + func testAddressWithDefaults() throws { loadPlayground(app, settings: ["shipping": "on w/ defaults"]) let shippingButton = app.buttons["Shipping address"] XCTAssertTrue(shippingButton.waitForExistence(timeout: 4.0)) shippingButton.tap() - + // The Save address button should be enabled let saveAddressButton = app.buttons["Save address"] XCTAssertTrue(saveAddressButton.isEnabled) - + saveAddressButton.tap() - + // The merchant app should get back the expected address XCTAssertEqual(shippingButton.label, "Jane Doe, 510 Townsend St., San Francisco CA 94102, US, +15555555555") } - + func testAddressAutoComplete_UnitedStates() throws { loadPlayground(app, settings: [:]) let shippingButton = app.buttons["Shipping address"] XCTAssertTrue(shippingButton.waitForExistence(timeout: 4.0)) shippingButton.tap() - + // The Save address button should be disabled let saveAddressButton = app.buttons["Save address"] XCTAssertFalse(saveAddressButton.isEnabled) - + // Tapping the address field should go to autocomplete app.textFields["Address"].waitForExistenceAndTap() - + // Enter partial address and tap first result app.typeText("4 Pennsylvania Plaza") let searchedCell = app.tables.element(boundBy: 0).cells.containing(NSPredicate(format: "label CONTAINS %@", "4 Pennsylvania Plaza")).element - let _ = searchedCell.waitForExistence(timeout: 5) + _ = searchedCell.waitForExistence(timeout: 5) searchedCell.tap() - + // Verify text fields - let _ = app.textFields["Address line 1"].waitForExistence(timeout: 5) + _ = app.textFields["Address line 1"].waitForExistence(timeout: 5) XCTAssertEqual(app.textFields["Address line 1"].value as! String, "4 Pennsylvania Plaza") XCTAssertEqual(app.textFields["Address line 2"].value as! String, "") XCTAssertEqual(app.textFields["City"].value as! String, "New York") XCTAssertEqual(app.textFields["State"].value as! String, "New York") XCTAssertEqual(app.textFields["ZIP"].value as! String, "10001") - + // Type in phone number app.textFields["Phone"].tap() app.textFields["Phone"].typeText("5555555555") - + // Type in the name to complete the form app.textFields["Full name"].tap() app.typeText("Jane Doe") - + XCTAssertTrue(saveAddressButton.isEnabled) saveAddressButton.tap() // The merchant app should get back the expected address XCTAssertEqual(shippingButton.label, "Jane Doe, 4 Pennsylvania Plaza, New York NY 10001, US, +15555555555") } - + /// This test ensures we don't show auto complete for an unsupported country func testAddressAutoComplete_NewZeland() throws { loadPlayground(app, settings: [:]) let shippingButton = app.buttons["Shipping address"] XCTAssertTrue(shippingButton.waitForExistence(timeout: 4.0)) shippingButton.tap() - + // The Save address button should be disabled let saveAddressButton = app.buttons["Save address"] XCTAssertFalse(saveAddressButton.isEnabled) - + app.textFields["Full name"].tap() app.textFields["Full name"].typeText("Jane Doe") - + // Set country to New Zealand app.textFields["Country or region"].tap() app.pickerWheels.firstMatch.adjust(toPickerWheelValue: "🇳🇿 New Zealand") app.toolbars.buttons["Done"].tap() - + // Address line 1 field should not contain an autocomplete affordance b/c autocomplete doesn't support New Zealand XCTAssertFalse(app.buttons["autocomplete_affordance"].exists) - + // Tapping the address line 1 field... app.textFields["Address line 1"].tap() - + // ...should not go to auto complete b/c it's disabled for New Zealand XCTAssertFalse(app.buttons["Enter address manually"].waitForExistence(timeout: 3)) - + // Make sure we can still fill out the form - + // Tapping the address line 1 field should now just let us enter the field manually app.textFields["Address line 1"].tap() app.typeText("1 South Bay Parade") @@ -184,28 +184,27 @@ class PaymentSheet_AddressTests: XCTestCase { app.textFields["Phone"].typeText("5555555555") XCTAssertTrue(saveAddressButton.isEnabled) saveAddressButton.tap() - + // The merchant app should get back the expected address - let _ = shippingButton.waitForExistence(timeout: 5.0) + _ = shippingButton.waitForExistence(timeout: 5.0) XCTAssertEqual(shippingButton.label, "Jane Doe, 1 South Bay Parade, Apt 152, Kaikōura 7300, NZ, +645555555555") } - + func testPaymentSheetFlowControllerUpdatesShipping() { loadPlayground(app, settings: [ "apple_pay": "off", "automatic_payment_methods": "off", - "link": "off" + "link": "off", ]) // Using PaymentSheet.FlowController w/o a shipping address... app.buttons["present_saved_pms"].waitForExistenceAndTap() - + // ...should not show the "Billing address is same as shipping" checkbox XCTAssertEqual(app.textFields["Country or region"].value as? String, "United States") XCTAssertEqual(app.textFields["ZIP"].value as? String, "") XCTAssertFalse(app.switches["Billing address is same as shipping"].exists) app.buttons["Close"].tap() - // Entering a shipping address... let shippingButton = app.buttons["Shipping address"] @@ -233,7 +232,7 @@ class PaymentSheet_AddressTests: XCTestCase { XCTAssertEqual(app.textFields["Country or region"].value as? String, "United States") XCTAssertEqual(app.textFields["ZIP"].value as? String, "94102") XCTAssertTrue(app.switches["Billing address is same as shipping"].isSelected) - + // Updating the shipping address country... app.buttons["Close"].tap() app.buttons["Shipping address"].tap() @@ -242,19 +241,19 @@ class PaymentSheet_AddressTests: XCTestCase { app.toolbars.buttons["Done"].tap() app.buttons["Close"].tap() - //...should update PaymentSheet.FlowController + // ...should update PaymentSheet.FlowController app.buttons["present_saved_pms"].waitForExistenceAndTap() XCTAssertEqual(app.textFields["Country or region"].value as? String, "Canada") - + // If you change the billing address, however... let updatedBillingAddressPostalCode = "12345" app.textFields["Postal code"].tap() let deleteString = String(repeating: XCUIKeyboardKey.delete.rawValue, count: 5) app.textFields["Postal code"].typeText(deleteString + updatedBillingAddressPostalCode) - + // ...the "Billing address is same as shipping" checkbox should become deselected... XCTAssertFalse(app.switches["Billing address is same as shipping"].isSelected) - + // ...and changing the shipping address... app.buttons["Close"].tap() app.buttons["Shipping address"].tap() @@ -262,33 +261,33 @@ class PaymentSheet_AddressTests: XCTestCase { app.pickerWheels.firstMatch.adjust(toPickerWheelValue: "🇺🇸 United States") app.toolbars.buttons["Done"].tap() app.buttons["Close"].tap() - + // ...should not affect your billing address... app.buttons["present_saved_pms"].waitForExistenceAndTap() XCTAssertEqual(app.textFields["Country or region"].value as? String, "Canada") XCTAssertEqual(app.textFields["Postal code"].value as? String, updatedBillingAddressPostalCode) - + // ...until 'Billing address is same as shipping' checkbox is selected again app.switches["Billing address is same as shipping"].tap() XCTAssertEqual(app.textFields["Country or region"].value as? String, "United States") XCTAssertEqual(app.textFields["ZIP"].value as? String, "94102") } - + func testManualAddressEntry_phoneCountryDoesPersist() throws { loadPlayground(app, settings: [:]) let shippingButton = app.buttons["Shipping address"] XCTAssertTrue(shippingButton.waitForExistence(timeout: 4.0)) shippingButton.tap() - + // The Save Address button should be disabled let saveAddressButton = app.buttons["Save address"] XCTAssertFalse(saveAddressButton.isEnabled) - + // Select UK for phone number country app.textFields["United States +1"].tap() app.pickerWheels.firstMatch.adjust(toPickerWheelValue: "🇬🇧 United Kingdom +44") app.toolbars.buttons["Done"].tap() - + // Ensure UK is persisted as phone country after tapping done XCTAssert(app.textFields["United Kingdom +44"].exists) } diff --git a/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheetSnapshotTests.swift b/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheetSnapshotTests.swift index 40126f0835a..f09016d0a57 100644 --- a/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheetSnapshotTests.swift +++ b/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheetSnapshotTests.swift @@ -6,28 +6,28 @@ // Copyright © 2022 stripe-ios. All rights reserved. // -import UIKit import iOSSnapshotTestCase -import StripeCoreTestUtils import OHHTTPStubs import OHHTTPStubsSwift +import StripeCoreTestUtils +import UIKit -@_spi(STP) @testable import StripeUICore @_spi(STP) @testable import StripeCore @_spi(STP) @testable import StripePaymentSheet +@_spi(STP) @testable import StripeUICore class PaymentSheetSnapshotTests: FBSnapshotTestCase { private let backendCheckoutUrl = URL(string: "https://stripe-mobile-payment-sheet-test-playground-v6.glitch.me/checkout")! - + private var paymentSheet: PaymentSheet! - + private var window: UIWindow { let window = UIWindow(frame: CGRect(x: 0, y: 0, width: 428, height: 1026)) window.isHidden = false return window } - + private var configuration = PaymentSheet.Configuration() // Change this to true to hit the real glitch backend. This may be required @@ -35,7 +35,7 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { var runAgainstLiveService: Bool = false override func setUp() { super.setUp() - + configuration = PaymentSheet.Configuration() configuration.merchantDisplayName = "Example, Inc." configuration.applePay = .init( @@ -111,7 +111,7 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { presentPaymentSheet(darkMode: false, preferredContentSizeCategory: .extraExtraLarge) verify(paymentSheet.bottomSheetViewController.view!) } - + func testPaymentSheetShadow() { stubNewCustomerResponse() @@ -121,7 +121,7 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { presentPaymentSheet(darkMode: false, preferredContentSizeCategory: .extraExtraLarge) verify(paymentSheet.bottomSheetViewController.view!) } - + func testPaymentSheetShadowRoundsCorners() { stubNewCustomerResponse() @@ -131,19 +131,19 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { presentPaymentSheet(darkMode: false, preferredContentSizeCategory: .extraExtraLarge) verify(paymentSheet.bottomSheetViewController.view!) } - + func testPaymentSheetFont() { stubNewCustomerResponse() var appearance = PaymentSheet.Appearance() appearance.font.sizeScaleFactor = 1.15 appearance.font.base = UIFont(name: "AvenirNext-Regular", size: UIFont.labelFontSize)! - + preparePaymentSheet(appearance: appearance) presentPaymentSheet(darkMode: false) verify(paymentSheet.bottomSheetViewController.view!) } - + func testPaymentSheetColors() { stubNewCustomerResponse() @@ -159,12 +159,12 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { appearance.colors.componentPlaceholderText = .white appearance.colors.icon = .orange appearance.colors.danger = .cyan - + preparePaymentSheet(appearance: appearance) presentPaymentSheet(darkMode: false) verify(paymentSheet.bottomSheetViewController.view!) } - + func testPaymentSheetPrimaryButton() { stubNewCustomerResponse() @@ -176,35 +176,34 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { appearance.primaryButton.borderWidth = 2.0 appearance.primaryButton.font = UIFont(name: "AvenirNext-Regular", size: UIFont.labelFontSize)! appearance.primaryButton.shadow = PaymentSheet.Appearance.Shadow(color: .yellow, opacity: 0.5, offset: CGSize(width: 0, height: 2), radius: 6) - + preparePaymentSheet(appearance: appearance) presentPaymentSheet(darkMode: false) verify(paymentSheet.bottomSheetViewController.view!) } - + func testPaymentSheetCornerRadius() { stubNewCustomerResponse() var appearance = PaymentSheet.Appearance() appearance.cornerRadius = 0.0 - + preparePaymentSheet(appearance: appearance) presentPaymentSheet(darkMode: false) verify(paymentSheet.bottomSheetViewController.view!) } - + func testPaymentSheetBorderWidth() { stubNewCustomerResponse() var appearance = PaymentSheet.Appearance() appearance.borderWidth = 2.0 - + preparePaymentSheet(appearance: appearance) presentPaymentSheet(darkMode: false) verify(paymentSheet.bottomSheetViewController.view!) } - func testPaymentSheetCustom() { stubReturningCustomerResponse() @@ -260,21 +259,21 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { presentPaymentSheet(darkMode: false) verify(paymentSheet.bottomSheetViewController.view!) } - + func testPaymentSheetCustomFont() { stubReturningCustomerResponse() var appearance = PaymentSheet.Appearance() appearance.font.sizeScaleFactor = 1.15 appearance.font.base = UIFont(name: "AvenirNext-Regular", size: UIFont.labelFontSize)! - + preparePaymentSheet(customer: "snapshot", appearance: appearance, applePayEnabled: false) presentPaymentSheet(darkMode: false) verify(paymentSheet.bottomSheetViewController.view!) } - + func testPaymentSheetCustomColors() { stubReturningCustomerResponse() @@ -290,13 +289,13 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { appearance.colors.componentPlaceholderText = .white appearance.colors.icon = .orange appearance.colors.danger = .cyan - + preparePaymentSheet(customer: "snapshot", appearance: appearance, applePayEnabled: false) presentPaymentSheet(darkMode: false) verify(paymentSheet.bottomSheetViewController.view!) } - + func testPaymentSheetCustomPrimaryButton() { stubReturningCustomerResponse() @@ -308,24 +307,24 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { appearance.primaryButton.borderWidth = 2.0 appearance.primaryButton.font = UIFont(name: "AvenirNext-Regular", size: UIFont.labelFontSize)! appearance.primaryButton.shadow = PaymentSheet.Appearance.Shadow(color: .yellow, opacity: 0.5, offset: CGSize(width: 0, height: 2), radius: 6) - + preparePaymentSheet(customer: "snapshot", appearance: appearance, applePayEnabled: false) presentPaymentSheet(darkMode: false) verify(paymentSheet.bottomSheetViewController.view!) } - + func testPaymentSheetCustomPrimaryButtonLabel() { stubNewCustomerResponse() configuration.primaryButtonLabel = "Donate" - + preparePaymentSheet() presentPaymentSheet(darkMode: false) verify(paymentSheet.bottomSheetViewController.view!) } - + func testPaymentSheetCustomApplePayCta() { stubNewCustomerResponse() @@ -333,7 +332,7 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { merchantId: "com.foo.example", merchantCountryCode: "US", buttonType: .donate) - + preparePaymentSheet(applePayEnabled: true) presentPaymentSheet(darkMode: false) verify(paymentSheet.bottomSheetViewController.view!) @@ -347,7 +346,7 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { merchantId: "com.foo.example", merchantCountryCode: "US", buttonType: .donate) - + preparePaymentSheet(applePayEnabled: true) presentPaymentSheet(darkMode: false) verify(paymentSheet.bottomSheetViewController.view!) @@ -358,20 +357,20 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { var appearance = PaymentSheet.Appearance() appearance.cornerRadius = 0.0 - + preparePaymentSheet(customer: "snapshot", appearance: appearance, applePayEnabled: false) presentPaymentSheet(darkMode: false) verify(paymentSheet.bottomSheetViewController.view!) } - + func testPaymentSheetCustomBorderWidth() { stubReturningCustomerResponse() var appearance = PaymentSheet.Appearance() appearance.borderWidth = 2.0 - + preparePaymentSheet(customer: "snapshot", appearance: appearance, applePayEnabled: false) @@ -433,8 +432,12 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { func testPaymentSheet_LPM_Affirm_only() { stubSessions(fileMock: .elementsSessionsPaymentMethod_200, responseCallback: { data in - return self.updatePaymentMethodDetail(data: data, variables: ["": "\"affirm\"", - "": "\"usd\""]) + return self.updatePaymentMethodDetail( + data: data, + variables: [ + "": "\"affirm\"", + "": "\"usd\"", + ]) }) stubPaymentMethods(fileMock: .saved_payment_methods_200) stubCustomers() @@ -449,8 +452,12 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { func testPaymentSheet_LPM_AfterpayClearpay_only() { stubSessions(fileMock: .elementsSessionsPaymentMethod_200, responseCallback: { data in - return self.updatePaymentMethodDetail(data: data, variables: ["": "\"afterpay_clearpay\"", - "": "\"usd\""]) + return self.updatePaymentMethodDetail( + data: data, + variables: [ + "": "\"afterpay_clearpay\"", + "": "\"usd\"", + ]) }) stubPaymentMethods(fileMock: .saved_payment_methods_200) stubCustomers() @@ -465,8 +472,12 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { func testPaymentSheet_LPM_klarna_only() { stubSessions(fileMock: .elementsSessionsPaymentMethod_200, responseCallback: { data in - return self.updatePaymentMethodDetail(data: data, variables: ["": "\"klarna\"", - "": "\"usd\""]) + return self.updatePaymentMethodDetail( + data: data, + variables: [ + "": "\"klarna\"", + "": "\"usd\"", + ]) }) stubPaymentMethods(fileMock: .saved_payment_methods_200) stubCustomers() @@ -481,8 +492,12 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { func testPaymentSheet_LPM_iDeal_only() { stubSessions(fileMock: .elementsSessionsPaymentMethod_200, responseCallback: { data in - return self.updatePaymentMethodDetail(data: data, variables: ["": "\"ideal\"", - "": "\"eur\""]) + return self.updatePaymentMethodDetail( + data: data, + variables: [ + "": "\"ideal\"", + "": "\"eur\"", + ]) }) stubPaymentMethods(fileMock: .saved_payment_methods_200) stubCustomers() @@ -498,8 +513,12 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { func testPaymentSheet_LPM_bancontact_only() { stubSessions(fileMock: .elementsSessionsPaymentMethod_200, responseCallback: { data in - return self.updatePaymentMethodDetail(data: data, variables: ["": "\"bancontact\"", - "": "\"eur\""]) + return self.updatePaymentMethodDetail( + data: data, + variables: [ + "": "\"bancontact\"", + "": "\"eur\"", + ]) }) stubPaymentMethods(fileMock: .saved_payment_methods_200) stubCustomers() @@ -515,8 +534,12 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { func testPaymentSheet_LPM_sofort_only() { stubSessions(fileMock: .elementsSessionsPaymentMethod_200, responseCallback: { data in - return self.updatePaymentMethodDetail(data: data, variables: ["": "\"sofort\"", - "": "\"eur\""]) + return self.updatePaymentMethodDetail( + data: data, + variables: [ + "": "\"sofort\"", + "": "\"eur\"", + ]) }) stubPaymentMethods(fileMock: .saved_payment_methods_200) stubCustomers() @@ -532,8 +555,12 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { func testPaymentSheet_LPM_sepaDebit_only() { stubSessions(fileMock: .elementsSessionsPaymentMethod_200, responseCallback: { data in - return self.updatePaymentMethodDetail(data: data, variables: ["": "\"sepa_debit\"", - "": "\"eur\""]) + return self.updatePaymentMethodDetail( + data: data, + variables: [ + "": "\"sepa_debit\"", + "": "\"eur\"", + ]) }) stubPaymentMethods(fileMock: .saved_payment_methods_200) stubCustomers() @@ -546,12 +573,15 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { verify(paymentSheet.bottomSheetViewController.view!) } - func testPaymentSheet_LPM_eps_only() { stubSessions(fileMock: .elementsSessionsPaymentMethod_200, responseCallback: { data in - return self.updatePaymentMethodDetail(data: data, variables: ["": "\"eps\"", - "": "\"eur\""]) + return self.updatePaymentMethodDetail( + data: data, + variables: [ + "": "\"eps\"", + "": "\"eur\"", + ]) }) stubPaymentMethods(fileMock: .saved_payment_methods_200) stubCustomers() @@ -567,8 +597,11 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { func testPaymentSheet_LPM_giropay_only() { stubSessions(fileMock: .elementsSessionsPaymentMethod_200, responseCallback: { data in - return self.updatePaymentMethodDetail(data: data, variables: ["": "\"giropay\"", - "": "\"eur\""]) + return self.updatePaymentMethodDetail( + data: data, variables: [ + "": "\"giropay\"", + "": "\"eur\"", + ]) }) stubPaymentMethods(fileMock: .saved_payment_methods_200) stubCustomers() @@ -584,8 +617,12 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { func testPaymentSheet_LPM_p24_only() { stubSessions(fileMock: .elementsSessionsPaymentMethod_200, responseCallback: { data in - return self.updatePaymentMethodDetail(data: data, variables: ["": "\"p24\"", - "": "\"eur\""]) + return self.updatePaymentMethodDetail( + data: data, + variables: [ + "": "\"p24\"", + "": "\"eur\"", + ]) }) stubPaymentMethods(stubRequestCallback: nil, fileMock: .saved_payment_methods_200) stubCustomers() @@ -601,8 +638,12 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { func testPaymentSheet_LPM_aubecs_only() { stubSessions(fileMock: .elementsSessionsPaymentMethod_200, responseCallback: { data in - return self.updatePaymentMethodDetail(data: data, variables: ["": "\"au_becs_debit\"", - "": "\"aud\""]) + return self.updatePaymentMethodDetail( + data: data, + variables: [ + "": "\"au_becs_debit\"", + "": "\"aud\"", + ]) }) stubPaymentMethods(stubRequestCallback: nil, fileMock: .saved_payment_methods_200) stubCustomers() @@ -618,8 +659,12 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { func testPaymentSheet_LPM_paypal_only() { stubSessions(fileMock: .elementsSessionsPaymentMethod_200, responseCallback: { data in - return self.updatePaymentMethodDetail(data: data, variables: ["": "\"paypal\"", - "": "\"GBP\""]) + return self.updatePaymentMethodDetail( + data: data, + variables: [ + "": "\"paypal\"", + "": "\"GBP\"", + ]) }) stubPaymentMethods(stubRequestCallback: nil, fileMock: .saved_payment_methods_200) stubCustomers() @@ -631,12 +676,16 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { presentPaymentSheet(darkMode: false) verify(paymentSheet.bottomSheetViewController.view!) } - - func testPaymentSheet_LPM_upi_only() { + + func testPaymentSheet_LPM_upi_only() { stubSessions(fileMock: .elementsSessionsPaymentMethod_200, responseCallback: { data in - return self.updatePaymentMethodDetail(data: data, variables: ["": "\"upi\"", - "": "\"inr\""]) + return self.updatePaymentMethodDetail( + data: data, + variables: [ + "": "\"upi\"", + "": "\"inr\"", + ]) }) stubPaymentMethods(stubRequestCallback: nil, fileMock: .saved_payment_methods_200) stubCustomers() @@ -649,7 +698,7 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { verify(paymentSheet.bottomSheetViewController.view!) } - private func updatePaymentMethodDetail(data: Data, variables: [String:String]) -> Data { + private func updatePaymentMethodDetail(data: Data, variables: [String: String]) -> Data { var template = String(data: data, encoding: .utf8)! for (templateKey, templateValue) in variables { let translated = template.replacingOccurrences(of: templateKey, with: templateValue) @@ -658,8 +707,10 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { return template.data(using: .utf8)! } - private func stubPaymentMethods(stubRequestCallback: ((URLRequest) -> Bool?)? = nil, - fileMock: FileMock) { + private func stubPaymentMethods( + stubRequestCallback: ((URLRequest) -> Bool?)? = nil, + fileMock: FileMock + ) { guard !runAgainstLiveService else { return } @@ -668,7 +719,7 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { return shouldStub } return urlRequest.url?.absoluteString.contains("/v1/payment_methods") ?? false - } response: { urlRequest in + } response: { _ in let mockResponseData = try! fileMock.data() return HTTPStubsResponse(data: mockResponseData, statusCode: 200, headers: nil) } @@ -680,7 +731,7 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { } stub { urlRequest in return urlRequest.url?.absoluteString.contains("/v1/customers") ?? false - } response: { urlRequest in + } response: { _ in let mockResponseData = try! FileMock.customers_200.data() return HTTPStubsResponse(data: mockResponseData, statusCode: 200, headers: nil) } @@ -692,7 +743,7 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { } stub { urlRequest in return urlRequest.url?.absoluteString.contains("/v1/elements/sessions") ?? false - } response: { urlRequest in + } response: { _ in let mockResponseData = try! fileMock.data() let data = responseCallback?(mockResponseData) ?? mockResponseData return HTTPStubsResponse(data: data, statusCode: 200, headers: nil) @@ -736,7 +787,7 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { "mode": "payment", "set_shipping_address": "false", "automatic_payment_methods": automaticPaymentMethods, - "use_link": useLink + "use_link": useLink, ] as [String: Any] if let override_payment_methods_types = override_payment_methods_types { @@ -748,7 +799,7 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { urlRequest.httpMethod = "POST" urlRequest.httpBody = json urlRequest.setValue("application/json", forHTTPHeaderField: "Content-type") - let task = session.dataTask(with: urlRequest) { data, response, error in + let task = session.dataTask(with: urlRequest) { data, _, error in guard error == nil, let data = data, @@ -822,14 +873,14 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { while isLoading && count < 10 { count += 1 DispatchQueue.main.sync { - guard let _ = self.paymentSheet.bottomSheetViewController.contentStack.first as? LoadingViewController else { + guard (self.paymentSheet.bottomSheetViewController.contentStack.first as? LoadingViewController) != nil else { isLoading = false presentingExpectation.fulfill() return } } if isLoading { - usleep(50000) //50ms + usleep(50000) // 50ms } } } @@ -837,7 +888,7 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { paymentSheet.bottomSheetViewController.presentationController!.overrideTraitCollection = UITraitCollection(preferredContentSizeCategory: preferredContentSizeCategory) } - + func verify( _ view: UIView, identifier: String? = nil, @@ -850,13 +901,13 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { file: file, line: line) } - + private func stubNewCustomerResponse() { stubSessions(fileMock: .elementsSessionsPaymentMethod_savedPM_200) stubPaymentMethods(fileMock: .saved_payment_methods_200) stubCustomers() } - + private func stubReturningCustomerResponse() { stubSessions(fileMock: .elementsSessionsPaymentMethod_savedPM_200) stubPaymentMethods(stubRequestCallback: { urlRequest in @@ -869,7 +920,7 @@ class PaymentSheetSnapshotTests: FBSnapshotTestCase { }, fileMock: .saved_payment_methods_200) stubCustomers() } - + } private extension PaymentSheet.Appearance { @@ -880,7 +931,6 @@ private extension PaymentSheet.Appearance { var font = PaymentSheet.Appearance.Font() font.sizeScaleFactor = 0.85 font.base = UIFont(name: "AvenirNext-Regular", size: 12)! - appearance.cornerRadius = 0.0 appearance.borderWidth = 2.0 @@ -905,13 +955,13 @@ private extension PaymentSheet.Appearance { appearance.font = font appearance.colors = colors - + return appearance } - + static var snapshotPrimaryButtonTestTheme: PaymentSheet.Appearance { var appearance = PaymentSheet.Appearance.snapshotTestTheme - + var button = PaymentSheet.Appearance.PrimaryButton() button.backgroundColor = .purple button.textColor = .red @@ -926,12 +976,10 @@ private extension PaymentSheet.Appearance { appearance.primaryButton = button - return appearance } } - public class ClassForBundle { } @_spi(STP) public enum FileMock: String, MockData { public typealias ResponseType = StripeFile diff --git a/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheetUITest.swift b/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheetUITest.swift index 44c5a7976d3..96c401ddfbf 100644 --- a/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheetUITest.swift +++ b/Example/PaymentSheet Example/PaymentSheetUITest/PaymentSheetUITest.swift @@ -10,7 +10,7 @@ import XCTest class PaymentSheetUITest: XCTestCase { var app: XCUIApplication! - + override func setUpWithError() throws { // Put setup code here. This method is called before the invocation of each test method in the class. @@ -46,12 +46,12 @@ class PaymentSheetUITest: XCTestCase { let buyButton = app.staticTexts["Buy"] XCTAssertTrue(buyButton.waitForExistence(timeout: 60.0)) buyButton.tap() - + let numberField = app.textFields["Card number"] XCTAssertTrue(numberField.waitForExistence(timeout: 60.0)) numberField.tap() numberField.typeText("378282246310005") - + // Test that Amex card changes "CVC" -> "CVV" and allows 4 digits let cvvField = app.textFields["CVV"] XCTAssertTrue(cvvField.waitForExistence(timeout: 10.0)) @@ -59,12 +59,12 @@ class PaymentSheetUITest: XCTestCase { let expField = app.textFields["expiration date"] XCTAssertTrue((expField.value as? String)?.isEmpty ?? true) XCTAssertNoThrow(expField.typeText("1228")) - + XCTAssertTrue((cvvField.value as? String)?.isEmpty ?? true) XCTAssertNoThrow(cvvField.typeText("1234")) - + app.toolbars.buttons["Done"].tap() // Country picker toolbar's "Done" button - + let postalField = app.textFields["ZIP"] XCTAssertTrue((postalField.value as? String)?.isEmpty ?? true) XCTAssertNoThrow(postalField.typeText("12345")) @@ -105,7 +105,7 @@ class PaymentSheetUITest: XCTestCase { // This test case is testing a feature not available when Link is on, // so we must manually turn off Link. "automatic_payment_methods": "off", - "link": "off" + "link": "off", ]) var paymentMethodButton = app.buttons["Select Payment Method"] @@ -149,7 +149,7 @@ class PaymentSheetUITest: XCTestCase { saveThisCardToggle.tap() } XCTAssertTrue(saveThisCardToggle.isSelected) - + // Complete payment app.buttons["Continue"].tap() app.buttons["Checkout (Custom)"].tap() @@ -216,17 +216,17 @@ class PaymentSheetUITest: XCTestCase { XCTAssertTrue(successText.waitForExistence(timeout: 10.0)) XCTAssertNotNil(successText.label.range(of: "Your order is confirmed!")) } - + func testIdealPaymentMethodHasTextFieldsAndDropdown() throws { loadPlayground(app, settings: [ "customer_mode": "new", "apple_pay": "off", - "currency": "EUR" + "currency": "EUR", ]) app.buttons["Checkout (Complete)"].tap() let payButton = app.buttons["Pay €50.99"] - + guard let iDEAL = scroll(collectionView: app.collectionViews.firstMatch, toFindCellWithId: "iDEAL") else { XCTFail() return @@ -238,14 +238,14 @@ class PaymentSheetUITest: XCTestCase { name.tap() name.typeText("John Doe") name.typeText(XCUIKeyboardKey.return.rawValue) - + let bank = app.textFields["iDEAL Bank"] bank.tap() app.pickerWheels.firstMatch.adjust(toPickerWheelValue: "ASN Bank") app.toolbars.buttons["Done"].tap() payButton.tap() - + let webviewCloseButton = app.otherElements["TopBrowserBar"].buttons["Close"] XCTAssertTrue(webviewCloseButton.waitForExistence(timeout: 10.0)) webviewCloseButton.tap() @@ -255,7 +255,7 @@ class PaymentSheetUITest: XCTestCase { loadPlayground(app, settings: [ "customer_mode": "new", "apple_pay": "off", - "currency": "EUR" + "currency": "EUR", ]) app.buttons["Checkout (Complete)"].tap() @@ -289,7 +289,7 @@ class PaymentSheetUITest: XCTestCase { loadPlayground(app, settings: [ "customer_mode": "new", "apple_pay": "off", - "currency": "EUR" + "currency": "EUR", ]) app.buttons["Checkout (Complete)"].tap() @@ -318,7 +318,7 @@ class PaymentSheetUITest: XCTestCase { loadPlayground(app, settings: [ "customer_mode": "new", "apple_pay": "off", - "currency": "EUR" + "currency": "EUR", ]) app.buttons["Checkout (Complete)"].tap() @@ -358,12 +358,12 @@ class PaymentSheetUITest: XCTestCase { func testKlarnaPaymentMethod() throws { loadPlayground(app, settings: [ "customer_mode": "new", // new customer - "automatic_payment_methods": "off" + "automatic_payment_methods": "off", ]) app.buttons["Checkout (Complete)"].tap() let payButton = app.buttons["Pay $50.99"] - + // Select Klarna guard let klarna = scroll(collectionView: app.collectionViews.firstMatch, toFindCellWithId: "Klarna") else { XCTFail() @@ -376,12 +376,12 @@ class PaymentSheetUITest: XCTestCase { name.tap() name.typeText("foo@bar.com") name.typeText(XCUIKeyboardKey.return.rawValue) - + // Country should be pre-filled - + // Attempt payment payButton.tap() - + // Close the webview, no need to see the successful pay let webviewCloseButton = app.otherElements["TopBrowserBar"].buttons["Close"] XCTAssertTrue(webviewCloseButton.waitForExistence(timeout: 10.0)) @@ -392,13 +392,12 @@ class PaymentSheetUITest: XCTestCase { loadPlayground(app, settings: [ "customer_mode": "new", // new customer "automatic_payment_methods": "off", - "shipping": "on w/ defaults" // collect shipping + "shipping": "on w/ defaults", // collect shipping ]) app.buttons["Checkout (Complete)"].tap() let payButton = app.buttons["Pay $50.99"] - // Select affirm guard let affirm = scroll(collectionView: app.collectionViews.firstMatch, toFindCellWithId: "Affirm") else { XCTFail() @@ -407,17 +406,17 @@ class PaymentSheetUITest: XCTestCase { affirm.tap() XCTAssertTrue(payButton.isEnabled) - + // Attempt payment, should fail payButton.tap() - + } func testUSBankAccountPaymentMethod() throws { loadPlayground(app, settings: [ "customer_mode": "new", "automatic_payment_methods": "off", - "allows_delayed_pms": "true" + "allows_delayed_pms": "true", ]) app.buttons["Checkout (Complete)"].tap() @@ -452,20 +451,18 @@ class PaymentSheetUITest: XCTestCase { let unselectedMandate = "By continuing, you agree to authorize payments pursuant to these terms." XCTAssertTrue(app.textViews[expectDefaultSelectionOn ? selectedMandate : unselectedMandate].waitForExistence(timeout: 5)) - - let saveThisAccountToggle = app.switches["Save this account for future Example, Inc. payments"] saveThisAccountToggle.tap() XCTAssertTrue(app.textViews[expectDefaultSelectionOn ? unselectedMandate : selectedMandate].waitForExistence(timeout: 5)) // no pay button tap because linked account is stubbed/fake in UI test } - + func testUPIPaymentMethod() throws { loadPlayground(app, settings: [ "customer_mode": "new", "merchant_country_code": "IN", - "currency": "INR" + "currency": "INR", ]) app.buttons["Checkout (Complete)"].tap() @@ -485,12 +482,12 @@ class PaymentSheetUITest: XCTestCase { payButton.tap() } - + func testUPIPaymentMethod_invalidVPA() throws { loadPlayground(app, settings: [ "customer_mode": "new", "merchant_country_code": "IN", - "currency": "INR" + "currency": "INR", ]) app.buttons["Checkout (Complete)"].tap() @@ -523,7 +520,7 @@ extension PaymentSheetUITest { loadPlayground(app, settings: [ "customer_mode": "new", "automatic_payment_methods": "off", - "link": "on" + "link": "on", ]) app.buttons["Checkout (Complete)"].tap() @@ -535,7 +532,7 @@ extension PaymentSheetUITest { let emailField = app.textFields["Email"] emailField.tap() emailField.typeText("mobile-payments-sdk-ci+\(UUID())@stripe.com") - + let phoneField = app.textFields["Phone"] // Phone field appears after the network call finishes. We want to wait for it to appear. XCTAssert(phoneField.waitForExistence(timeout: 10)) @@ -563,7 +560,7 @@ extension PaymentSheetUITest { loadPlayground(app, settings: [ "customer_mode": "new", "automatic_payment_methods": "off", - "link": "on" + "link": "on", ]) app.buttons["Checkout (Complete)"].tap() @@ -601,7 +598,7 @@ extension PaymentSheetUITest { loadPlayground(app, settings: [ "customer_mode": "new", "automatic_payment_methods": "off", - "link": "on" + "link": "on", ]) app.buttons["Checkout (Complete)"].tap() @@ -676,7 +673,7 @@ extension PaymentSheetUITest { loadPlayground(app, settings: [ "customer_mode": "new", "automatic_payment_methods": "off", - "link": "on" + "link": "on", ]) app.buttons["Checkout (Complete)"].tap() @@ -694,7 +691,7 @@ extension PaymentSheetUITest { loadPlayground(app, settings: [ "customer_mode": "new", "automatic_payment_methods": "off", - "link": "on" + "link": "on", ]) let paymentMethodButton = app.buttons["Select Payment Method"] diff --git a/Example/PaymentSheet Example/PaymentSheetUITest/XCUITest+Utilities.swift b/Example/PaymentSheet Example/PaymentSheetUITest/XCUITest+Utilities.swift index 8c20c5694b7..d1b26262c75 100644 --- a/Example/PaymentSheet Example/PaymentSheetUITest/XCUITest+Utilities.swift +++ b/Example/PaymentSheet Example/PaymentSheetUITest/XCUITest+Utilities.swift @@ -30,7 +30,7 @@ extension XCUIElement { testCase.waitForExpectations(timeout: 15.0, handler: nil) self.forceTapElement() } - + @discardableResult func waitForExistenceAndTap() -> Bool { guard waitForExistence(timeout: 4) else { @@ -57,38 +57,37 @@ extension XCUIApplication { } // https://gist.github.com/jlnquere/d2cd529874ca73624eeb7159e3633d0f -func scroll(collectionView: XCUIElement, toFindCellWithId identifier:String) -> XCUIElement? { +func scroll(collectionView: XCUIElement, toFindCellWithId identifier: String) -> XCUIElement? { guard collectionView.elementType == .collectionView else { fatalError("XCUIElement is not a collectionView.") } var reachedTheEnd = false var allVisibleElements = [String]() - + while !reachedTheEnd { let cell = collectionView.cells[identifier] - + // Did we find our cell ? if cell.exists { return cell } // If not: we store the list of all the elements we've got in the CollectionView - let allElements = collectionView.cells.allElementsBoundByIndex.map({$0.identifier}) - + let allElements = collectionView.cells.allElementsBoundByIndex.map({ $0.identifier }) + // Did we read then end of the CollectionView ? // i.e: do we have the same elements visible than before scrolling ? reachedTheEnd = (allElements == allVisibleElements) allVisibleElements = allElements - + // Then, we do a scroll right on the scrollview let startCoordinate = collectionView.coordinate(withNormalizedOffset: CGVector(dx: 0.9, dy: 0.99)) - startCoordinate.press(forDuration: 0.01, thenDragTo: collectionView.coordinate(withNormalizedOffset:CGVector(dx: 0.1, dy: 0.99))) + startCoordinate.press(forDuration: 0.01, thenDragTo: collectionView.coordinate(withNormalizedOffset: CGVector(dx: 0.1, dy: 0.99))) } return nil } - extension XCTestCase { func fillCardData(_ app: XCUIApplication, container: XCUIElement? = nil) throws { let context = container ?? app @@ -107,7 +106,7 @@ extension XCTestCase { expectation(for: exists, evaluatedWith: target, handler: nil) waitForExpectations(timeout: 60.0, handler: nil) } - + func reload(_ app: XCUIApplication) { app.buttons["Reload PaymentSheet"].tap() diff --git a/Example/PaymentSheet Example/Project.swift b/Example/PaymentSheet Example/Project.swift index 1304f82d921..813006f2d56 100644 --- a/Example/PaymentSheet Example/Project.swift +++ b/Example/PaymentSheet Example/Project.swift @@ -111,6 +111,6 @@ let project = Project( expandVariableFromTarget: "PaymentSheet Example" ), runAction: .runAction(executable: "PaymentSheet Example") - ) + ), ] ) diff --git a/Example/UI Examples/Project.swift b/Example/UI Examples/Project.swift index 4b3867f6f26..ba70a8d223f 100644 --- a/Example/UI Examples/Project.swift +++ b/Example/UI Examples/Project.swift @@ -43,14 +43,14 @@ let project = Project( settings: .stripeTargetSettings( baseXcconfigFilePath: "BuildConfigurations/UI-Examples" ) - ) + ), ], schemes: [ Scheme( name: "UI Examples", buildAction: .buildAction(targets: ["UI Examples"]), runAction: .runAction(executable: "UI Examples") - ) + ), ], additionalFiles: "README.md" ) diff --git a/Example/UI Examples/UI Examples/Source/AppDelegate.swift b/Example/UI Examples/UI Examples/Source/AppDelegate.swift index d7de736075f..f382ddf3cf9 100644 --- a/Example/UI Examples/UI Examples/Source/AppDelegate.swift +++ b/Example/UI Examples/UI Examples/Source/AppDelegate.swift @@ -20,7 +20,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ) -> Bool { // STPTestingPublishableKey STPAPIClient.shared.publishableKey = "pk_test_ErsyMEOTudSjQR8hh0VrQr5X008sBXGOu6" - + let rootVC = BrowseViewController() let navController = UINavigationController(rootViewController: rootVC) let window = UIWindow(frame: UIScreen.main.bounds) diff --git a/Example/UI Examples/UI Examples/Source/BrowseViewController.swift b/Example/UI Examples/UI Examples/Source/BrowseViewController.swift index 8807c89bb19..bead7e5c2eb 100644 --- a/Example/UI Examples/UI Examples/Source/BrowseViewController.swift +++ b/Example/UI Examples/UI Examples/Source/BrowseViewController.swift @@ -7,8 +7,8 @@ // import PassKit -import UIKit import SwiftUI +import UIKit @testable import Stripe @@ -24,7 +24,7 @@ class BrowseViewController: UITableViewController, STPAddCardViewControllerDeleg return 10 } } - + case STPPaymentCardTextField case STPAddCardViewController case STPAddCardViewControllerWithAddress @@ -217,7 +217,8 @@ class BrowseViewController: UITableViewController, STPAddCardViewControllerDeleg func addCardViewController( _ addCardViewController: STPAddCardViewController, - didCreatePaymentMethod paymentMethod: STPPaymentMethod, completion: @escaping STPErrorBlock + didCreatePaymentMethod paymentMethod: STPPaymentMethod, + completion: @escaping STPErrorBlock ) { dismiss(animated: true, completion: nil) } @@ -253,14 +254,16 @@ class BrowseViewController: UITableViewController, STPAddCardViewControllerDeleg func shippingAddressViewController( _ addressViewController: STPShippingAddressViewController, - didFinishWith address: STPAddress, shippingMethod method: PKShippingMethod? + didFinishWith address: STPAddress, + shippingMethod method: PKShippingMethod? ) { self.customerContext.updateCustomer(withShippingAddress: address, completion: nil) dismiss(animated: true, completion: nil) } func shippingAddressViewController( - _ addressViewController: STPShippingAddressViewController, didEnter address: STPAddress, + _ addressViewController: STPShippingAddressViewController, + didEnter address: STPAddress, completion: @escaping STPShippingMethodsCompletionBlock ) { let upsGround = PKShippingMethod() diff --git a/Example/UI Examples/UI Examples/Source/CardFormViewController.swift b/Example/UI Examples/UI Examples/Source/CardFormViewController.swift index a142e871b86..24c6779bd44 100644 --- a/Example/UI Examples/UI Examples/Source/CardFormViewController.swift +++ b/Example/UI Examples/UI Examples/Source/CardFormViewController.swift @@ -24,7 +24,7 @@ class CardFormViewController: UIViewController { let cardForm = STPCardFormView() cardForm.translatesAutoresizingMaskIntoConstraints = false view.addSubview(cardForm) - + NSLayoutConstraint.activate([ cardForm.leadingAnchor.constraint(equalToSystemSpacingAfter: view.safeAreaLayoutGuide.leadingAnchor, multiplier: 4), view.safeAreaLayoutGuide.trailingAnchor.constraint(equalToSystemSpacingAfter: cardForm.trailingAnchor, multiplier: 4), @@ -34,11 +34,11 @@ class CardFormViewController: UIViewController { view.safeAreaLayoutGuide.bottomAnchor.constraint(greaterThanOrEqualToSystemSpacingBelow: cardForm.bottomAnchor, multiplier: 1), cardForm.centerYAnchor.constraint(equalTo: view.centerYAnchor), ]) - + navigationItem.leftBarButtonItem = UIBarButtonItem( barButtonSystemItem: .done, target: self, action: #selector(done)) } - + @objc func done() { dismiss(animated: true, completion: nil) } diff --git a/Example/UI Examples/UI Examples/Source/PaymentMethodMessagingViewController.swift b/Example/UI Examples/UI Examples/Source/PaymentMethodMessagingViewController.swift index 94b8c1d697f..15baa3a5d9b 100644 --- a/Example/UI Examples/UI Examples/Source/PaymentMethodMessagingViewController.swift +++ b/Example/UI Examples/UI Examples/Source/PaymentMethodMessagingViewController.swift @@ -46,14 +46,14 @@ class PaymentMethodMessagingViewController: UIViewController { @objc func done() { dismiss(animated: true, completion: nil) } - + private func addPaymentMethodMessagingView(_ paymentMethodMessagingView: PaymentMethodMessagingView) { view.addSubview(paymentMethodMessagingView) paymentMethodMessagingView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ paymentMethodMessagingView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 15), paymentMethodMessagingView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -15), - paymentMethodMessagingView.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor) + paymentMethodMessagingView.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor), ]) } } diff --git a/Example/UI Examples/UI Examples/Source/SwiftUICardFormView.swift b/Example/UI Examples/UI Examples/Source/SwiftUICardFormView.swift index b4aee1b3c88..d1533b5f9f7 100644 --- a/Example/UI Examples/UI Examples/Source/SwiftUICardFormView.swift +++ b/Example/UI Examples/UI Examples/Source/SwiftUICardFormView.swift @@ -6,15 +6,15 @@ // Copyright © 2021 Stripe. All rights reserved. // -import SwiftUI import Stripe +import SwiftUI @available(iOS 13.0.0, *) struct SwiftUICardFormView: View { - + @State private var paymentMethodParams: STPPaymentMethodParams = STPPaymentMethodParams() @State private var cardFormIsComplete: Bool = false - + var body: some View { VStack { STPCardFormView.Representable(paymentMethodParams: $paymentMethodParams, diff --git a/Stripe3DS2/Project.swift b/Stripe3DS2/Project.swift index 79d166c5e89..f9d34aac1fa 100644 --- a/Stripe3DS2/Project.swift +++ b/Stripe3DS2/Project.swift @@ -12,7 +12,7 @@ let project = Project( .remote( url: "https://github.com/uber/ios-snapshot-test-case", requirement: .upToNextMajor(from: "8.0.0") - ) + ), ], settings: .settings( configurations: [ diff --git a/StripePaymentsUI/StripePaymentsUI/Source/Helpers/STPStringUtils.swift b/StripePaymentsUI/StripePaymentsUI/Source/Helpers/STPStringUtils.swift index 7306446a04d..b2f80c0c542 100644 --- a/StripePaymentsUI/StripePaymentsUI/Source/Helpers/STPStringUtils.swift +++ b/StripePaymentsUI/StripePaymentsUI/Source/Helpers/STPStringUtils.swift @@ -9,10 +9,9 @@ import Foundation @_spi(STP) public class STPStringUtils: NSObject { + // This code was adapted from Stripe.js /// Reformats an expiration date with a four-digit year to one with a two digit year. /// Ex: `01/2021` to `01/21`. - // This code was adapted from Stripe.js - static let expirationDateStringRegex: NSRegularExpression = { return try! NSRegularExpression( pattern: "^(\\d{2}\\D{1,3})(\\d{1,4})?", diff --git a/StripePaymentsUI/StripePaymentsUI/Source/Internal/UI/Views/Inputs/Card/STPCardCVCInputTextField.swift b/StripePaymentsUI/StripePaymentsUI/Source/Internal/UI/Views/Inputs/Card/STPCardCVCInputTextField.swift index fab295c9591..eb0edd7588d 100644 --- a/StripePaymentsUI/StripePaymentsUI/Source/Internal/UI/Views/Inputs/Card/STPCardCVCInputTextField.swift +++ b/StripePaymentsUI/StripePaymentsUI/Source/Internal/UI/Views/Inputs/Card/STPCardCVCInputTextField.swift @@ -41,6 +41,7 @@ import UIKit ) // set card brand in a defer to ensure didSet is called updating the formatter & validator + // swiftlint:disable:next inert_defer defer { self.cardBrand = prefillDetails?.cardBrand ?? .unknown } diff --git a/StripePaymentsUI/StripePaymentsUI/Source/Internal/UI/Views/Inputs/Card/STPCardNumberInputTextFieldValidator.swift b/StripePaymentsUI/StripePaymentsUI/Source/Internal/UI/Views/Inputs/Card/STPCardNumberInputTextFieldValidator.swift index 416a8504d3f..2658df6cbb1 100644 --- a/StripePaymentsUI/StripePaymentsUI/Source/Internal/UI/Views/Inputs/Card/STPCardNumberInputTextFieldValidator.swift +++ b/StripePaymentsUI/StripePaymentsUI/Source/Internal/UI/Views/Inputs/Card/STPCardNumberInputTextFieldValidator.swift @@ -64,7 +64,7 @@ class STPCardNumberInputTextFieldValidator: STPInputTextFieldValidator { if STPBINController.shared.hasBINRanges(forPrefix: inputValue) { updateValidationState() } else { - STPBINController.shared.retrieveBINRanges(forPrefix: inputValue) { (result) in + STPBINController.shared.retrieveBINRanges(forPrefix: inputValue) { (_) in // Needs better error handling and analytics https://jira.corp.stripe.com/browse/MOBILESDK-110 updateValidationState() } diff --git a/StripePaymentsUI/StripePaymentsUI/Source/Internal/UI/Views/Inputs/Card/STPPostalCodeInputTextFieldValidator.swift b/StripePaymentsUI/StripePaymentsUI/Source/Internal/UI/Views/Inputs/Card/STPPostalCodeInputTextFieldValidator.swift index 5508c30c7e1..6680bdc3072 100644 --- a/StripePaymentsUI/StripePaymentsUI/Source/Internal/UI/Views/Inputs/Card/STPPostalCodeInputTextFieldValidator.swift +++ b/StripePaymentsUI/StripePaymentsUI/Source/Internal/UI/Views/Inputs/Card/STPPostalCodeInputTextFieldValidator.swift @@ -62,7 +62,7 @@ class STPPostalCodeInputTextFieldValidator: STPInputTextFieldValidator { // primarily a backup for missing api error strings) validationState = .invalid(errorMessage: defaultErrorMessage) case .incomplete: - var incompleteDescription: String? = nil + var incompleteDescription: String? if let inputValue = inputValue, !inputValue.isEmpty { if countryCode?.uppercased() == "US" { incompleteDescription = String.Localized.your_zip_is_incomplete diff --git a/StripePaymentsUI/StripePaymentsUI/Source/Internal/UI/Views/Inputs/STPInputTextField.swift b/StripePaymentsUI/StripePaymentsUI/Source/Internal/UI/Views/Inputs/STPInputTextField.swift index ec501010ea3..a1545cefdd6 100644 --- a/StripePaymentsUI/StripePaymentsUI/Source/Internal/UI/Views/Inputs/STPInputTextField.swift +++ b/StripePaymentsUI/StripePaymentsUI/Source/Internal/UI/Views/Inputs/STPInputTextField.swift @@ -15,7 +15,7 @@ public class STPInputTextField: STPFloatingPlaceholderTextField, STPFormInputVal let validator: STPInputTextFieldValidator - weak var formContainer: STPFormContainer? = nil + weak var formContainer: STPFormContainer? var wantsAutoFocus: Bool { return true @@ -90,7 +90,7 @@ public class STPInputTextField: STPFloatingPlaceholderTextField, STPFormInputVal let text = self.text ?? "" let formatted = formatter.formattedText(from: text, with: defaultTextAttributes) if formatted != attributedText { - var updatedCursorPosition: UITextPosition? = nil + var updatedCursorPosition: UITextPosition? if let selection = selectedTextRange { let cursorPosition = offset(from: beginningOfDocument, to: selection.start) updatedCursorPosition = position( @@ -250,7 +250,7 @@ public class STPInputTextField: STPFloatingPlaceholderTextField, STPFormInputVal input: "\u{08}", modifierFlags: .command, action: #selector(commandDeleteBackwards) - ) + ), ] } diff --git a/StripePaymentsUI/StripePaymentsUI/Source/Internal/UI/Views/STPFormTextField.swift b/StripePaymentsUI/StripePaymentsUI/Source/Internal/UI/Views/STPFormTextField.swift index ae1af4b5a75..43c50eddd20 100644 --- a/StripePaymentsUI/StripePaymentsUI/Source/Internal/UI/Views/STPFormTextField.swift +++ b/StripePaymentsUI/StripePaymentsUI/Source/Internal/UI/Views/STPFormTextField.swift @@ -329,7 +329,7 @@ import UIKit input: "\u{08}", modifierFlags: .command, action: #selector(commandDeleteBackwards) - ) + ), ] } diff --git a/StripePaymentsUI/StripePaymentsUI/Source/UI Components/PaymentMethodMessagingView.swift b/StripePaymentsUI/StripePaymentsUI/Source/UI Components/PaymentMethodMessagingView.swift index b07e55b040a..4c0eb5f372e 100644 --- a/StripePaymentsUI/StripePaymentsUI/Source/UI Components/PaymentMethodMessagingView.swift +++ b/StripePaymentsUI/StripePaymentsUI/Source/UI Components/PaymentMethodMessagingView.swift @@ -172,7 +172,7 @@ import WebKit extension PaymentMethodMessagingView { /// A regex that matches tags and captures its url static let imgTagRegex = try! NSRegularExpression(pattern: "") - + /// - Returns: The given `html` string with tags replaced with tags, and a list of the URLs contained in the replaced tags static func htmlReplacingImageTags(html: String) -> (String, [URL]) { var html = html @@ -192,7 +192,7 @@ extension PaymentMethodMessagingView { } return (html, images) } - + static func makeCSS(for font: UIFont) -> String { // To set the font, we prepend CSS to the given `html` before converting it to an NSAttributedString let isSystemFont = font.familyName == UIFont.systemFont(ofSize: font.pointSize).familyName @@ -206,7 +206,7 @@ extension PaymentMethodMessagingView { """ } - + static func makeAttributedString( from html: String, configuration: Configuration @@ -224,7 +224,7 @@ extension PaymentMethodMessagingView { } // 4. Replace the links in the attributed string with image attachments let mAttributedString = NSMutableAttributedString(attributedString: attributedString) - mAttributedString.enumerateAttribute(.link, in: NSRange(0.. UIImage? { let request = apiClient.configuredRequest(for: url) let data: Data @@ -301,7 +301,7 @@ extension PaymentMethodMessagingView { } else { /// Adapted from https://www.swiftbysundell.com/articles/making-async-system-apis-backward-compatible/ data = try await withCheckedThrowingContinuation { continuation in - let task = apiClient.urlSession.dataTask(with: request) { data, response, error in + let task = apiClient.urlSession.dataTask(with: request) { data, _, error in guard let data = data else { let error = error ?? URLError(.badServerResponse) return continuation.resume(throwing: error) @@ -353,7 +353,7 @@ extension PaymentMethodMessagingView { extension NSAttributedString { func withFontSize(_ size: CGFloat) -> NSAttributedString { let mutable = NSMutableAttributedString(attributedString: self) - mutable.enumerateAttributes(in: NSRange(0.. Bool { // grab the next first responder before calling super (which will cause any current first responder to resign) - var firstResponder: STPFormInput? = nil + var firstResponder: STPFormInput? if currentFirstResponderField() != nil { // we are already first responder, move to next field sequentially firstResponder = nextInSequenceFirstResponderField() ?? sequentialFields.first @@ -250,7 +250,7 @@ public class STPFormView: UIView, STPFormInputValidationObserver { var sequentialFields: [STPFormInput] { return sections.reduce(into: [STPFormInput]()) { (result, section) in result.append( - contentsOf: section.rows.reduce(into: [STPInputTextField]()) { (innerResult, row) in + contentsOf: section.rows.reduce(into: [STPInputTextField]()) { (_, row) in for input in row { if !input.isHidden { result.append(input) diff --git a/StripePaymentsUI/StripePaymentsUI/Source/UI Components/STPPaymentCardTextField.swift b/StripePaymentsUI/StripePaymentsUI/Source/UI Components/STPPaymentCardTextField.swift index dd44781a378..04cc8795107 100644 --- a/StripePaymentsUI/StripePaymentsUI/Source/UI Components/STPPaymentCardTextField.swift +++ b/StripePaymentsUI/StripePaymentsUI/Source/UI Components/STPPaymentCardTextField.swift @@ -695,8 +695,8 @@ open class STPPaymentCardTextField: UIControl, UIKeyInput, STPFormTextFieldDeleg STPPaymentCardTextFieldViewModel() @objc internal var internalCardParams = STPPaymentMethodCardParams() - @objc internal var internalBillingDetails: STPPaymentMethodBillingDetails? = nil - @objc internal var internalMetadata: [String: String]? = nil + @objc internal var internalBillingDetails: STPPaymentMethodBillingDetails? + @objc internal var internalMetadata: [String: String]? @objc @_spi(STP) public var allFields: [STPFormTextField] = [] private lazy var sizingField: STPFormTextField = { @@ -1063,7 +1063,7 @@ open class STPPaymentCardTextField: UIControl, UIKeyInput, STPFormTextFieldDeleg let sortedCardNumberFormat = (STPCardValidator.cardNumberFormat(forCardNumber: cardNumber ?? "") as NSArray) .sortedArray( - using: #selector(getter:NSNumber.uintValue) + using: #selector(getter: NSNumber.uintValue) ) as! [NSNumber] let fragmentLength = STPCardValidator.fragmentLength(for: currentBrand) let maxLength: Int = max(Int(fragmentLength), sortedCardNumberFormat.last!.intValue) diff --git a/Testers/IntegrationTester/Common/IntegrationMethods.swift b/Testers/IntegrationTester/Common/IntegrationMethods.swift index a91a7a6ccc0..b41e41ad5a1 100644 --- a/Testers/IntegrationTester/Common/IntegrationMethods.swift +++ b/Testers/IntegrationTester/Common/IntegrationMethods.swift @@ -8,7 +8,7 @@ import Foundation import Stripe -public enum IntegrationMethod : String, CaseIterable { +public enum IntegrationMethod: String, CaseIterable { case card = "Card" case cardSetupIntents = "Card (SetupIntents)" case applePay = "Apple Pay" @@ -32,102 +32,96 @@ public enum IntegrationMethod : String, CaseIterable { // MARK: IntegrationMethod PaymentMethod/Sources Params extension IntegrationMethod { public var defaultPaymentMethodParams: STPPaymentMethodParams { - get { - switch self { - case .fpx: - let fpx = STPPaymentMethodFPXParams() - fpx.bank = .HSBC - return STPPaymentMethodParams(fpx: fpx, billingDetails: nil, metadata: nil) - case .iDEAL: - let ideal = STPPaymentMethodiDEALParams() - return STPPaymentMethodParams(iDEAL: ideal, billingDetails: nil, metadata: nil) - case .sofort: - let sofort = STPPaymentMethodSofortParams() - sofort.country = "NL" - return STPPaymentMethodParams(sofort: sofort, billingDetails: nil, metadata: nil) - case .sepaDebit: - let sepaDebit = STPPaymentMethodSEPADebitParams() - return STPPaymentMethodParams(sepaDebit: sepaDebit, billingDetails: Self.defaultBillingDetails, metadata: nil) - case .alipay: - let alipay = STPPaymentMethodAlipayParams() - return STPPaymentMethodParams(alipay: alipay, billingDetails: nil, metadata: nil) - case .bacsDebit: - // You must provide UI to collect the following hard-coded customer information. Bacs in - // the UK has strict requirements around customer-facing mandate collection forms. Stripe - // needs to approve any forms for collecting mandates. Contact bacs-debits@stripe.com with - // any questions. - let bacsDebit = STPPaymentMethodBacsDebitParams() - bacsDebit.sortCode = "108800" - bacsDebit.accountNumber = "00012345" - return STPPaymentMethodParams(bacsDebit: bacsDebit, billingDetails: Self.defaultBillingDetails, metadata: nil) - case .aubecsDebit: - let aubecsDebit = STPPaymentMethodAUBECSDebitParams() - return STPPaymentMethodParams(aubecsDebit: aubecsDebit, billingDetails: Self.defaultBillingDetails, metadata: nil) - case .giropay: - let giropay = STPPaymentMethodGiropayParams() - return STPPaymentMethodParams(giropay: giropay, billingDetails: Self.defaultBillingDetails, metadata: nil) - case .przelewy24: - let przelewy24 = STPPaymentMethodPrzelewy24Params() - return STPPaymentMethodParams(przelewy24: przelewy24, billingDetails: Self.defaultBillingDetails, metadata: nil) - case .bancontact: - let bancontact = STPPaymentMethodBancontactParams() - return STPPaymentMethodParams(bancontact: bancontact, billingDetails: Self.defaultBillingDetails, metadata: nil) - case .eps: - let eps = STPPaymentMethodEPSParams() - return STPPaymentMethodParams(eps: eps, billingDetails: Self.defaultBillingDetails, metadata: nil) - case .grabpay: - let grabpay = STPPaymentMethodGrabPayParams() - return STPPaymentMethodParams(grabPay: grabpay, billingDetails: nil, metadata: nil) - case .oxxo: - let oxxo = STPPaymentMethodOXXOParams() - return STPPaymentMethodParams(oxxo: oxxo, billingDetails: nil, metadata: nil) - case .afterpay: - let afterpay = STPPaymentMethodAfterpayClearpayParams() - return STPPaymentMethodParams(afterpayClearpay: afterpay, billingDetails: nil, metadata: nil) - case .card, - .cardSetupIntents: - let cardParams = STPPaymentMethodCardParams() - cardParams.number = "4242424242424242" - cardParams.expYear = NSNumber(value: Calendar.current.dateComponents([.year], from: Date()).year! % 100 + 2) - cardParams.expMonth = 12 - cardParams.cvc = "123" - return STPPaymentMethodParams(card: cardParams, billingDetails: nil, metadata: nil) - case .weChatPay: + switch self { + case .fpx: + let fpx = STPPaymentMethodFPXParams() + fpx.bank = .HSBC + return STPPaymentMethodParams(fpx: fpx, billingDetails: nil, metadata: nil) + case .iDEAL: + let ideal = STPPaymentMethodiDEALParams() + return STPPaymentMethodParams(iDEAL: ideal, billingDetails: nil, metadata: nil) + case .sofort: + let sofort = STPPaymentMethodSofortParams() + sofort.country = "NL" + return STPPaymentMethodParams(sofort: sofort, billingDetails: nil, metadata: nil) + case .sepaDebit: + let sepaDebit = STPPaymentMethodSEPADebitParams() + return STPPaymentMethodParams(sepaDebit: sepaDebit, billingDetails: Self.defaultBillingDetails, metadata: nil) + case .alipay: + let alipay = STPPaymentMethodAlipayParams() + return STPPaymentMethodParams(alipay: alipay, billingDetails: nil, metadata: nil) + case .bacsDebit: + // You must provide UI to collect the following hard-coded customer information. Bacs in + // the UK has strict requirements around customer-facing mandate collection forms. Stripe + // needs to approve any forms for collecting mandates. Contact bacs-debits@stripe.com with + // any questions. + let bacsDebit = STPPaymentMethodBacsDebitParams() + bacsDebit.sortCode = "108800" + bacsDebit.accountNumber = "00012345" + return STPPaymentMethodParams(bacsDebit: bacsDebit, billingDetails: Self.defaultBillingDetails, metadata: nil) + case .aubecsDebit: + let aubecsDebit = STPPaymentMethodAUBECSDebitParams() + return STPPaymentMethodParams(aubecsDebit: aubecsDebit, billingDetails: Self.defaultBillingDetails, metadata: nil) + case .giropay: + let giropay = STPPaymentMethodGiropayParams() + return STPPaymentMethodParams(giropay: giropay, billingDetails: Self.defaultBillingDetails, metadata: nil) + case .przelewy24: + let przelewy24 = STPPaymentMethodPrzelewy24Params() + return STPPaymentMethodParams(przelewy24: przelewy24, billingDetails: Self.defaultBillingDetails, metadata: nil) + case .bancontact: + let bancontact = STPPaymentMethodBancontactParams() + return STPPaymentMethodParams(bancontact: bancontact, billingDetails: Self.defaultBillingDetails, metadata: nil) + case .eps: + let eps = STPPaymentMethodEPSParams() + return STPPaymentMethodParams(eps: eps, billingDetails: Self.defaultBillingDetails, metadata: nil) + case .grabpay: + let grabpay = STPPaymentMethodGrabPayParams() + return STPPaymentMethodParams(grabPay: grabpay, billingDetails: nil, metadata: nil) + case .oxxo: + let oxxo = STPPaymentMethodOXXOParams() + return STPPaymentMethodParams(oxxo: oxxo, billingDetails: nil, metadata: nil) + case .afterpay: + let afterpay = STPPaymentMethodAfterpayClearpayParams() + return STPPaymentMethodParams(afterpayClearpay: afterpay, billingDetails: nil, metadata: nil) + case .card, + .cardSetupIntents: + let cardParams = STPPaymentMethodCardParams() + cardParams.number = "4242424242424242" + cardParams.expYear = NSNumber(value: Calendar.current.dateComponents([.year], from: Date()).year! % 100 + 2) + cardParams.expMonth = 12 + cardParams.cvc = "123" + return STPPaymentMethodParams(card: cardParams, billingDetails: nil, metadata: nil) + case .weChatPay: // return STPPaymentMethodParams(weChatPay: STPPaymentMethodWeChatPayParams(), billingDetails: nil, metadata: nil) - assertionFailure("WeChat Pay is currently unavailable") - return STPPaymentMethodParams() - case .applePay: - assertionFailure("Not supported by PaymentMethods") - return STPPaymentMethodParams() - } + assertionFailure("WeChat Pay is currently unavailable") + return STPPaymentMethodParams() + case .applePay: + assertionFailure("Not supported by PaymentMethods") + return STPPaymentMethodParams() } } public var defaultPaymentMethodOptions: STPConfirmPaymentMethodOptions? { - get { - switch self { - case .weChatPay: - let pmOptions = STPConfirmPaymentMethodOptions() - pmOptions.weChatPayOptions = STPConfirmWeChatPayOptions(appId: "wx65997d6307c3827d") - return pmOptions - default: - return nil - } + switch self { + case .weChatPay: + let pmOptions = STPConfirmPaymentMethodOptions() + pmOptions.weChatPayOptions = STPConfirmWeChatPayOptions(appId: "wx65997d6307c3827d") + return pmOptions + default: + return nil } } public static var defaultBillingDetails: STPPaymentMethodBillingDetails { - get { - let billingDetails = STPPaymentMethodBillingDetails() - billingDetails.name = "Test Test" - billingDetails.email = "test@example.com" - let address = STPPaymentMethodAddress() - address.line1 = "Threadneedle St" - address.city = "London" - address.postalCode = "EC2R 8AH" - address.country = "GB" - billingDetails.address = address - return billingDetails - } + let billingDetails = STPPaymentMethodBillingDetails() + billingDetails.name = "Test Test" + billingDetails.email = "test@example.com" + let address = STPPaymentMethodAddress() + address.line1 = "Threadneedle St" + address.city = "London" + address.postalCode = "EC2R 8AH" + address.country = "GB" + billingDetails.address = address + return billingDetails } } diff --git a/Testers/IntegrationTester/IntegrationTester/Source/HelperViews/HelperViews.swift b/Testers/IntegrationTester/IntegrationTester/Source/HelperViews/HelperViews.swift index 30a38d18119..9be83bdd105 100644 --- a/Testers/IntegrationTester/IntegrationTester/Source/HelperViews/HelperViews.swift +++ b/Testers/IntegrationTester/IntegrationTester/Source/HelperViews/HelperViews.swift @@ -5,8 +5,8 @@ // Created by David Estes on 2/10/21. // -import SwiftUI import Stripe +import SwiftUI struct PaymentHandlerStatusView: View { let actionStatus: STPPaymentHandlerActionStatus @@ -20,7 +20,7 @@ struct PaymentHandlerStatusView: View { Text("Payment complete!") case .failed: Image(systemName: "xmark.octagon.fill").foregroundColor(.red) - Text("Payment failed! \(lastPaymentError ?? NSError())") + Text("Payment failed! \(lastPaymentError ?? NSError())") // swiftlint:disable:this discouraged_direct_init case .canceled: Image(systemName: "xmark.octagon.fill").foregroundColor(.orange) Text("Payment canceled.") @@ -55,4 +55,3 @@ struct PaymentStatusView: View { .accessibility(identifier: "Payment status view") } } - diff --git a/Testers/IntegrationTester/IntegrationTester/Source/HelperViews/PaymentButton.swift b/Testers/IntegrationTester/IntegrationTester/Source/HelperViews/PaymentButton.swift index ef8436b1206..fa69c5a3f5a 100644 --- a/Testers/IntegrationTester/IntegrationTester/Source/HelperViews/PaymentButton.swift +++ b/Testers/IntegrationTester/IntegrationTester/Source/HelperViews/PaymentButton.swift @@ -11,12 +11,12 @@ Abstract: A button that hosts PKPaymentButton from Apple's Fruta example app. */ -import SwiftUI import PassKit +import SwiftUI struct PaymentButton: View { var action: () -> Void - + var height: CGFloat { #if os(macOS) return 30 @@ -24,7 +24,7 @@ struct PaymentButton: View { return 45 #endif } - + var body: some View { Representable(action: action) .frame(minWidth: 100, maxWidth: 400) @@ -40,23 +40,23 @@ extension PaymentButton { #else typealias ViewRepresentable = NSViewRepresentable #endif - + struct Representable: ViewRepresentable { var action: () -> Void - + init(action: @escaping () -> Void) { self.action = action } - + func makeCoordinator() -> Coordinator { Coordinator(action: action) } - + #if os(iOS) func makeUIView(context: Context) -> UIView { context.coordinator.button } - + func updateUIView(_ rootView: UIView, context: Context) { context.coordinator.action = action } @@ -64,17 +64,17 @@ extension PaymentButton { func makeNSView(context: Context) -> NSView { context.coordinator.button } - + func updateNSView(_ rootView: NSView, context: Context) { context.coordinator.action = action } #endif } - + class Coordinator: NSObject { var action: () -> Void var button = PKPaymentButton(paymentButtonType: .buy, paymentButtonStyle: .automatic) - + init(action: @escaping () -> Void) { self.action = action super.init() @@ -85,7 +85,7 @@ extension PaymentButton { button.target = self #endif } - + @objc func callback(_ sender: Any) { action() diff --git a/Testers/IntegrationTester/IntegrationTester/Source/IntegrationTesterApp.swift b/Testers/IntegrationTester/IntegrationTester/Source/IntegrationTesterApp.swift index f12b0f4fad8..02b1bd267b6 100644 --- a/Testers/IntegrationTester/IntegrationTester/Source/IntegrationTesterApp.swift +++ b/Testers/IntegrationTester/IntegrationTester/Source/IntegrationTesterApp.swift @@ -5,8 +5,8 @@ // Created by David Estes on 2/8/21. // -import SwiftUI import Stripe +import SwiftUI @main struct IntegrationTesterApp: App { @@ -18,13 +18,13 @@ struct IntegrationTesterApp: App { } .onAppear { // As early as possible, configure STPAPIClient with your publishable key. - BackendModel.shared.loadPublishableKey() { publishableKey in + BackendModel.shared.loadPublishableKey { publishableKey in STPAPIClient.shared.publishableKey = publishableKey } - + // Disable hardware keyboards in CI: #if targetEnvironment(simulator) - if (ProcessInfo.processInfo.environment["UITesting"] != nil) { + if ProcessInfo.processInfo.environment["UITesting"] != nil { let setHardwareLayout = NSSelectorFromString("setHardwareLayout:") UITextInputMode.activeInputModes .filter({ $0.responds(to: setHardwareLayout) }) diff --git a/Testers/IntegrationTester/IntegrationTester/Source/MainMenu.swift b/Testers/IntegrationTester/IntegrationTester/Source/MainMenu.swift index 97e1fe1bcac..194a6f9262f 100644 --- a/Testers/IntegrationTester/IntegrationTester/Source/MainMenu.swift +++ b/Testers/IntegrationTester/IntegrationTester/Source/MainMenu.swift @@ -10,7 +10,7 @@ import SwiftUI struct IntegrationView: View { let integrationMethod: IntegrationMethod - + var body: some View { switch integrationMethod { case .card: @@ -47,7 +47,7 @@ struct IntegrationView: View { struct MainMenu: View { var body: some View { NavigationView { - List(IntegrationMethod.allCases, id:\.rawValue) { integrationMethod in + List(IntegrationMethod.allCases, id: \.rawValue) { integrationMethod in NavigationLink(destination: IntegrationView(integrationMethod: integrationMethod)) { Text(integrationMethod.rawValue) } diff --git a/Testers/IntegrationTester/IntegrationTester/Source/Models/ApplePayModel.swift b/Testers/IntegrationTester/IntegrationTester/Source/Models/ApplePayModel.swift index dd83b348551..000fe169445 100644 --- a/Testers/IntegrationTester/IntegrationTester/Source/Models/ApplePayModel.swift +++ b/Testers/IntegrationTester/IntegrationTester/Source/Models/ApplePayModel.swift @@ -6,23 +6,23 @@ // import Foundation -import Stripe import PassKit +import Stripe -class MyApplePayBackendModel : NSObject, ObservableObject, STPApplePayContextDelegate { +class MyApplePayBackendModel: NSObject, ObservableObject, STPApplePayContextDelegate { @Published var paymentStatus: STPPaymentStatus? @Published var lastPaymentError: Error? func pay() { // Configure a payment request let pr = StripeAPI.paymentRequest(withMerchantIdentifier: "merchant.stripetest.banana", country: "US", currency: "USD") - + // You'd generally want to configure at least `.postalAddress` here. // We don't require anything here, as we don't want to enter an address // in CI. pr.requiredShippingContactFields = [] pr.requiredBillingContactFields = [] - + // Configure shipping methods let firstClassShipping = PKShippingMethod(label: "First Class Mail", amount: NSDecimalNumber(string: "10.99")) firstClassShipping.detail = "Arrives in 3-5 days" @@ -32,23 +32,22 @@ class MyApplePayBackendModel : NSObject, ObservableObject, STPApplePayContextDel rocketRidesShipping.identifier = "rocketrides" pr.shippingMethods = [ firstClassShipping, - rocketRidesShipping + rocketRidesShipping, ] - + // Build payment summary items // (You'll generally want to configure these based on the selected address and shipping method. pr.paymentSummaryItems = [ PKPaymentSummaryItem(label: "A very nice computer", amount: NSDecimalNumber(string: "19.99")), PKPaymentSummaryItem(label: "Shipping", amount: NSDecimalNumber(string: "10.99")), - PKPaymentSummaryItem(label: "Stripe Computer Shop", amount: NSDecimalNumber(string: "29.99")) + PKPaymentSummaryItem(label: "Stripe Computer Shop", amount: NSDecimalNumber(string: "29.99")), ] - + // Present the Apple Pay Context: let applePayContext = STPApplePayContext(paymentRequest: pr, delegate: self) applePayContext?.presentApplePay() } - - + func applePayContext(_ context: STPApplePayContext, didCreatePaymentMethod paymentMethod: STPPaymentMethod, paymentInformation: PKPayment, completion: @escaping STPIntentClientSecretCompletionBlock) { // When the Apple Pay sheet is confirmed, create a PaymentIntent on your backend from the provided PKPayment information. BackendModel.shared.fetchPaymentIntent(integrationMethod: .card) { pip in @@ -56,11 +55,11 @@ class MyApplePayBackendModel : NSObject, ObservableObject, STPApplePayContextDel // Call the completion block with the PaymentIntent's client secret. completion(clientSecret, nil) } else { - completion(nil, NSError()) + completion(nil, NSError()) // swiftlint:disable:this discouraged_direct_init } } } - + func applePayContext(_ context: STPApplePayContext, willCompleteWithResult authorizationResult: PKPaymentAuthorizationResult, handler: @escaping (PKPaymentAuthorizationResult) -> Void) { #if compiler(>=5.7) if #available(iOS 16.0, *) { @@ -73,7 +72,7 @@ class MyApplePayBackendModel : NSObject, ObservableObject, STPApplePayContextDel #endif handler(authorizationResult) } - + func applePayContext(_ context: STPApplePayContext, didCompleteWith status: STPPaymentStatus, error: Error?) { // When the payment is complete, display the status. self.paymentStatus = status diff --git a/Testers/IntegrationTester/IntegrationTester/Source/Models/BackendModel.swift b/Testers/IntegrationTester/IntegrationTester/Source/Models/BackendModel.swift index 4bb8dcf6e34..6f7484c6467 100644 --- a/Testers/IntegrationTester/IntegrationTester/Source/Models/BackendModel.swift +++ b/Testers/IntegrationTester/IntegrationTester/Source/Models/BackendModel.swift @@ -5,19 +5,19 @@ // Created by David Estes on 2/10/21. // -import IntegrationTesterCommon import Foundation +import IntegrationTesterCommon import Stripe class BackendModel { // You can replace this with your own backend URL. // Visit https://glitch.com/edit/#!/stripe-integration-tester and click "remix". static let backendAPIURL = URL(string: "https://stripe-integration-tester.glitch.me")! - + static let returnURL = "stp-integration-tester://stripe-redirect" - + public static let shared = BackendModel() - + func fetchPaymentIntent(integrationMethod: IntegrationMethod = .card, completion: @escaping (STPPaymentIntentParams?) -> Void) { let params = ["integration_method": integrationMethod.rawValue] getAPI(method: "create_pi", params: params) { (json) in @@ -29,7 +29,7 @@ class BackendModel { } } - func fetchSetupIntent(params: [String : Any] = [:], completion: @escaping (STPSetupIntentConfirmParams?) -> Void) { + func fetchSetupIntent(params: [String: Any] = [:], completion: @escaping (STPSetupIntentConfirmParams?) -> Void) { getAPI(method: "setup", params: params) { (json) in guard let setupIntentClientSecret = json["setupIntent"] as? String else { completion(nil) @@ -38,7 +38,7 @@ class BackendModel { completion(STPSetupIntentConfirmParams(clientSecret: setupIntentClientSecret)) } } - + func loadPublishableKey(integrationMethod: IntegrationMethod = .card, completion: @escaping (String) -> Void) { let params = ["integration_method": integrationMethod.rawValue] getAPI(method: "get_pub_key", params: params) { (json) in @@ -49,32 +49,32 @@ class BackendModel { } } } - - private func getAPI(method: String, params: [String : Any] = [:], completion: @escaping ([String : Any]) -> Void) { + + private func getAPI(method: String, params: [String: Any] = [:], completion: @escaping ([String: Any]) -> Void) { var request = URLRequest(url: Self.backendAPIURL.appendingPathComponent(method)) request.httpMethod = "POST" - + request.httpBody = try! JSONSerialization.data(withJSONObject: params, options: .prettyPrinted) request.addValue("application/json", forHTTPHeaderField: "Content-Type") request.addValue("application/json", forHTTPHeaderField: "Accept") - - let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in + + let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, _, error) in guard let unwrappedData = data, - let json = try? JSONSerialization.jsonObject(with: unwrappedData, options: []) as? [String : Any], + let json = try? JSONSerialization.jsonObject(with: unwrappedData, options: []) as? [String: Any], let publishableKey = json["publishableKey"] as? String else { if let data = data { print("\(String(decoding: data, as: UTF8.self))") } else { - print("\(error ?? NSError())") + print("\(error ?? NSError())") // swiftlint:disable:this discouraged_direct_init } return } - + // Your app will generally only use one publishable key. In this example app, we use a variety of // different Stripe accounts based in different countries, so we'll want to set the publishable key // each time we set up a new PaymentIntent. STPAPIClient.shared.publishableKey = publishableKey - + DispatchQueue.main.async { completion(json) } diff --git a/Testers/IntegrationTester/IntegrationTester/Source/Models/PaymentsModels.swift b/Testers/IntegrationTester/IntegrationTester/Source/Models/PaymentsModels.swift index 15f3c6dd096..801beb3a72f 100644 --- a/Testers/IntegrationTester/IntegrationTester/Source/Models/PaymentsModels.swift +++ b/Testers/IntegrationTester/IntegrationTester/Source/Models/PaymentsModels.swift @@ -5,16 +5,16 @@ // Created by David Estes on 2/18/21. // -import IntegrationTesterCommon import Foundation +import IntegrationTesterCommon import Stripe -class MySIModel : ObservableObject { +class MySIModel: ObservableObject { @Published var paymentStatus: STPPaymentHandlerActionStatus? @Published var intentParams: STPSetupIntentConfirmParams? @Published var lastPaymentError: NSError? var integrationMethod: IntegrationMethod = .cardSetupIntents - + func prepareSetupIntent() { BackendModel.shared.fetchSetupIntent { sip in sip?.returnURL = BackendModel.returnURL @@ -28,7 +28,7 @@ class MySIModel : ObservableObject { } } -class MyPIModel : ObservableObject { +class MyPIModel: ObservableObject { @Published var paymentStatus: STPPaymentHandlerActionStatus? @Published var paymentIntentParams: STPPaymentIntentParams? @Published var lastPaymentError: NSError? @@ -45,12 +45,12 @@ class MyPIModel : ObservableObject { BackendModel.shared.fetchPaymentIntent(integrationMethod: integrationMethod) { pip in pip?.paymentMethodParams = self.integrationMethod.defaultPaymentMethodParams pip?.paymentMethodOptions = self.integrationMethod.defaultPaymentMethodOptions - + // WeChat Pay is the only supported Payment Method that doesn't allow a returnURL. if self.integrationMethod != .weChatPay { pip?.returnURL = BackendModel.returnURL } - + self.paymentIntentParams = pip } } diff --git a/Testers/IntegrationTester/IntegrationTester/Source/Views/AUBECSDebitView.swift b/Testers/IntegrationTester/IntegrationTester/Source/Views/AUBECSDebitView.swift index 9a50283d495..2ab8dd32875 100644 --- a/Testers/IntegrationTester/IntegrationTester/Source/Views/AUBECSDebitView.swift +++ b/Testers/IntegrationTester/IntegrationTester/Source/Views/AUBECSDebitView.swift @@ -5,8 +5,8 @@ // Created by David Estes on 2/8/21. // -import SwiftUI import Stripe +import SwiftUI struct AUBECSDebitView: View { @StateObject var model = MyPIModel() @@ -38,13 +38,12 @@ struct AUBECSDebitView: View { } } -struct AUBECSDebitView_Preview : PreviewProvider { +struct AUBECSDebitView_Preview: PreviewProvider { static var previews: some View { AUBECSDebitView() } } - extension STPAUBECSDebitFormView { public struct Representable: UIViewRepresentable { @Binding var paymentMethodParams: STPPaymentMethodParams? @@ -52,7 +51,7 @@ extension STPAUBECSDebitFormView { public init(paymentMethodParams: Binding) { _paymentMethodParams = paymentMethodParams } - + public func makeCoordinator() -> Coordinator { return Coordinator(parent: self) } @@ -61,7 +60,7 @@ extension STPAUBECSDebitFormView { let formView = STPAUBECSDebitFormView(companyName: "Test") formView.becsDebitFormDelegate = context.coordinator formView.setContentHuggingPriority(.required, for: .vertical) - + return formView } @@ -80,4 +79,3 @@ extension STPAUBECSDebitFormView { } } } - diff --git a/Testers/IntegrationTester/IntegrationTester/Source/Views/ApplePayView.swift b/Testers/IntegrationTester/IntegrationTester/Source/Views/ApplePayView.swift index 078364977ed..41565bb3cc0 100644 --- a/Testers/IntegrationTester/IntegrationTester/Source/Views/ApplePayView.swift +++ b/Testers/IntegrationTester/IntegrationTester/Source/Views/ApplePayView.swift @@ -5,15 +5,15 @@ // Created by David Estes on 2/8/21. // -import SwiftUI import Stripe +import SwiftUI struct ApplePayView: View { @StateObject var model = MyApplePayBackendModel() var body: some View { VStack { - PaymentButton() { + PaymentButton { model.pay() } .padding() @@ -24,7 +24,7 @@ struct ApplePayView: View { } } -struct ApplePayView_Preview : PreviewProvider { +struct ApplePayView_Preview: PreviewProvider { static var previews: some View { ApplePayView() } diff --git a/Testers/IntegrationTester/IntegrationTester/Source/Views/CardSetupIntentsView.swift b/Testers/IntegrationTester/IntegrationTester/Source/Views/CardSetupIntentsView.swift index ce09cbbd6d6..00012b5da8d 100644 --- a/Testers/IntegrationTester/IntegrationTester/Source/Views/CardSetupIntentsView.swift +++ b/Testers/IntegrationTester/IntegrationTester/Source/Views/CardSetupIntentsView.swift @@ -5,8 +5,8 @@ // Created by David Estes on 2/8/21. // -import SwiftUI import Stripe +import SwiftUI struct CardSetupIntentsView: View { @StateObject var model = MySIModel() @@ -35,7 +35,7 @@ struct CardSetupIntentsView: View { } } -struct CardSetupIntentsView_Preview : PreviewProvider { +struct CardSetupIntentsView_Preview: PreviewProvider { static var previews: some View { CardSetupIntentsView() } diff --git a/Testers/IntegrationTester/IntegrationTester/Source/Views/CardView.swift b/Testers/IntegrationTester/IntegrationTester/Source/Views/CardView.swift index 700eab8fe7f..5fa57be234d 100644 --- a/Testers/IntegrationTester/IntegrationTester/Source/Views/CardView.swift +++ b/Testers/IntegrationTester/IntegrationTester/Source/Views/CardView.swift @@ -5,8 +5,8 @@ // Created by David Estes on 2/8/21. // -import SwiftUI import Stripe +import SwiftUI struct CardView: View { @StateObject var model = MyPIModel() @@ -35,7 +35,7 @@ struct CardView: View { } } -struct CardView_Preview : PreviewProvider { +struct CardView_Preview: PreviewProvider { static var previews: some View { CardView() } diff --git a/Testers/IntegrationTester/IntegrationTester/Source/Views/FPXView.swift b/Testers/IntegrationTester/IntegrationTester/Source/Views/FPXView.swift index 703981a425e..a8cff68e2e1 100644 --- a/Testers/IntegrationTester/IntegrationTester/Source/Views/FPXView.swift +++ b/Testers/IntegrationTester/IntegrationTester/Source/Views/FPXView.swift @@ -5,13 +5,13 @@ // Created by David Estes on 2/8/21. // -import SwiftUI import Stripe +import SwiftUI struct FPXView: View { @StateObject var model = MyPIModel() @State var isConfirmingPayment = false - + var body: some View { VStack { if let paymentStatus = model.paymentStatus { @@ -36,7 +36,7 @@ struct FPXView: View { } } -struct FPXView_Preview : PreviewProvider { +struct FPXView_Preview: PreviewProvider { static var previews: some View { FPXView() } @@ -45,11 +45,11 @@ struct FPXView_Preview : PreviewProvider { extension STPBankSelectionViewController { struct Representable: UIViewControllerRepresentable { let onCompletion: (STPPaymentMethodParams?) -> Void - + public init(onCompletion: @escaping (STPPaymentMethodParams?) -> Void) { self.onCompletion = onCompletion } - + func makeCoordinator() -> Coordinator { return Coordinator(parent: self) } @@ -66,7 +66,7 @@ extension STPBankSelectionViewController { func bankSelectionViewController(_ bankViewController: STPBankSelectionViewController, didCreatePaymentMethodParams paymentMethodParams: STPPaymentMethodParams) { self.parent.onCompletion(paymentMethodParams) } - + var parent: Representable init(parent: Representable) { self.parent = parent @@ -75,7 +75,7 @@ extension STPBankSelectionViewController { vc.delegate = self } - let vc : STPBankSelectionViewController + let vc: STPBankSelectionViewController } } } diff --git a/Testers/IntegrationTester/IntegrationTester/Source/Views/PaymentMethodView.swift b/Testers/IntegrationTester/IntegrationTester/Source/Views/PaymentMethodView.swift index 61f584687da..aa870c983cd 100644 --- a/Testers/IntegrationTester/IntegrationTester/Source/Views/PaymentMethodView.swift +++ b/Testers/IntegrationTester/IntegrationTester/Source/Views/PaymentMethodView.swift @@ -6,14 +6,13 @@ // import IntegrationTesterCommon -import SwiftUI import Stripe +import SwiftUI struct PaymentMethodView: View { let integrationMethod: IntegrationMethod @StateObject var model = MyPIModel() @State var isConfirmingPayment = false - var body: some View { VStack { @@ -37,7 +36,7 @@ struct PaymentMethodView: View { } } -struct PaymentMethodView_Preview : PreviewProvider { +struct PaymentMethodView_Preview: PreviewProvider { static var previews: some View { PaymentMethodView(integrationMethod: .iDEAL) } diff --git a/Testers/IntegrationTester/IntegrationTester/Source/Views/PaymentMethodWithContactInfoView.swift b/Testers/IntegrationTester/IntegrationTester/Source/Views/PaymentMethodWithContactInfoView.swift index dfba3a1d7dc..cb0530e49db 100644 --- a/Testers/IntegrationTester/IntegrationTester/Source/Views/PaymentMethodWithContactInfoView.swift +++ b/Testers/IntegrationTester/IntegrationTester/Source/Views/PaymentMethodWithContactInfoView.swift @@ -6,8 +6,8 @@ // import IntegrationTesterCommon -import SwiftUI import Stripe +import SwiftUI struct PaymentMethodWithContactInfoView: View { let integrationMethod: IntegrationMethod @@ -51,7 +51,7 @@ struct PaymentMethodWithContactInfoView: View { } } -struct PaymentMethodWithContactInfoView_Preview : PreviewProvider { +struct PaymentMethodWithContactInfoView_Preview: PreviewProvider { static var previews: some View { PaymentMethodWithContactInfoView(integrationMethod: .oxxo) } diff --git a/Testers/IntegrationTester/IntegrationTester/Source/Views/PaymentMethodWithShippingInfoView.swift b/Testers/IntegrationTester/IntegrationTester/Source/Views/PaymentMethodWithShippingInfoView.swift index 99c87c94a41..7c1d37522a6 100644 --- a/Testers/IntegrationTester/IntegrationTester/Source/Views/PaymentMethodWithShippingInfoView.swift +++ b/Testers/IntegrationTester/IntegrationTester/Source/Views/PaymentMethodWithShippingInfoView.swift @@ -6,8 +6,8 @@ // import IntegrationTesterCommon -import SwiftUI import Stripe +import SwiftUI struct PaymentMethodWithShippingInfoView: View { let integrationMethod: IntegrationMethod @@ -76,7 +76,7 @@ struct PaymentMethodWithShippingInfoView: View { } } -struct PaymentMethodWithShippingInfoView_Preview : PreviewProvider { +struct PaymentMethodWithShippingInfoView_Preview: PreviewProvider { static var previews: some View { PaymentMethodWithShippingInfoView(integrationMethod: .oxxo) } diff --git a/Testers/IntegrationTester/IntegrationTester/Source/Views/SEPADebitView.swift b/Testers/IntegrationTester/IntegrationTester/Source/Views/SEPADebitView.swift index 300d83913f4..d302e7fbe7f 100644 --- a/Testers/IntegrationTester/IntegrationTester/Source/Views/SEPADebitView.swift +++ b/Testers/IntegrationTester/IntegrationTester/Source/Views/SEPADebitView.swift @@ -6,8 +6,8 @@ // import IntegrationTesterCommon -import SwiftUI import Stripe +import SwiftUI struct SEPADebitView: View { @StateObject var model = MyPIModel() @@ -59,12 +59,12 @@ struct SEPADebitView: View { model.preparePaymentIntent() } } - + // This text is required by https://www.europeanpaymentscouncil.eu/what-we-do/sepa-schemes/sepa-direct-debit/sdd-mandate let mandateAuthText = "By providing your IBAN and confirming this payment, you are authorizing EXAMPLE COMPANY NAME and Stripe, our payment service provider, to send instructions to your bank to debit your account and your bank to debit your account in accordance with those instructions. You are entitled to a refund from your bank under the terms and conditions of your agreement with your bank. A refund must be claimed within 8 weeks starting from the date on which your account was debited." } -struct SEPADebitView_Preview : PreviewProvider { +struct SEPADebitView_Preview: PreviewProvider { static var previews: some View { SEPADebitView() } diff --git a/Testers/IntegrationTester/IntegrationTesterUITests/IntegrationTesterUITests.swift b/Testers/IntegrationTester/IntegrationTesterUITests/IntegrationTesterUITests.swift index 3c9d72c5e67..93184e87298 100644 --- a/Testers/IntegrationTester/IntegrationTesterUITests/IntegrationTesterUITests.swift +++ b/Testers/IntegrationTester/IntegrationTesterUITests/IntegrationTesterUITests.swift @@ -11,8 +11,8 @@ // defaults write com.apple.iphonesimulator ConnectHardwareKeyboard -bool false import IntegrationTesterCommon -import XCTest import Stripe +import XCTest enum ConfirmationBehavior { // No confirmation needed @@ -26,31 +26,31 @@ enum ConfirmationBehavior { class IntegrationTesterUITests: XCTestCase { var app: XCUIApplication! var appLaunched = false - + override func setUpWithError() throws { // In UI tests it is usually best to stop immediately when a failure occurs. continueAfterFailure = false - + app = XCUIApplication() app.launchEnvironment = ["UITesting": "true"] - if (!appLaunched) { + if !appLaunched { app.launch() appLaunched = true } popToMainMenu() } - + override func tearDownWithError() throws { // Put teardown code here. This method is called after the invocation of each test method in the class. } - + func popToMainMenu() { let menuButton = app.buttons["Integrations"] if menuButton.exists { menuButton.tap() } } - + func fillCardData(_ app: XCUIApplication, number: String = "4242424242424242") throws { let numberField = app.textFields["card number"] XCTAssertTrue(numberField.waitForExistence(timeout: 10.0)) @@ -68,20 +68,20 @@ class IntegrationTesterUITests: XCTestCase { let postalField = app.textFields["ZIP"] postalField.typeText("12345") } - + func testAuthentication(cardNumber: String, expectedResult: String = "Payment complete!", confirmationBehavior: ConfirmationBehavior = .none) { print("Testing \(cardNumber)") self.popToMainMenu() let tablesQuery = app.collectionViews - + let cardExampleElement = tablesQuery.cells.buttons["Card"] cardExampleElement.tap() try! fillCardData(app, number: cardNumber) - + let buyButton = app.buttons["Buy"] XCTAssertTrue(buyButton.waitForExistence(timeout: 10.0)) buyButton.forceTapElement() - + switch confirmationBehavior { case .none: break case .threeDS1: @@ -94,12 +94,12 @@ class IntegrationTesterUITests: XCTestCase { XCTAssertTrue(completeAuth.waitForExistence(timeout: 60.0)) completeAuth.tap() } - + let statusView = app.staticTexts["Payment status view"] XCTAssertTrue(statusView.waitForExistence(timeout: 10.0)) XCTAssertNotNil(statusView.label.range(of: expectedResult)) } - + func testNoAuthenticationCustomCard() throws { let cardNumbers = [ // Main test cards @@ -117,7 +117,7 @@ class IntegrationTesterUITests: XCTestCase { "36227206271667", // diners club (14 digit) "3566002020360505", // jcb "6200000000000005", // cup - + // Non-US "4000000760000002", // br "4000001240000000", // ca @@ -127,66 +127,66 @@ class IntegrationTesterUITests: XCTestCase { testAuthentication(cardNumber: card) } } - + func testStandardCustomCard3DS1() throws { testAuthentication(cardNumber: "4000000000003063", confirmationBehavior: .threeDS1) } - + func testStandardCustomCard3DS2() throws { testAuthentication(cardNumber: "4000000000003220", confirmationBehavior: .threeDS2) } - + func testDeclinedCard() throws { testAuthentication(cardNumber: "4000000000000002", expectedResult: "declined") } - + func testSetupIntents() throws { self.popToMainMenu() let tablesQuery = app.collectionViews - + let cardExampleElement = tablesQuery.cells.buttons["Card (SetupIntents)"] cardExampleElement.tap() try! fillCardData(app, number: "4242424242424242") - + let buyButton = app.buttons["Setup"] XCTAssertTrue(buyButton.waitForExistence(timeout: 10.0)) buyButton.forceTapElement() - + let statusView = app.staticTexts["Payment status view"] XCTAssertTrue(statusView.waitForExistence(timeout: 10.0)) XCTAssertNotNil(statusView.label.range(of: "complete!")) } - + func testApplePay() throws { self.popToMainMenu() let tablesQuery = app.collectionViews - + let applePayElement = tablesQuery.cells.buttons["Apple Pay"] applePayElement.tap() let applePayButton = app.buttons["Buy with Apple Pay"] XCTAssertTrue(applePayButton.waitForExistence(timeout: 10.0)) applePayButton.tap() - + let applePay = XCUIApplication(bundleIdentifier: "com.apple.PassbookUIService") _ = applePay.wait(for: .runningForeground, timeout: 10) - + var cardButton = applePay.buttons["Simulated Card - AmEx, ‪•••• 1234‬"] XCTAssertTrue(cardButton.waitForExistence(timeout: 10.0)) cardButton.forceTapElement() - + cardButton = applePay.buttons["Simulated Card - AmEx, ‪•••• 1234‬"].firstMatch XCTAssertTrue(cardButton.waitForExistence(timeout: 10.0)) cardButton.forceTapElement() - + let payButton = applePay.buttons["Pay with Passcode"] XCTAssertTrue(payButton.waitForExistence(timeout: 10.0)) payButton.forceTapElement() - + let statusView = app.staticTexts["Payment status view"] XCTAssertTrue(statusView.waitForExistence(timeout: 20.0)) XCTAssertNotNil(statusView.label.range(of: "complete!")) } - + func testAllIntegrationMethods() throws { for integrationMethod in IntegrationMethod.allCases { print("Testing \(integrationMethod.rawValue)") @@ -213,7 +213,7 @@ class IntegrationTesterUITests: XCTestCase { } } } - + func testAUBECSDebit() { return // TODO: AU BECS Debit is broken in testmode. @@ -252,62 +252,61 @@ class IntegrationTesterUITests: XCTestCase { // XCTAssertTrue(statusView.waitForExistence(timeout: 10.0)) // XCTAssertNotNil(statusView.label.range(of: "Payment complete")) } - + func testOxxo() { self.popToMainMenu() - + let tablesQuery = app.collectionViews let rowForPaymentMethod = tablesQuery.cells.buttons["OXXO"] rowForPaymentMethod.scrollToAndTap(in: app) - + let buyButton = app.buttons["Buy"] XCTAssertTrue(buyButton.waitForExistence(timeout: 10.0)) buyButton.forceTapElement() - - + let webView = app.webViews.firstMatch XCTAssert(webView.waitForExistence(timeout: 10)) let closeButton = app.buttons["Close"] XCTAssert(closeButton.waitForExistence(timeout: 10)) closeButton.forceTapElement() - + let statusView = app.staticTexts["Payment status view"] XCTAssertTrue(statusView.waitForExistence(timeout: 10.0)) XCTAssertNotNil(statusView.label.range(of: "Payment complete")) } - + func testFPX() { self.popToMainMenu() - + let tablesQuery = app.collectionViews let rowForPaymentMethod = tablesQuery.cells.buttons["FPX"] rowForPaymentMethod.scrollToAndTap(in: app) - + let maybank = app.tables.staticTexts["Maybank2U"] XCTAssertTrue(maybank.waitForExistence(timeout: 60.0)) maybank.tap() - + let webViewsQuery = app.webViews let completeAuth = webViewsQuery.descendants(matching: .any)["AUTHORIZE TEST PAYMENT"].firstMatch XCTAssertTrue(completeAuth.waitForExistence(timeout: 60.0)) completeAuth.forceTapElement() - + let statusView = app.staticTexts["Payment status view"] XCTAssertTrue(statusView.waitForExistence(timeout: 10.0)) XCTAssertNotNil(statusView.label.range(of: "Payment complete")) } - + func testNoInputIntegrationMethod(_ integrationMethod: IntegrationMethod, shouldConfirm: Bool) { self.popToMainMenu() let tablesQuery = app.collectionViews - + let rowForPaymentMethod = tablesQuery.cells.buttons[integrationMethod.rawValue] rowForPaymentMethod.scrollToAndTap(in: app) - + let buyButton = app.buttons["Buy"] XCTAssertTrue(buyButton.waitForExistence(timeout: 10.0)) buyButton.forceTapElement() - + if shouldConfirm { let webViewsQuery = app.webViews // Sometimes this is a Button, sometimes it's a StaticText. ¯\_(ツ)_/¯ @@ -315,23 +314,23 @@ class IntegrationTesterUITests: XCTestCase { XCTAssertTrue(completeAuth.waitForExistence(timeout: 60.0)) completeAuth.forceTapElement() } - + let statusView = app.staticTexts["Payment status view"] XCTAssertTrue(statusView.waitForExistence(timeout: 10.0)) XCTAssertNotNil(statusView.label.range(of: "Payment complete")) } - + func testAppToAppRedirect(_ integrationMethod: IntegrationMethod) { self.popToMainMenu() let tablesQuery = app.collectionViews - + let rowForPaymentMethod = tablesQuery.cells.buttons[integrationMethod.rawValue] rowForPaymentMethod.scrollToAndTap(in: app) - + let buyButton = app.buttons["Buy"] XCTAssertTrue(buyButton.waitForExistence(timeout: 10.0)) buyButton.forceTapElement() - + let safari = XCUIApplication(bundleIdentifier: "com.apple.mobilesafari") XCTAssertTrue(safari.wait(for: .runningForeground, timeout: 10)) // wait for Safari to open let webViewsQuery = safari.webViews @@ -339,31 +338,31 @@ class IntegrationTesterUITests: XCTestCase { let completeAuth = webViewsQuery.descendants(matching: .any)["AUTHORIZE TEST PAYMENT"].firstMatch XCTAssertTrue(completeAuth.waitForExistence(timeout: 60.0)) completeAuth.forceTapElement() - + let safariOpenButton = safari.buttons["Open"] XCTAssertTrue(safariOpenButton.waitForExistence(timeout: 5.0)) if safariOpenButton.exists { safariOpenButton.tap() } - + _ = app.wait(for: .runningForeground, timeout: 10) // wait to switch back to IntegrationTester - + let statusView = app.staticTexts["Payment status view"] XCTAssertTrue(statusView.waitForExistence(timeout: 10.0)) XCTAssertNotNil(statusView.label.range(of: "Payment complete")) } - + func testAppToAppRedirectWithoutReturnURL(_ integrationMethod: IntegrationMethod) { self.popToMainMenu() let tablesQuery = app.collectionViews - + let rowForPaymentMethod = tablesQuery.cells.buttons[integrationMethod.rawValue] rowForPaymentMethod.scrollToAndTap(in: app) - + let buyButton = app.buttons["Buy"] XCTAssertTrue(buyButton.waitForExistence(timeout: 10.0)) buyButton.forceTapElement() - + let safari = XCUIApplication(bundleIdentifier: "com.apple.mobilesafari") XCTAssertTrue(safari.wait(for: .runningForeground, timeout: 10)) // wait for Safari to open let webViewsQuery = safari.webViews @@ -371,22 +370,21 @@ class IntegrationTesterUITests: XCTestCase { let completeAuth = webViewsQuery.descendants(matching: .any)["AUTHORIZE TEST PAYMENT"].firstMatch XCTAssertTrue(completeAuth.waitForExistence(timeout: 60.0)) completeAuth.forceTapElement() - + let successful = webViewsQuery.descendants(matching: .any)["Payment successful"].firstMatch XCTAssertTrue(successful.waitForExistence(timeout: 60.0)) - + sleep(2) // Allow some time for the PaymentIntent state to update on the backend (RUN_MOBILESDK-288) - + app.activate() _ = app.wait(for: .runningForeground, timeout: 10) // wait to switch back to IntegrationTester - + let statusView = app.staticTexts["Payment status view"] XCTAssertTrue(statusView.waitForExistence(timeout: 10.0)) XCTAssertNotNil(statusView.label.range(of: "Payment complete")) } } - // There seems to be an issue with our SwiftUI buttons - XCTest fails to scroll to the button's position. // Work around this by targeting a coordinate inside the button. // https://stackoverflow.com/questions/33422681/xcode-ui-test-ui-testing-failure-failed-to-scroll-to-visible-by-ax-action @@ -398,7 +396,7 @@ extension XCUIElement { withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)) coordinate.tap() } - + func scrollToAndTap(in app: XCUIApplication) { while !self.exists { app.swipeUp() diff --git a/Testers/IntegrationTester/Project.swift b/Testers/IntegrationTester/Project.swift index 086d62983fd..2a7c2e4a4e0 100644 --- a/Testers/IntegrationTester/Project.swift +++ b/Testers/IntegrationTester/Project.swift @@ -83,6 +83,6 @@ let project = Project( name: "IntegrationTester", buildAction: .buildAction(targets: ["IntegrationTester"]), testAction: .targets(["IntegrationTesterUITests"]) - ) + ), ] ) diff --git a/Testers/LocalizationTester/Project.swift b/Testers/LocalizationTester/Project.swift index a2e70584322..89d5d55c783 100644 --- a/Testers/LocalizationTester/Project.swift +++ b/Testers/LocalizationTester/Project.swift @@ -66,6 +66,6 @@ let project = Project( name: "LocalizationTester", buildAction: .buildAction(targets: ["LocalizationTester"]), testAction: .targets(["LocalizationTesterUITests"]) - ) + ), ] ) diff --git a/Tuist/ProjectDescriptionHelpers/Tuist+Stripe.swift b/Tuist/ProjectDescriptionHelpers/Tuist+Stripe.swift index 7e00a4a3c29..bf9b3edb3b0 100644 --- a/Tuist/ProjectDescriptionHelpers/Tuist+Stripe.swift +++ b/Tuist/ProjectDescriptionHelpers/Tuist+Stripe.swift @@ -4,7 +4,7 @@ import ProjectDescription extension Project { /// Options for test targets inside Stripe frameworks. public struct TestOptions { - public var resources: ResourceFileElements? = nil + public var resources: ResourceFileElements? public var dependencies: [TargetDependency] = [] public var settings: Settings = .stripeTargetSettings( baseXcconfigFilePath: "//BuildConfigurations/StripeiOS Tests" @@ -326,7 +326,7 @@ extension Project { ? Arguments( environment: [ "FB_REFERENCE_IMAGE_DIR": - "$(SRCROOT)/../Tests/ReferenceImages" + "$(SRCROOT)/../Tests/ReferenceImages", ] ) : nil, expandVariableFromTarget: "\(name)" @@ -440,4 +440,3 @@ extension String { ) } } - diff --git a/bitrise.yml b/bitrise.yml index 6397192fe4f..f60134ba63d 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -355,7 +355,7 @@ workflows: steps: - script@1: inputs: - - content: "./ci_scripts/lint.sh" + - content: "./ci_scripts/lint_modified_files.sh" title: Run swiftlint - fastlane@3: inputs: diff --git a/ci_scripts/lint.sh b/ci_scripts/lint.sh deleted file mode 100755 index 909dbe53d14..00000000000 --- a/ci_scripts/lint.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash - -if ! command -v swiftlint &> /dev/null; then - echo "swiftlint is not installed! Use:" - tput setaf 7 # white - echo - echo ' brew install swiftlint' - echo - tput sgr0 # reset - exit 1 -fi - -lint_dirs=( - "Stripe" - "StripeCore" - "StripeUICore" - "StripeFinancialConnections" - "StripeApplePay" - "StripeCameraCore" - "StripeCardScan" - "StripePayments" - "StripePaymentSheet" - "StripeIdentity" -) - -exit_code=0 -for dir in ${lint_dirs[@]}; do - swiftlint --strict --config .swiftlint.yml "$dir/" - code=$? - if [ "$code" != "0" ]; then - exit_code=$code - fi -done -exit $exit_code \ No newline at end of file diff --git a/ci_scripts/lint_before_push.sh b/ci_scripts/lint_modified_files.sh similarity index 74% rename from ci_scripts/lint_before_push.sh rename to ci_scripts/lint_modified_files.sh index d9ea79f5f47..3ac2b446317 100755 --- a/ci_scripts/lint_before_push.sh +++ b/ci_scripts/lint_modified_files.sh @@ -1,7 +1,7 @@ #!/bin/bash # To set as a local pre-push hook: -# ln -s "$(pwd)/ci_scripts/lint_before_push.sh" .git/hooks/pre-push && chmod +x .git/hooks/pre-push +# ln -s "$(pwd)/ci_scripts/lint_modified_files.sh" .git/hooks/pre-push && chmod +x .git/hooks/pre-push function suggest_no_verify() { sleep 0.01 # wait for git to print its error @@ -14,13 +14,19 @@ function suggest_no_verify() { } if which swiftlint >/dev/null; then + IS_HOOK=false + if [ $(dirname $0) == ".git/hooks" ]; then + IS_HOOK=true + fi START=`date +%s` z40=0000000000000000000000000000000000000000 - echo "Linting before pushing (use $(tput setaf 7)git push --no-verify$(tput sgr0) to skip)." - echo "" + if [ "$IS_HOOK" = true ]; then + echo "Linting before pushing (use $(tput setaf 7)git push --no-verify$(tput sgr0) to skip)." + echo "" + fi CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) count=0 @@ -45,10 +51,10 @@ if which swiftlint >/dev/null; then END=`date +%s` echo "" echo "Linted in $(($END - $START))s." - if [ "$EXIT_CODE" != '0' ]; then - suggest_no_verify & - else + if [ "$EXIT_CODE" == '0' ]; then echo 'All lints passed.' + elif [ "$IS_HOOK" = true ]; then + suggest_no_verify & fi exit $EXIT_CODE else