-
Notifications
You must be signed in to change notification settings - Fork 6
feature: Checkout Components #1212
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
maintain all the existing features
# Conflicts: # Package.resolved
| } | ||
| } | ||
|
|
||
| /// The main entry point for CheckoutComponents, providing a familiar API similar to the main Primer class |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This wont be relevant to all readers of the docs - going forward the majority of consumers wont be migrating, or be familiar with the main Primer class, which Im not sure what it refers to anyway.
| // MARK: - Properties | ||
|
|
||
| /// The currently active checkout view controller | ||
| private weak var activeCheckoutController: UIViewController? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will this always be accessible, even in pure SwiftUI?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@NQuinn27 , great observation! You're right to question this. Let me clarify the architecture:
The separation is intentional:
- CheckoutComponentsPrimer.swift - UIKit-only wrapper that provides presentCheckout(from: UIViewController) methods. It handles UIKit presentation concerns like modal presentation, sheet detents,
and UIViewController lifecycle. This class always creates a PrimerSwiftUIBridgeViewController internally to host the SwiftUI content. - PrimerCheckout.swift - Pure SwiftUI View that can be directly embedded in SwiftUI apps without any UIKit dependencies. SwiftUI developers can use this directly in their view hierarchies.
Why this separation exists:
- Different paradigms: UIKit apps need imperative APIs (presentCheckout()) while SwiftUI apps need declarative views (PrimerCheckout())
- Clean separation: SwiftUI apps shouldn't need to import/know about UIKit presentation logic
- Developer experience: Each platform gets idiomatic APIs - UIKit devs get familiar delegate patterns and view controller presentation, SwiftUI devs get Views and modifiers
To answer your specific question: activeCheckoutController will always be accessible even in "pure SwiftUI" because when SwiftUI apps use CheckoutComponentsPrimer (the UIKit wrapper), it creates a
bridge controller. However, pure SwiftUI apps should use PrimerCheckout directly and wouldn't interact with CheckoutComponentsPrimer at all.
The architecture is:
- UIKit apps → CheckoutComponentsPrimer → PrimerSwiftUIBridgeViewController → PrimerCheckout
- SwiftUI apps → PrimerCheckout directly
I've updated the comments to make this distinction clearer. The key insight is that these serve different audiences - one for UIKit integration, one for pure SwiftUI.
Does this make sense to you? Am I overkilling it?
| ) | ||
| } | ||
|
|
||
| /// Present the card form directly without payment method selection |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting usecase, generally a safe bet to assume that PAYMENT_CARD is available, what will happen if the response from /configuration doesnt include it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was a remnant of an AI hallucination, removed.
| @@ -0,0 +1,180 @@ | |||
| // swiftlint:disable all | |||
| import SwiftUI | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import SwiftUICore
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
got this error:
Module 'SwiftUICore' is an implementation detail of 'SwiftUI'; import 'SwiftUI' instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yeah they made it private in Xcode 26 👍
| } | ||
|
|
||
| /// Compare 3DS options for equality | ||
| private func areThreeDsOptionsEqual(_ lhs: PrimerThreeDsOptions?, _ rhs: PrimerThreeDsOptions?) -> Bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we not conform these types to equatable instead of these functions?
| // MARK: - Properties | ||
|
|
||
| @Published private var internalState = PrimerSelectCountryState() | ||
| private weak var cardFormScope: DefaultCardFormScope? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private below public vars
| // Handle all font cases | ||
| switch font { | ||
| case .title2: | ||
| self.init(descriptor: UIFont.preferredFont(forTextStyle: .title2).fontDescriptor, size: 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a comment here why the size is zero
|
|
||
| import Foundation | ||
|
|
||
| public struct ContainerDiagnostics: Sendable, CustomStringConvertible { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a lot of engineering here. I just wonder whether the value of them warrants the complexity.
My instinct would be that if our consumers are expected to use container diagnostics, our SDK is too complex for them to use.
If this is a nice to have, it could probably be introduced further in the future to cut down the complexity of the work
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My idea was to create a proper DI library that could be reused across all the iOS SDKs in Primer. It might be overkill for now, but this can be part of the PrimerCore lib when we start modularising the SDK even further, WDYT? @henry-cooper-primer
# Conflicts: # Debug App/Podfile.lock # Sources/PrimerSDK/Classes/Error Handler/PrimerInternalError.swift # Sources/PrimerSDK/Classes/PCI/Checkout Components/PrimerRawCardDataTokenizationBuilder.swift
# Conflicts: # Sources/PrimerSDK/Classes/PCI/Tokenization View Models/FormsTokenizationViewModel/CardFormPaymentMethodTokenizationViewModel.swift
# Conflicts: # Debug App/Primer.io Debug App SPM.xcodeproj/project.pbxproj # Sources/PrimerSDK/Classes/Core/BIN Data/CardValidationService.swift # Sources/PrimerSDK/Classes/PCI/Checkout Components/PrimerRawCardDataTokenizationBuilder.swift
…n and API clarity (#1358) * Update DEMO files as per PR review * swiftlint --fix --format * Apply PR review feedback for CheckoutComponents code quality Changes per Henry Cooper's review: - Remove redundant internal access modifiers (Swift defaults to internal) - Replace OR operators with array.contains() for better readability - Consolidate similar switch case statements - Use Swift 5.7+ guard let shorthand syntax - Remove obvious/redundant code comments ("Default implementation", etc.) Improves code maintainability and follows Swift best practices. * Address additional PR review feedback from Henry Architecture improvements: - Extract SDK initialization logic into CheckoutSDKInitializer service - Reduce InternalCheckout view responsibilities (SOLID principles) Code quality improvements: - Make InternalCheckout properties private (proper encapsulation) - Fix CheckoutNavigator to use @StateObject (ObservableObject handling) - Use State(wrappedValue:) instead of deprecated initialValue syntax - Remove redundant init?(coder:) from PrimerSwiftUIBridgeViewController - Remove unnecessary @mainactor annotations from View computed properties - Remove obvious/redundant property comments in PrimerFieldStyling - Clean up "Default implementation" comments throughout Type fixes: - Fix CheckoutCustomization type reference in examples view These changes improve separation of concerns, follow SwiftUI best practices, and reduce code noise from unnecessary annotations and comments. * Address PR review feedback for CheckoutComponents - Make PrimerThreeDsOptions, PrimerStripeOptions, and PrimerLocaleData conform to Equatable - Replace custom comparison functions with Equatable conformance in SettingsObserver - Reorder properties in DefaultSelectCountryScope to place private below public - Add comment explaining size 0 usage in UIFont extension for Dynamic Type preservation * remove all "Created by Claude" instances * remove print statements * remove obsolete file * Create NetworkingUtils utility to eliminate repeated networking boilerplate across demo files * Update comments for more clarity * Add new file to SPM project * Refactor CheckoutComponents customization closures to use any View instead of AnyView Implemented Henry's hybrid approach to solve Swift existential type limitations while improving developer experience * Update code as per comments * Extract the PrimerCardholderNameField styling values to private vars * Remove all the performance metrics and monitoring code from ValidationService.swift --------- Co-authored-by: Boris Nikolic <[email protected]>
Fix dark mode support by correcting token reference resolution in DesignTokensManager and implementing proper SwiftUI reactive pattern. Changes: - Fix reference resolution bug in resolveFlattenedReferences - Simplify DesignTokensManager using functional patterns - Use @StateObject for automatic UI updates on color scheme changes - Remove redundant manual state synchronization
…#1369) * Update DEMO files as per PR review * swiftlint --fix --format * Apply PR review feedback for CheckoutComponents code quality Changes per Henry Cooper's review: - Remove redundant internal access modifiers (Swift defaults to internal) - Replace OR operators with array.contains() for better readability - Consolidate similar switch case statements - Use Swift 5.7+ guard let shorthand syntax - Remove obvious/redundant code comments ("Default implementation", etc.) Improves code maintainability and follows Swift best practices. * Address additional PR review feedback from Henry Architecture improvements: - Extract SDK initialization logic into CheckoutSDKInitializer service - Reduce InternalCheckout view responsibilities (SOLID principles) Code quality improvements: - Make InternalCheckout properties private (proper encapsulation) - Fix CheckoutNavigator to use @StateObject (ObservableObject handling) - Use State(wrappedValue:) instead of deprecated initialValue syntax - Remove redundant init?(coder:) from PrimerSwiftUIBridgeViewController - Remove unnecessary @mainactor annotations from View computed properties - Remove obvious/redundant property comments in PrimerFieldStyling - Clean up "Default implementation" comments throughout Type fixes: - Fix CheckoutCustomization type reference in examples view These changes improve separation of concerns, follow SwiftUI best practices, and reduce code noise from unnecessary annotations and comments. * Address PR review feedback for CheckoutComponents - Make PrimerThreeDsOptions, PrimerStripeOptions, and PrimerLocaleData conform to Equatable - Replace custom comparison functions with Equatable conformance in SettingsObserver - Reorder properties in DefaultSelectCountryScope to place private below public - Add comment explaining size 0 usage in UIFont extension for Dynamic Type preservation * remove all "Created by Claude" instances * remove print statements * remove obsolete file * Create NetworkingUtils utility to eliminate repeated networking boilerplate across demo files * Update comments for more clarity * Add new file to SPM project * Refactor CheckoutComponents customization closures to use any View instead of AnyView Implemented Henry's hybrid approach to solve Swift existential type limitations while improving developer experience * Update code as per comments * Remove all hardcoded strings * Update mismatched strings and improve the Splash screen flow * Reuse existing translated string * Update the working branch with the latest from checkout-components * Add Swift Format Pass * Update as per comments --------- Co-authored-by: Boris Nikolic <[email protected]> Co-authored-by: Henry Cooper <[email protected]>
91253f6 to
b3ae3a0
Compare
…#1374) feat: Implement dynamic sheet height for UIKit CheckoutComponents Implement dynamic sheet sizing for CheckoutComponents presented via UIKit bridge controller. The sheet now automatically adjusts its height based on SwiftUI content size while respecting system constraints. Key changes: - Add custom detent for iOS 16+ with dynamic height calculation - Implement KVO observer to detect SwiftUI content size changes - Add sheet presentation configuration with size constraints - Include country selection modal with sheet presentation - Integrate design tokens manager for proper theme support Co-authored-by: Boris Nikolic <[email protected]>
* Implement analytics for checkout components
* Complete analytics event metadata per documentation requirements
Enhanced all CheckoutComponents analytics events to include required
and optional metadata fields according to the event definitions spec:
- Add userLocale (Locale.current.identifier) to all analytics events
- Extract paymentId and paymentMethod from PrimerError.paymentFailed
for PAYMENT_FAILURE events
- Include paymentMethod context in card form analytics events
(PAYMENT_DETAILS_ENTERED, PAYMENT_SUBMITTED, PAYMENT_PROCESSING_STARTED)
- Add paymentMethod to PAYMENT_THREEDS event from token data
- Add userLocale to PAYMENT_REDIRECT_TO_THIRD_PARTY event
Changed files:
- DefaultCheckoutScope.swift: Enhanced state tracking with metadata
extraction helper for failure cases
- CheckoutSDKInitializer.swift: Added userLocale to SDK init events
- DefaultCardFormScope.swift: Added metadata to card form events
- DefaultPaymentMethodSelectionScope.swift: Added userLocale
- HeadlessRepositoryImpl.swift: Enhanced 3DS and redirect event metadata
Note: PAYMENT_REATTEMPTED event not implemented as retry feature
does not exist yet.
* Remove code duplication, improve DI pattern implementation, and add more tests
* Discriminated union refactoring
* Remove obsolete code
* Inject analytics config provider into checkout initializer
* Use types
* Update as per Henry's comments
* Address PR review comments
- Remove unnecessary metadata variable assignment in DefaultAnalyticsInteractor
- Simplify AnalyticsEventBufferTests to use default nil metadata value
- Replace UUIDGenerator with existing String.uuid extension
- Extract duplicate device type checking logic into private helper method in UIDeviceExtension
- Add proper assertions to AnalyticsEventServiceTests"
* Improve test coverage
---------
Co-authored-by: Boris Nikolic <[email protected]>
# Conflicts: # Sources/PrimerSDK/Classes/Core/PrimerHeadlessUniversalCheckout/Models/PrimerHeadlessUniversalCheckoutInputElement.swift
|




🚀 iOS CheckoutComponents SDK Implementation
Overview
This PR introduces the new CheckoutComponents SDK for iOS - a modern, SwiftUI-native payment integration framework that provides exact API parity with our Android SDK while leveraging iOS platform
strengths.
RFC Reference
https://www.notion.so/primerio/RFC-iOS-CheckoutComponents-SDK-23bca65dc30e8163bf68dea650baf9d9
Key Features Implemented
🏗️ Core Architecture
💳 Payment Components
🎨 Customization Capabilities
📱 Debug App Integration
Technical Highlights
Performance Optimizations
Modern iOS Patterns
File Changes Summary
Testing
Production Readiness
This implementation is feature-complete but requires additional work before production release as outlined in the RFC:
Dependencies
Breaking Changes
None - This is a new module that doesn't affect existing Drop-in UI functionality.
Next Steps