Skip to content

Commit 44a93b5

Browse files
committed
Fix issue with promoted product event not firing on older devices
1 parent 8d7813f commit 44a93b5

File tree

5 files changed

+109
-15
lines changed

5 files changed

+109
-15
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,13 @@ We've like to update this solution as version changes in `react-native-iap`.
367367
#### How do I handle promoted products in ios?
368368

369369
- Offical doc is [here](https://developer.apple.com/app-store/promoting-in-app-purchases/)
370+
- Start the IAPPromotionObserver in `-[application:didFinishLaunchingWithOptions:]` your AppDelegate:
371+
372+
```objc
373+
// Add '#import "IAPPromotionObserver.h"' to your imports
374+
[IAPPromotionObserver startObserving];
375+
```
376+
370377
- Add an event listener for the `iap-promoted-product` event somewhere early in your app's lifecycle:
371378
372379
```javascript

ios/IAPPromotionObserver.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#import <StoreKit/StoreKit.h>
2+
3+
@protocol IAPPromotionObserverDelegate;
4+
5+
@interface IAPPromotionObserver: NSObject <SKPaymentTransactionObserver>
6+
7+
@property (strong, nonatomic, readonly) SKPayment *payment;
8+
@property (strong, nonatomic, readonly) SKProduct *product;
9+
@property (weak, nonatomic) id<IAPPromotionObserverDelegate> delegate;
10+
11+
+ (instancetype)sharedObserver;
12+
+ (void)startObserving;
13+
14+
@end
15+
16+
@protocol IAPPromotionObserverDelegate <NSObject>
17+
18+
@required
19+
- (BOOL)shouldAddStorePayment:(SKPayment *)payment forProduct:(SKProduct *)product;
20+
21+
@end

ios/IAPPromotionObserver.m

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#import "IAPPromotionObserver.h"
2+
3+
@interface IAPPromotionObserver () {
4+
SKPayment *_promotedPayment;
5+
SKProduct *_promotedProduct;
6+
}
7+
8+
@end
9+
10+
@implementation IAPPromotionObserver
11+
12+
+ (instancetype)sharedObserver {
13+
static IAPPromotionObserver *sharedInstance = nil;
14+
static dispatch_once_t onceToken;
15+
16+
dispatch_once(&onceToken, ^{
17+
sharedInstance = [[IAPPromotionObserver alloc] init];
18+
});
19+
20+
return sharedInstance;
21+
}
22+
23+
+ (void)startObserving {
24+
[IAPPromotionObserver sharedObserver];
25+
}
26+
27+
- (instancetype)init {
28+
if ((self = [super init])) {
29+
[SKPaymentQueue.defaultQueue addTransactionObserver:self];
30+
}
31+
32+
return self;
33+
}
34+
35+
-(void) dealloc {
36+
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
37+
}
38+
39+
- (SKPayment *)payment {
40+
return _promotedPayment;
41+
}
42+
43+
- (SKProduct *)product {
44+
return _promotedProduct;
45+
}
46+
47+
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions {
48+
}
49+
50+
- (BOOL)paymentQueue:(SKPaymentQueue *)queue shouldAddStorePayment:(SKPayment *)payment forProduct:(SKProduct *)product {
51+
_promotedProduct = product;
52+
_promotedPayment = payment;
53+
54+
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(shouldAddStorePayment:forProduct:)]) {
55+
return [self.delegate shouldAddStorePayment:payment forProduct:product];
56+
}
57+
58+
return NO;
59+
}
60+
61+
@end

ios/RNIap.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
28CBF364220349F6005F51AC /* IAPPromotionObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 28CBF363220349F6005F51AC /* IAPPromotionObserver.m */; };
1011
B3E7B58A1CC2AC0600A0062D /* RNIapIos.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNIapIos.m */; };
1112
/* End PBXBuildFile section */
1213

@@ -24,6 +25,8 @@
2425

2526
/* Begin PBXFileReference section */
2627
134814201AA4EA6300B7C361 /* libRNIap.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNIap.a; sourceTree = BUILT_PRODUCTS_DIR; };
28+
28CBF362220349F6005F51AC /* IAPPromotionObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IAPPromotionObserver.h; sourceTree = "<group>"; };
29+
28CBF363220349F6005F51AC /* IAPPromotionObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IAPPromotionObserver.m; sourceTree = "<group>"; };
2730
B3E7B5881CC2AC0600A0062D /* RNIapIos.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNIapIos.h; sourceTree = "<group>"; };
2831
B3E7B5891CC2AC0600A0062D /* RNIapIos.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNIapIos.m; sourceTree = "<group>"; };
2932
/* End PBXFileReference section */
@@ -52,6 +55,8 @@
5255
children = (
5356
B3E7B5881CC2AC0600A0062D /* RNIapIos.h */,
5457
B3E7B5891CC2AC0600A0062D /* RNIapIos.m */,
58+
28CBF362220349F6005F51AC /* IAPPromotionObserver.h */,
59+
28CBF363220349F6005F51AC /* IAPPromotionObserver.m */,
5560
134814211AA4EA7D00B7C361 /* Products */,
5661
);
5762
sourceTree = "<group>";
@@ -112,6 +117,7 @@
112117
isa = PBXSourcesBuildPhase;
113118
buildActionMask = 2147483647;
114119
files = (
120+
28CBF364220349F6005F51AC /* IAPPromotionObserver.m in Sources */,
115121
B3E7B58A1CC2AC0600A0062D /* RNIapIos.m in Sources */,
116122
);
117123
runOnlyForDeploymentPostprocessing = 0;

ios/RNIapIos.m

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
#import "RNIapIos.h"
2+
#import "IAPPromotionObserver.h"
23

34
#import <React/RCTLog.h>
45
#import <React/RCTConvert.h>
56

67
#import <StoreKit/StoreKit.h>
78

89
//////////////////////////////////////////////////// _//////////_ // Private Members
9-
@interface RNIapIos() {
10+
@interface RNIapIos() <IAPPromotionObserverDelegate> {
1011
NSMutableDictionary *promisesByKey;
1112
BOOL autoReceiptConform;
1213
SKPaymentTransaction *currentTransaction;
1314
dispatch_queue_t myQueue;
1415
BOOL hasListeners;
15-
SKProduct *promotedProduct;
16-
SKPayment *promotedPayment;
1716
}
1817
@end
1918

@@ -24,6 +23,7 @@ -(instancetype)init {
2423
if ((self = [super init])) {
2524
promisesByKey = [NSMutableDictionary dictionary];
2625
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
26+
[IAPPromotionObserver sharedObserver].delegate = self;
2727
}
2828
myQueue = dispatch_queue_create("reject", DISPATCH_QUEUE_SERIAL);
2929
validProducts = [NSMutableArray array];
@@ -49,6 +49,7 @@ - (void)stopObserving {
4949
- (void)addListener:(NSString *)eventName {
5050
[super addListener:eventName];
5151

52+
SKPayment *promotedPayment = [IAPPromotionObserver sharedObserver].payment;
5253
if ([eventName isEqualToString:@"iap-promoted-product"] && promotedPayment != nil) {
5354
[self sendEventWithName:@"iap-promoted-product" body:promotedPayment.productIdentifier];
5455
}
@@ -89,6 +90,14 @@ -(void)rejectPromisesForKey:(NSString*)key code:(NSString*)code message:(NSStrin
8990
}
9091
}
9192

93+
//////////////////////////////////////////////////// _//////////_ // IAPPromotionObserverDelegate
94+
- (BOOL)shouldAddStorePayment:(SKPayment *)payment forProduct:(SKProduct *)product {
95+
if (hasListeners) {
96+
[self sendEventWithName:@"iap-promoted-product" body:product.productIdentifier];
97+
}
98+
return NO;
99+
}
100+
92101
//////////////////////////////////////////////////// _//////////_// EXPORT_MODULE
93102
RCT_EXPORT_MODULE();
94103

@@ -209,11 +218,13 @@ -(void)rejectPromisesForKey:(NSString*)key code:(NSString*)code message:(NSStrin
209218
RCT_EXPORT_METHOD(promotedProduct:(RCTPromiseResolveBlock)resolve
210219
reject:(RCTPromiseRejectBlock)reject) {
211220
NSLog(@"\n\n\n *** get promoted product. \n\n.");
221+
SKProduct *promotedProduct = [IAPPromotionObserver sharedObserver].product;
212222
resolve(promotedProduct ? promotedProduct.productIdentifier : [NSNull null]);
213223
}
214224

215225
RCT_EXPORT_METHOD(buyPromotedProduct:(RCTPromiseResolveBlock)resolve
216226
reject:(RCTPromiseRejectBlock)reject) {
227+
SKPayment *promotedPayment = [IAPPromotionObserver sharedObserver].payment;
217228
if (promotedPayment) {
218229
NSLog(@"\n\n\n *** buy promoted product. \n\n.");
219230
[[SKPaymentQueue defaultQueue] addPayment:promotedPayment];
@@ -318,18 +329,6 @@ -(void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWi
318329
NSLog(@"\n\n\n restoreCompletedTransactionsFailedWithError \n\n.");
319330
}
320331

321-
- (BOOL)paymentQueue:(SKPaymentQueue *)queue shouldAddStorePayment:(SKPayment *)payment forProduct:(SKProduct *)product {
322-
NSLog(@"\n\n\n *** should add store payment. \n\n.");
323-
promotedProduct = product;
324-
promotedPayment = payment;
325-
326-
if (hasListeners) {
327-
[self sendEventWithName:@"iap-promoted-product" body:payment.productIdentifier];
328-
}
329-
330-
return NO;
331-
}
332-
333332
-(void)purchaseProcess:(SKPaymentTransaction *)transaction {
334333
if (autoReceiptConform) {
335334
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];

0 commit comments

Comments
 (0)