Skip to content

Commit

Permalink
Uses transactionUpdated listener for Sk2 (#1966)
Browse files Browse the repository at this point in the history
* Uses transactionUpdated listener for Sk2

* swiftlint

* Added optional parameter alsoPublishToEventListener

* lint doc

Co-authored-by: Andres Aguilar <andres.aguilar@nfl.com>
  • Loading branch information
andresesfm and Andres Aguilar authored Sep 19, 2022
1 parent 16d9989 commit e20ebd7
Show file tree
Hide file tree
Showing 15 changed files with 230 additions and 134 deletions.
4 changes: 0 additions & 4 deletions docs/docs/api-reference/methods/listeners/_category_.json

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

18 changes: 15 additions & 3 deletions docs/docs/migrate_to_11.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ This allows for greater flexibility to use methods that are specific to a platfo

## Using Storekit 2

Storekit 2 requiers iOS 15 as a minimum. If your app supports older iOS versions, you'll have to consider the endgecases of jumping back and forth. The library will use the old implementation (Storekit 1) as a default on devices with older versions of iOS
Storekit 2 (Sk2) requires iOS 15 as a minimum. If your app supports older iOS versions, you'll have to consider the endgecases of jumping back and forth. The library will use the old implementation (Storekit 1) as a default on devices with older versions of iOS

### How do I know what's the minimum version of iOS my app supports?

Expand Down Expand Up @@ -61,6 +61,18 @@ The name of this parameter has changed to match the new API

- Purchase promoted product. I haven't found the equivalent of promoted product purchase in the new SDK.

- `transactionReceipt`,`purchaseToken` are not available on Sk2's purchases
- `transactionReceipt`,`purchaseToken` are not available on `Purchase`s when using Sk

- `currency` is no longer available, use `localizedPrice` instead
- `currency` is no longer available in `Product` of `Subscription` objects, use `localizedPrice` instead

### Event Listeners

In Sk1 we have Promoted product, purchase updated and purchase error
In Sk2 we have Transaction Updated. Purchase updated and purchase error and only for backward compatibility and will be removed on a future iteration.

Note that the Transaction Updated Listener accepts `TransactionEvent`s that can be either an error or a valid transaction.

### Why change the Event Listeners?

The new native libraries allow us to move away from sending events and instead returning responses for events that are generated by user interaction. Having both models becomes hard to document so we are phasing it out and keeping only one listener for events outside the normal purchase flow.
For example `getAvaiableItems` was both posting events and returing the list of events. That won't be the case when using Sk2, it will only return the list of Transactions. If you still want to get the events published in Sk1, use the optional parameter `{alsoPublishToEventListener:true}` in `getAvailableItems`
6 changes: 0 additions & 6 deletions ios/IapTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,6 @@ import StoreKit

typealias RNIapIosPromise = (RCTPromiseResolveBlock, RCTPromiseRejectBlock)

@available(iOS 15.0, *)
struct ProductOrError {
let product: Product?
let error: Error?
}

public enum StoreError: Error {
case failedVerification
}
Expand Down
3 changes: 2 additions & 1 deletion ios/RNIapIos.swift
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ class RNIapIos: RCTEventEmitter, SKRequestDelegate, SKPaymentTransactionObserver
}
}
@objc public func getAvailableItems(
alsoPublishToEventListener: Bool,
_ resolve: @escaping RCTPromiseResolveBlock = { _ in },
reject: @escaping RCTPromiseRejectBlock = { _, _, _ in }
) {
Expand Down Expand Up @@ -831,7 +832,7 @@ class RNIapIos: RCTEventEmitter, SKRequestDelegate, SKPaymentTransactionObserver
"transactionDate": transaction.transactionDate?.millisecondsSince1970String,
"transactionId": transaction.transactionIdentifier,
"productId": transaction.payment.productIdentifier,
"transactionReceipt": receiptData?.base64EncodedString(options: [])
"transactionReceipt": receiptData?.base64EncodedString(options: [.endLineWithCarriageReturn])
]

// originalTransaction is available for restore purchase and purchase of cancelled/expired subscriptions
Expand Down
3 changes: 2 additions & 1 deletion ios/RNIapIosSk2.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ @interface RCT_EXTERN_MODULE (RNIapIosSk2, NSObject)
reject:(RCTPromiseRejectBlock)reject)

RCT_EXTERN_METHOD(getAvailableItems:
(RCTPromiseResolveBlock)resolve
alsoPublishToEventListener:(BOOL)alsoPublishToEventListener
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)

RCT_EXTERN_METHOD(buyProduct:
Expand Down
25 changes: 18 additions & 7 deletions ios/RNIapIosSk2.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class RNIapIosSk2: RCTEventEmitter {

if self.hasListeners {
self.sendEvent(withName: "purchase-updated", body: serialize(transaction))
self.sendEvent(withName: "iap-transaction-updated", body: ["transaction": serialize(transaction)])
}
// Always finish a transaction.
// await transaction.finish()
Expand All @@ -69,6 +70,7 @@ class RNIapIosSk2: RCTEventEmitter {
]

self.sendEvent(withName: "purchase-error", body: err)
self.sendEvent(withName: "iap-transaction-updated", body: ["error": err])
}
}
}
Expand All @@ -86,8 +88,13 @@ class RNIapIosSk2: RCTEventEmitter {
super.addListener(eventName)
}

/**
"iap-transaction-updated" is unique to Sk2.
"iap-promoted-product" is only avaiable on Sk1
"purchase-updated", "purchase-error" are for backward compatibility
*/
override func supportedEvents() -> [String]? {
return [ "iap-promoted-product", "purchase-updated", "purchase-error"]
return [ "purchase-updated", "purchase-error", "iap-transaction-updated"]
}

@objc public func initConnection(
Expand Down Expand Up @@ -129,19 +136,23 @@ class RNIapIosSk2: RCTEventEmitter {
}

@objc public func getAvailableItems(
alsoPublishToEventListener: Bool,
_ resolve: @escaping RCTPromiseResolveBlock = { _ in },
reject: @escaping RCTPromiseRejectBlock = { _, _, _ in }
) {
Task {
var purchasedItems: [ProductOrError] = []
var purchasedItems: [Transaction] = []

func addPurchase(transaction: Transaction, product: Product) {
purchasedItems.append(ProductOrError(product: product, error: nil))
sendEvent(withName: "purchase-update", body: serialize(transaction))
purchasedItems.append( transaction)
if alsoPublishToEventListener {
sendEvent(withName: "purchase-update", body: serialize(transaction))
}
}
func addError( error: Error, errorDict: [String: String]) {
purchasedItems.append(ProductOrError(product: nil, error: error))
sendEvent(withName: "purchase-error", body: errorDict)
if alsoPublishToEventListener {
sendEvent(withName: "purchase-error", body: errorDict)
}
}
// Iterate through all of the user's purchased products.
for await result in Transaction.currentEntitlements {
Expand Down Expand Up @@ -203,7 +214,7 @@ class RNIapIosSk2: RCTEventEmitter {
// group, so products in the subscriptions array all belong to the same group. The statuses that
// `product.subscription.status` returns apply to the entire subscription group.
// subscriptionGroupStatus = try? await subscriptions.first?.subscription?.status.first?.state
resolve(purchasedItems.map({(p: ProductOrError) in ["product": p.product.flatMap { serialize($0)}, "error": serialize(p.error)]}))
resolve(purchasedItems.map({(t: Transaction) in serialize(t)}))
}
}

Expand Down
Loading

0 comments on commit e20ebd7

Please sign in to comment.