-
Notifications
You must be signed in to change notification settings - Fork 6
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
feature: Components - breaking the ice #1109
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// | ||
// DefaultPaymentMethodContentScope.swift | ||
// | ||
// | ||
// Created by Boris on 6.2.25.. | ||
// | ||
|
||
import Foundation | ||
#if canImport(SwiftUI) | ||
import SwiftUI | ||
#endif | ||
|
||
/// The default implementation of PaymentMethodContentScope for any payment method. | ||
struct DefaultPaymentMethodContentScope: PaymentMethodContentScope { | ||
let method: PaymentMethod | ||
|
||
// Simulated state; in a real system, this state would be derived from backend/network updates. | ||
var simulatedState = PaymentMethodState( | ||
isLoading: false, | ||
validationState: PaymentValidationState(isValid: true) | ||
) | ||
|
||
/// Returns the current simulated state. | ||
func getState() async -> PaymentMethodState { | ||
// TODO: Replace simulated state with actual state mapping from your processing logic. | ||
return simulatedState | ||
} | ||
|
||
/// Simulate payment submission. | ||
func submit() async -> Result<PaymentResult, Error> { | ||
// TODO: Replace with real payment submission logic. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
try? await Task.sleep(nanoseconds: 2 * 1_000_000_000) // simulate a 2-second delay | ||
return .success(PaymentResult(success: true, message: "Payment processed successfully")) | ||
} | ||
|
||
#if canImport(SwiftUI) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we require |
||
/// Provides default SwiftUI UI for the payment method. | ||
@ViewBuilder | ||
func defaultContent() -> AnyView { | ||
// Wrap your view in AnyView to satisfy the return type. | ||
AnyView( | ||
VStack { | ||
Text("Default UI for \(method.name)") | ||
.font(.headline) | ||
.padding() | ||
// TODO: Add form fields, validations, and error messaging as needed. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
) | ||
} | ||
#endif | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// | ||
// PaymentFlow.swift | ||
// | ||
// | ||
// Created by Boris on 6.2.25.. | ||
// | ||
|
||
import Foundation | ||
#if canImport(SwiftUI) | ||
import SwiftUI | ||
#endif | ||
|
||
/// The headless core component that encapsulates payment processing logic and state. | ||
/// This actor leverages Swift Concurrency (async/await) as per Apple's guidelines. | ||
actor PaymentFlow: PaymentFlowScope { | ||
|
||
// MARK: Internal State | ||
|
||
private var _paymentMethods: [PaymentMethod] = [] | ||
private var _selectedMethod: PaymentMethod? | ||
|
||
// AsyncStream continuations to emit updates. | ||
private var paymentMethodsContinuation: AsyncStream<[PaymentMethod]>.Continuation? | ||
private var selectedMethodContinuation: AsyncStream<PaymentMethod?>.Continuation? | ||
|
||
/// Initialize with default payment methods. | ||
init() { | ||
// TODO: In a production system, fetch or update available payment methods dynamically. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
_paymentMethods = [ | ||
CardPaymentMethod(id: "card", name: "Credit Card"), | ||
PayPalPaymentMethod(id: "paypal", name: "PayPal"), | ||
ApplePayPaymentMethod(id: "applePay", name: "Apple Pay") | ||
] | ||
} | ||
|
||
// MARK: AsyncStream Providers | ||
|
||
/// Provides an `AsyncStream` for payment methods. | ||
func getPaymentMethods() async -> AsyncStream<[PaymentMethod]> { | ||
return AsyncStream { continuation in | ||
self.paymentMethodsContinuation = continuation | ||
continuation.yield(self._paymentMethods) | ||
// TODO: Implement dynamic updates if payment methods change. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
} | ||
|
||
/// Provides an `AsyncStream` for the currently selected payment method. | ||
func getSelectedMethod() async -> AsyncStream<PaymentMethod?> { | ||
return AsyncStream { continuation in | ||
self.selectedMethodContinuation = continuation | ||
continuation.yield(self._selectedMethod) | ||
// TODO: Implement dynamic updates if the selection changes. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
} | ||
|
||
// MARK: Internal Update Helpers | ||
|
||
private func updatePaymentMethods() { | ||
paymentMethodsContinuation?.yield(_paymentMethods) | ||
} | ||
|
||
private func updateSelectedMethod() { | ||
selectedMethodContinuation?.yield(_selectedMethod) | ||
} | ||
|
||
// MARK: PaymentFlowScope Implementation | ||
|
||
func selectPaymentMethod(_ method: PaymentMethod?) async { | ||
_selectedMethod = method | ||
updateSelectedMethod() | ||
} | ||
|
||
#if canImport(SwiftUI) | ||
nonisolated func paymentMethodContent<Content: View>( | ||
for method: PaymentMethod, | ||
@ViewBuilder content: @escaping (any PaymentMethodContentScope) -> Content | ||
) -> AnyView { | ||
// Wrap the helper view in AnyView for type erasure. | ||
AnyView(PaymentMethodContentView(method: method, content: content)) | ||
} | ||
#endif | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// | ||
// PaymentFlowScope.swift | ||
// | ||
// | ||
// Created by Boris on 6.2.25.. | ||
// | ||
|
||
import Foundation | ||
#if canImport(SwiftUI) | ||
import SwiftUI | ||
#endif | ||
|
||
/// The main integration interface (similar to PaymentFlowScope on Android). | ||
protocol PaymentFlowScope { | ||
/// Retrieves the list of available payment methods as an `AsyncStream`. | ||
func getPaymentMethods() async -> AsyncStream<[PaymentMethod]> | ||
|
||
/// Retrieves the currently selected payment method as an `AsyncStream`. | ||
func getSelectedMethod() async -> AsyncStream<PaymentMethod?> | ||
|
||
/// Asynchronously select a payment method. | ||
func selectPaymentMethod(_ method: PaymentMethod?) async | ||
|
||
#if canImport(SwiftUI) | ||
/// SwiftUI helper to render payment method content. | ||
@ViewBuilder | ||
func paymentMethodContent<Content: View>( | ||
for method: PaymentMethod, | ||
@ViewBuilder content: @escaping (any PaymentMethodContentScope) -> Content | ||
) -> AnyView | ||
#endif | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// | ||
// PaymentFlowViewModel.swift | ||
// | ||
// | ||
// Created by Boris on 6.2.25.. | ||
// | ||
|
||
import SwiftUI | ||
|
||
/// A view model that bridges the PaymentFlow actor with SwiftUI state. | ||
@MainActor | ||
class PaymentFlowViewModel: ObservableObject { | ||
/// The underlying PaymentFlow actor. | ||
let paymentFlow = PaymentFlow() | ||
|
||
@Published var paymentMethods: [PaymentMethod] = [] | ||
@Published var selectedMethod: PaymentMethod? | ||
|
||
/// Load payment methods from the PaymentFlow actor. | ||
func loadPaymentMethods() async { | ||
for await methods in await paymentFlow.getPaymentMethods() { | ||
self.paymentMethods = methods | ||
} | ||
} | ||
|
||
func loadSelectedMethod() async { | ||
for await method in await paymentFlow.getSelectedMethod() { | ||
self.selectedMethod = method | ||
} | ||
} | ||
|
||
/// Select a payment method. | ||
func selectMethod(_ method: PaymentMethod) async { | ||
await paymentFlow.selectPaymentMethod(method) | ||
self.selectedMethod = method | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// | ||
// PaymentMethod.swift | ||
// | ||
// | ||
// Created by Boris on 6.2.25.. | ||
// | ||
|
||
/// Protocol representing a payment method. | ||
protocol PaymentMethod { | ||
var id: String { get } | ||
var name: String { get } | ||
var methodType: PaymentMethodType { get } | ||
// TODO: Add additional properties (e.g., icon, configuration) if needed. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
/// Enumeration for different payment method types. | ||
enum PaymentMethodType { | ||
case card | ||
case paypal | ||
case applePay | ||
// TODO: Add more types as needed. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
/// Represents the validation state of a payment method. | ||
struct PaymentValidationState { | ||
let isValid: Bool | ||
// TODO: Add more validation info if needed (e.g., error messages). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
/// Represents the overall state for a payment method. | ||
struct PaymentMethodState { | ||
let isLoading: Bool | ||
let validationState: PaymentValidationState | ||
// TODO: Extend with additional state properties as needed. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// | ||
// PaymentMethodContentScope.swift | ||
// | ||
// | ||
// Created by Boris on 6.2.25.. | ||
// | ||
|
||
import Foundation | ||
#if canImport(SwiftUI) | ||
import SwiftUI | ||
#endif | ||
|
||
/// Scope that handles behavior and UI for an individual payment method. | ||
protocol PaymentMethodContentScope { | ||
/// The payment method for which this scope applies. | ||
var method: PaymentMethod { get } | ||
|
||
/// Retrieve the current state asynchronously. | ||
func getState() async -> PaymentMethodState | ||
|
||
/// Asynchronously submit the payment. | ||
func submit() async -> Result<PaymentResult, Error> | ||
|
||
#if canImport(SwiftUI) | ||
/// Provides default SwiftUI UI for this payment method. | ||
@ViewBuilder | ||
func defaultContent() -> AnyView | ||
#endif | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// | ||
// PaymentMethodContentView.swift | ||
// | ||
// | ||
// Created by Boris on 6.2.25.. | ||
// | ||
|
||
import SwiftUI | ||
|
||
// PaymentMethodContentView.swift | ||
|
||
/// A helper view that instantiates the appropriate PaymentMethodContentScope and passes it to the provided content builder. | ||
struct PaymentMethodContentView<Content: View>: View { | ||
let method: PaymentMethod | ||
let content: (any PaymentMethodContentScope) -> Content | ||
|
||
var body: some View { | ||
// Decide which scope to use based on the payment method type. | ||
let scope: any PaymentMethodContentScope | ||
switch method.methodType { | ||
case .card: | ||
scope = DefaultPaymentMethodContentScope(method: method) | ||
case .paypal: | ||
scope = PayPalPaymentContentScope(method: method) | ||
case .applePay: | ||
scope = ApplePayPaymentContentScope(method: method) | ||
} | ||
return content(scope) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// | ||
// PaymentResult.swift | ||
// | ||
// | ||
// Created by Boris on 6.2.25.. | ||
// | ||
|
||
/// The result returned when a payment is processed. | ||
struct PaymentResult { | ||
let success: Bool | ||
let message: String? | ||
// TODO: Add error codes or more detailed response if required. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} |
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.
todo
)