Skip to content
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

feat(ad-hoc): prepare UI products for Swift 6 #370

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Example/Example/Sources/Application/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import Foundation
import ProcessOut

@MainActor
enum Constants {

/// Project configuration.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import ProcessOut

final class ApplePayTokenizationCoordinator: POApplePayTokenizationDelegate {

init(didTokenizeCard: @escaping (POCard) async throws -> Void) {
init(didTokenizeCard: @escaping @Sendable (POCard) async throws -> Void) {
self.didTokenizeCard = didTokenizeCard
}

/// Closure that is called when invoice is authorized.
let didTokenizeCard: (POCard) async throws -> Void
let didTokenizeCard: @Sendable (POCard) async throws -> Void

// MARK: -

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,14 @@ struct ConfigurationViewModelState {
extension ConfigurationViewModelState {

/// Idle state.
static let idle = ConfigurationViewModelState(
projectId: "",
projectKey: "",
environments: .init(sources: environmentSources, id: \.id, selection: .production),
customerId: "",
merchantId: ""
)

// MARK: - Private

private static let environmentSources: [Environment] = [
.init(id: .production, name: String(localized: .Configuration.productionEnvironment)),
.init(id: .stage, name: String(localized: .Configuration.stageEnvironment))
]
static var idle: ConfigurationViewModelState {
let environmentSources: [Environment] = [
.init(id: .production, name: String(localized: .Configuration.productionEnvironment)),
.init(id: .stage, name: String(localized: .Configuration.stageEnvironment))
]
let environments: PickerData<Environment, ProcessOutConfiguration.Environment> = .init(
sources: environmentSources, id: \.id, selection: .production
)
return .init(projectId: "", projectKey: "", environments: environments, customerId: "", merchantId: "")
}
}
1 change: 1 addition & 0 deletions Example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Project demonstrates multiple flows that ProcessOut framework is capable of.
3. Set constants defined in `Example/Sources/Application/Constants.swift` file to your project credentials. E.g.:

```swift
@MainActor
enum Constants {

/// Project configuration.
Expand Down
1 change: 1 addition & 0 deletions Example/project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ settings:
CURRENT_PROJECT_VERSION: 1
LOCALIZATION_PREFERS_STRING_CATALOGS: true
SWIFT_EMIT_LOC_STRINGS: true
SWIFT_VERSION: 6.0

# SPM Packages
packages:
Expand Down
27 changes: 13 additions & 14 deletions Sources/ProcessOut/Sources/Api/ProcessOut.swift
Original file line number Diff line number Diff line change
Expand Up @@ -334,23 +334,22 @@ extension ProcessOut {
/// - force: When set to `false` (the default) only the first invocation takes effect, all
/// subsequent calls to this method are ignored. Pass `true` to allow existing shared instance
/// reconfiguration (if any).
@MainActor
@preconcurrency
public static func configure(configuration: ProcessOutConfiguration, force: Bool = false) {
// todo(andrii-vysotskyi): isolate method to main actor when releasing 5.0.0
MainActor.assumeIsolated {
if isConfigured {
if force {
shared.replace(configuration: configuration)
shared.logger.debug("Did change ProcessOut configuration")
} else {
shared.logger.debug("ProcessOut can be configured only once, ignored")
}
if isConfigured {
if force {
shared.replace(configuration: configuration)
shared.logger.debug("Did change ProcessOut configuration")
} else {
Self.prewarm()
_shared.withLock { instance in
instance = ProcessOut(configuration: configuration)
}
shared.logger.debug("Did complete ProcessOut configuration")
shared.logger.debug("ProcessOut can be configured only once, ignored")
}
} else {
Self.prewarm()
_shared.withLock { instance in
instance = ProcessOut(configuration: configuration)
}
shared.logger.debug("Did complete ProcessOut configuration")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import UIKit
/// Service that emulates the normal 3DS authentication flow but does not actually make any calls to a real Access
/// Control Server (ACS). Should be used only for testing purposes in sandbox environment.
@available(*, deprecated, message: "Use ProcessOutUI.POTest3DSService instead.")
@MainActor
public final class POTest3DSService: PO3DS2Service {

/// Creates service instance.
Expand Down Expand Up @@ -40,7 +41,6 @@ public final class POTest3DSService: PO3DS2Service {
)
}

@MainActor
public func performChallenge(with parameters: PO3DS2ChallengeParameters) async throws -> PO3DS2ChallengeResult {
await withCheckedContinuation { continuation in
let alertController = UIAlertController(
Expand Down
3 changes: 3 additions & 0 deletions Sources/ProcessOut/Sources/Generated/Sourcery+Generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ extension POCardsService {

/// Tokenize previously authorized payment.
@MainActor
@preconcurrency
@available(*, deprecated, message: "Use the async method instead.")
@discardableResult
public func tokenize(
Expand All @@ -169,6 +170,7 @@ extension POCardsService {

/// Authorize given payment request and tokenize it.
@MainActor
@preconcurrency
@available(*, deprecated, message: "Use the async method instead.")
@discardableResult
public func tokenize(
Expand All @@ -183,6 +185,7 @@ extension POCardsService {

/// Authorize given payment request and tokenize it.
@MainActor
@preconcurrency
@available(*, deprecated, message: "Use the async method instead.")
@discardableResult
public func tokenize(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@
// Created by Andrii Vysotskyi on 05.09.2024.
//

// todo(andrii-vysotskyi): add Sendable conformance to POApplePayTokenizationDelegate when releasing 5.0.0

import PassKit

public protocol POApplePayTokenizationDelegate: AnyObject {
@preconcurrency
public protocol POApplePayTokenizationDelegate: Sendable { // swiftlint:disable:this class_delegate_protocol

/// Sent to the delegate after the user has acted on the payment request and it was tokenized by ProcessOut.
@MainActor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@ final class DefaultCardsService: POCardsService {
try await repository.updateCard(request: request)
}

@MainActor
func tokenize(request: POApplePayPaymentTokenizationRequest) async throws -> POCard {
let request = try applePayCardTokenizationRequestMapper.tokenizationRequest(from: request)
return try await repository.tokenize(request: request)
}

@MainActor
func tokenize(
request: POApplePayTokenizationRequest, delegate: POApplePayTokenizationDelegate?
) async throws -> POCard {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ public protocol POCardsService: POService { // sourcery: AutoCompletion

/// Tokenize previously authorized payment.
@MainActor
@preconcurrency
func tokenize(request: POApplePayPaymentTokenizationRequest) async throws -> POCard

/// Authorize given payment request and tokenize it.
@MainActor
@preconcurrency
func tokenize(
request: POApplePayTokenizationRequest, delegate: POApplePayTokenizationDelegate?
) async throws -> POCard
Expand All @@ -41,6 +43,7 @@ extension POCardsService {

/// Authorize given payment request and tokenize it.
@MainActor
@preconcurrency
public func tokenize(request: POApplePayTokenizationRequest) async throws -> POCard {
try await tokenize(request: request, delegate: nil)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
// Created by Andrii Vysotskyi on 18.09.2024.
//

// todo(andrii-vysotskyi): add Sendable conformance to PO3DS2Service when releasing 5.0.0

/// This interface provides methods to process 3-D Secure transactions.
public protocol PO3DS2Service: AnyObject {
@preconcurrency
public protocol PO3DS2Service: Sendable {

/// Asks implementation to create request that will be passed to 3DS Server to create the AReq.
func authenticationRequestParameters(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@

import Foundation

@MainActor
final class ThrottledWebAuthenticationSessionDecorator: WebAuthenticationSession {
actor ThrottledWebAuthenticationSessionDecorator: WebAuthenticationSession {

nonisolated init(session: WebAuthenticationSession) {
init(session: WebAuthenticationSession) {
self.session = session
semaphore = AsyncSemaphore(value: 1)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -398,8 +398,10 @@ final class DefaultNativeAlternativePaymentMethodInteractor: NativeAlternativePa
}

private func send(event: PONativeAlternativePaymentMethodEvent) {
logger.debug("Did send event: '\(String(describing: event))'")
delegate?.nativeAlternativePaymentMethodDidEmitEvent(event)
MainActor.assumeIsolated {
logger.debug("Did send event: '\(String(describing: event))'")
delegate?.nativeAlternativePaymentMethodDidEmitEvent(event)
}
}

private func defaultValues(
Expand All @@ -410,34 +412,26 @@ final class DefaultNativeAlternativePaymentMethodInteractor: NativeAlternativePa
completion([:])
return
}
if let delegate {
delegate.nativeAlternativePaymentMethodDefaultValues(for: parameters) { [self] values in
assert(Thread.isMainThread, "Completion must be called on main thread.")
var defaultValues: [String: State.ParameterValue] = [:]
parameters.forEach { parameter in
let defaultValue: String
if let value = values[parameter.key] {
switch parameter.type {
case .email, .numeric, .phone, .text:
defaultValue = self.formatted(value: value, type: parameter.type)
case .singleSelect:
precondition(
parameter.availableValues?.map(\.value).contains(value) == true,
"Unknown `singleSelect` parameter value."
)
defaultValue = value
}
} else {
defaultValue = self.defaultValue(for: parameter)
}
defaultValues[parameter.key] = .init(value: defaultValue, recentErrorMessage: nil)
}
completion(defaultValues)
}
} else {
Task { @MainActor in
let values = await delegate?.nativeAlternativePayment(defaultValuesFor: parameters) ?? [:]
var defaultValues: [String: State.ParameterValue] = [:]
parameters.forEach { parameter in
defaultValues[parameter.key] = .init(value: defaultValue(for: parameter), recentErrorMessage: nil)
let defaultValue: String
if let value = values[parameter.key] {
switch parameter.type {
case .email, .numeric, .phone, .text:
defaultValue = self.formatted(value: value, type: parameter.type)
case .singleSelect:
precondition(
parameter.availableValues?.map(\.value).contains(value) == true,
"Unknown `singleSelect` parameter value."
)
defaultValue = value
}
} else {
defaultValue = self.defaultValue(for: parameter)
}
defaultValues[parameter.key] = .init(value: defaultValue, recentErrorMessage: nil)
}
completion(defaultValues)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@
//

/// Native alternative payment module delegate definition.
public protocol PONativeAlternativePaymentMethodDelegate: AnyObject {
@preconcurrency
public protocol PONativeAlternativePaymentMethodDelegate: AnyObject, Sendable {

/// Invoked when module emits event.
@MainActor
func nativeAlternativePaymentMethodDidEmitEvent(_ event: PONativeAlternativePaymentMethodEvent)

/// Method provides an ability to supply default values for given parameters. Completion expects dictionary
/// where key is a parameter key, and value is desired default. It is not mandatory to provide defaults for
/// all parameters.
/// - NOTE: completion must be called on `main` thread.
@MainActor
func nativeAlternativePaymentMethodDefaultValues(
for parameters: [PONativeAlternativePaymentMethodParameter], completion: @escaping ([String: String]) -> Void
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
//

/// Describes events that could happen during native alternative payment module lifecycle.
public enum PONativeAlternativePaymentMethodEvent {
public enum PONativeAlternativePaymentMethodEvent: Sendable {

public struct WillSubmitParameters {
public struct WillSubmitParameters: Sendable {

/// Available parameters.
public let parameters: [PONativeAlternativePaymentMethodParameter]
Expand All @@ -25,7 +25,7 @@ public enum PONativeAlternativePaymentMethodEvent {
}
}

public struct ParametersChanged {
public struct ParametersChanged: Sendable {

/// Parameter definition that the user changed.
public let parameter: PONativeAlternativePaymentMethodParameter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import SwiftUI

@MainActor
final class FocusCoordinator: ObservableObject {

/// Holds boolean value indicating whether tracked control is currently being edited.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,14 @@ extension POBackport where Wrapped: View {
@available(iOS 14, *)
private struct FocusModifier<Value: Hashable>: ViewModifier {

init(binding: Binding<Value?>, value: Value) {
self._binding = binding
self.value = value
}
/// The state binding to register.
@Binding
private(set) var binding: Value?

/// The value to match against when determining whether the binding should change.
let value: Value

// MARK: - ViewModifier

func body(content: Content) -> some View {
content
Expand Down Expand Up @@ -63,13 +67,6 @@ private struct FocusModifier<Value: Hashable>: ViewModifier {

// MARK: - Private Properties

/// The value to match against when determining whether the binding should change.
private let value: Value

/// The state binding to register.
@Binding
private var binding: Value?

/// Indicates whether
@State
private var isVisible = false
Expand Down
Loading
Loading