Skip to content

Commit

Permalink
Refactored TransactionPoster: removed 2 dependencies and abstracted…
Browse files Browse the repository at this point in the history
… parameters

Follow up to #2540.
  • Loading branch information
NachoSoto committed May 26, 2023
1 parent 456b96b commit 397edda
Show file tree
Hide file tree
Showing 12 changed files with 431 additions and 385 deletions.
13 changes: 2 additions & 11 deletions Sources/Networking/Backend.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,24 +108,15 @@ class Backend {
completion: completion)
}

// swiftlint:disable:next function_parameter_count
func post(receiptData: Data,
appUserID: String,
isRestore: Bool,
productData: ProductRequestData?,
presentedOfferingIdentifier offeringIdentifier: String?,
transactionData: PurchasedTransactionData,
observerMode: Bool,
initiationSource: ProductRequestData.InitiationSource,
subscriberAttributes subscriberAttributesByKey: SubscriberAttribute.Dictionary?,
completion: @escaping CustomerAPI.CustomerInfoResponseHandler) {
self.customer.post(receiptData: receiptData,
appUserID: appUserID,
isRestore: isRestore,
productData: productData,
presentedOfferingIdentifier: offeringIdentifier,
transactionData: transactionData,
observerMode: observerMode,
initiationSource: initiationSource,
subscriberAttributes: subscriberAttributesByKey,
completion: completion)
}

Expand Down
36 changes: 20 additions & 16 deletions Sources/Networking/CustomerAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,20 +87,15 @@ final class CustomerAPI {
self.backendConfig.operationQueue.addOperation(postAttributionDataOperation)
}

// swiftlint:disable:next function_parameter_count
func post(receiptData: Data,
appUserID: String,
isRestore: Bool,
productData: ProductRequestData?,
presentedOfferingIdentifier offeringIdentifier: String?,
transactionData: PurchasedTransactionData,
observerMode: Bool,
initiationSource: ProductRequestData.InitiationSource,
subscriberAttributes subscriberAttributesByKey: SubscriberAttribute.Dictionary?,
completion: @escaping CustomerAPI.CustomerInfoResponseHandler) {
var subscriberAttributesToPost: SubscriberAttribute.Dictionary?

if !self.backendConfig.systemInfo.dangerousSettings.customEntitlementComputation {
subscriberAttributesToPost = subscriberAttributesByKey ?? [:]
subscriberAttributesToPost = transactionData.unsyncedAttributes ?? [:]
let attributionStatus = self.attributionFetcher.authorizationStatus
let consentStatus = SubscriberAttribute(attribute: ReservedSubscriberAttribute.consentStatus,
value: attributionStatus.description,
Expand All @@ -109,16 +104,14 @@ final class CustomerAPI {
}

let config = NetworkOperation.UserSpecificConfiguration(httpClient: self.backendConfig.httpClient,
appUserID: appUserID)
appUserID: transactionData.appUserID)

let postData = PostReceiptDataOperation.PostData(appUserID: appUserID,
receiptData: receiptData,
isRestore: isRestore,
productData: productData,
presentedOfferingIdentifier: offeringIdentifier,
observerMode: observerMode,
initiationSource: initiationSource,
subscriberAttributesByKey: subscriberAttributesToPost)
let postData = PostReceiptDataOperation.PostData(
transactionData: transactionData.withAttributesToPost(subscriberAttributesToPost),
productData: productData,
receiptData: receiptData,
observerMode: observerMode
)
let factory = PostReceiptDataOperation.createFactory(
configuration: config,
postData: postData,
Expand All @@ -136,3 +129,14 @@ final class CustomerAPI {
}

}

private extension PurchasedTransactionData {

func withAttributesToPost(_ newAttributes: SubscriberAttribute.Dictionary?) -> Self {
var copy = self
copy.unsyncedAttributes = newAttributes

return copy
}

}
22 changes: 22 additions & 0 deletions Sources/Networking/Operations/PostReceiptDataOperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,28 @@ final class PostReceiptDataOperation: CacheableNetworkOperation {

}

extension PostReceiptDataOperation.PostData {

init(
transactionData data: PurchasedTransactionData,
productData: ProductRequestData?,
receiptData: Data,
observerMode: Bool
) {
self.init(
appUserID: data.appUserID,
receiptData: receiptData,
isRestore: data.source.isRestore,
productData: productData,
presentedOfferingIdentifier: data.presentedOfferingID,
observerMode: observerMode,
initiationSource: data.source.initiationSource,
subscriberAttributesByKey: data.unsyncedAttributes
)
}

}

// MARK: - Private

private extension PostReceiptDataOperation {
Expand Down
2 changes: 0 additions & 2 deletions Sources/Purchasing/Purchases/Purchases.swift
Original file line number Diff line number Diff line change
Expand Up @@ -356,8 +356,6 @@ public typealias StartPurchaseBlock = (@escaping PurchaseCompletedBlock) -> Void
let transactionPoster = TransactionPoster(
productsManager: productsManager,
receiptFetcher: receiptFetcher,
currentUserProvider: identityManager,
attribution: subscriberAttributes,
backend: backend,
paymentQueueWrapper: paymentQueueWrapper,
systemInfo: systemInfo,
Expand Down
130 changes: 86 additions & 44 deletions Sources/Purchasing/Purchases/PurchasesOrchestrator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -829,14 +829,18 @@ extension PurchasesOrchestrator: StoreKit2TransactionListenerDelegate {
_ = try await Async.call { completed in
self.transactionPoster.handlePurchasedTransaction(
StoreTransaction.from(transaction: transaction),
presentedOfferingID: nil,
storefront: storefront,
source: .init(
isRestore: self.allowSharingAppStoreAccount,
initiationSource: .queue
data: .init(
appUserID: self.appUserID,
presentedOfferingID: nil,
unsyncedAttributes: self.unsyncedAttributes,
storefront: storefront,
source: .init(
isRestore: self.allowSharingAppStoreAccount,
initiationSource: .queue
)
)
) { _, customerInfo, error, _ in
completed(Result(customerInfo, error))
) { result in
completed(result.mapError(\.asPurchasesError))
}
}
}
Expand Down Expand Up @@ -897,6 +901,24 @@ private extension PurchasesOrchestrator {
}
}

func markSyncedIfNeeded(
subscriberAttributes: SubscriberAttribute.Dictionary?,
error: BackendError?
) {
if let error = error {
guard error.successfullySynced else { return }

if let attributeErrors = (error as NSError).subscriberAttributesErrors, !attributeErrors.isEmpty {
Logger.error(Strings.attribution.subscriber_attributes_error(
errors: attributeErrors
))
}
}

self.attribution.markAttributesAsSynced(subscriberAttributes, appUserID: self.appUserID)
}

// swiftlint:disable:next function_body_length
func syncPurchases(receiptRefreshPolicy: ReceiptRefreshPolicy,
isRestore: Bool,
initiationSource: ProductRequestData.InitiationSource,
Expand Down Expand Up @@ -945,13 +967,15 @@ private extension PurchasesOrchestrator {

self.createProductRequestData(with: receiptData) { productRequestData in
self.backend.post(receiptData: receiptData,
appUserID: currentAppUserID,
isRestore: isRestore,
productData: productRequestData,
presentedOfferingIdentifier: nil,
observerMode: self.observerMode,
initiationSource: initiationSource,
subscriberAttributes: unsyncedAttributes) { result in
transactionData: .init(
appUserID: currentAppUserID,
presentedOfferingID: nil,
unsyncedAttributes: unsyncedAttributes,
storefront: productRequestData?.storefront,
source: .init(isRestore: isRestore, initiationSource: initiationSource)
),
observerMode: self.observerMode) { result in
self.handleReceiptPost(result: result,
subscriberAttributes: unsyncedAttributes,
completion: completion)
Expand All @@ -964,41 +988,56 @@ private extension PurchasesOrchestrator {
func handleReceiptPost(result: Result<CustomerInfo, BackendError>,
subscriberAttributes: SubscriberAttribute.Dictionary,
completion: (@Sendable (Result<CustomerInfo, PurchasesError>) -> Void)?) {
operationDispatcher.dispatchOnMainThread {
if let customerInfo = result.value {
self.customerInfoManager.cache(customerInfo: customerInfo, appUserID: self.appUserID)
}

self.transactionPoster.markSyncedIfNeeded(subscriberAttributes: subscriberAttributes,
error: result.error)
self.handlePostReceiptResult(
result,
subscriberAttributes: subscriberAttributes
)

if let completion = completion {
self.operationDispatcher.dispatchOnMainThread {
completion(result.mapError { $0.asPurchasesError })
}
if let completion = completion {
self.operationDispatcher.dispatchOnMainThread {
completion(result.mapError { $0.asPurchasesError })
}
}
}

func handlePostReceiptResult(_ result: Result<CustomerInfo, BackendError>,
subscriberAttributes: SubscriberAttribute.Dictionary) {
if let customerInfo = result.value {
self.customerInfoManager.cache(customerInfo: customerInfo, appUserID: self.appUserID)
}

self.markSyncedIfNeeded(subscriberAttributes: subscriberAttributes,
error: result.error)
}

func handlePurchasedTransaction(_ purchasedTransaction: StoreTransaction,
storefront: StorefrontType?,
restored: Bool) {
let offeringID = self.getAndRemovePresentedOfferingIdentifier(for: purchasedTransaction)
let unsyncedAttributes = self.unsyncedAttributes

self.transactionPoster.handlePurchasedTransaction(
purchasedTransaction,
presentedOfferingID: offeringID,
storefront: storefront,
source: self.purchaseSource(for: purchasedTransaction.productIdentifier,
restored: restored)
) { transaction, customerInfo, error, cancelled in
let completion = self.getAndRemovePurchaseCompletedCallback(forTransaction: purchasedTransaction)

if let customerInfo = customerInfo {
self.customerInfoManager.cache(customerInfo: customerInfo, appUserID: self.appUserID)
}
data: .init(
appUserID: self.appUserID,
presentedOfferingID: offeringID,
unsyncedAttributes: unsyncedAttributes,
storefront: storefront,
source: self.purchaseSource(for: purchasedTransaction.productIdentifier,
restored: restored)
)
) { result in
self.handlePostReceiptResult(result, subscriberAttributes: unsyncedAttributes)

completion?(transaction, customerInfo, error, cancelled)
if let completion = self.getAndRemovePurchaseCompletedCallback(forTransaction: purchasedTransaction) {
self.operationDispatcher.dispatchOnMainActor {
completion(purchasedTransaction,
result.value,
result.error?.asPublicError,
result.error?.isCancelledError ?? false
)
}
}
}
}

Expand Down Expand Up @@ -1095,20 +1134,23 @@ extension PurchasesOrchestrator {
) async throws -> CustomerInfo {
let storefront = await Storefront.currentStorefront
let offeringID = self.getAndRemovePresentedOfferingIdentifier(for: transaction)
let unsyncedAttributes = self.unsyncedAttributes

return try await withCheckedThrowingContinuation { continuation in
self.transactionPoster.handlePurchasedTransaction(
transaction,
presentedOfferingID: offeringID,
storefront: storefront,
source: .init(isRestore: self.allowSharingAppStoreAccount,
initiationSource: initiationSource)
) { _, info, error, _ in
if let customerInfo = info {
self.customerInfoManager.cache(customerInfo: customerInfo, appUserID: self.appUserID)
}
data: .init(
appUserID: self.appUserID,
presentedOfferingID: offeringID,
unsyncedAttributes: unsyncedAttributes,
storefront: storefront,
source: .init(isRestore: self.allowSharingAppStoreAccount,
initiationSource: initiationSource)
)
) { result in
self.handlePostReceiptResult(result, subscriberAttributes: unsyncedAttributes)

continuation.resume(with: Result(info, error))
continuation.resume(with: result.mapError(\.asPurchasesError))
}
}
}
Expand Down
Loading

0 comments on commit 397edda

Please sign in to comment.