Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.3.7

* Adds `Future<SKStorefrontWrapper?> storefront()` in SKPaymentQueueWrapper class.

## 0.3.6+7

* Updates example code for current versions of Flutter.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ typedef void (^UpdatedDownloads)(NSArray<SKDownload *> *downloads);

@property(NS_NONATOMIC_IOSONLY, weak, nullable) id<SKPaymentQueueDelegate> delegate API_AVAILABLE(
ios(13.0), macos(10.15), watchos(6.2));
@property(NS_NONATOMIC_IOSONLY, readonly, nullable)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would we only want this to be nonatomic on iOS? Surely we don't have different threading semantics for macOS?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The storefront property in SKPaymentQueue is nonatomic on iOS. I tried to keep it the sam way but yeah we don't need it. Will revert to nonatomic for all.

SKStorefront *storefront API_AVAILABLE(ios(13.0), macos(10.15), watchos(6.2));

/// Creates a new FIAPaymentQueueHandler initialized with an empty
/// FIATransactionCache.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,4 +233,8 @@ - (BOOL)paymentQueue:(SKPaymentQueue *)queue
return self.queue.transactions;
}

- (SKStorefront *)storefront {
return self.queue.storefront;
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
[self canMakePayments:result];
} else if ([@"-[SKPaymentQueue transactions]" isEqualToString:call.method]) {
[self getPendingTransactions:result];
} else if ([@"-[SKPaymentQueue storefront]" isEqualToString:call.method]) {
[self getStorefront:result];
} else if ([@"-[InAppPurchasePlugin startProductRequest:result:]" isEqualToString:call.method]) {
[self handleProductRequestMethodCall:call result:result];
} else if ([@"-[InAppPurchasePlugin addPayment:result:]" isEqualToString:call.method]) {
Expand Down Expand Up @@ -139,6 +141,22 @@ - (void)getPendingTransactions:(FlutterResult)result {
result(transactionMaps);
}

- (void)getStorefront:(FlutterResult)result {
if (@available(iOS 13.0, *)) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should include macOS 10.15+ here too.

SKStorefront *storefront = self.paymentQueueHandler.storefront;
if (!storefront) {
result(nil);
return;
}
result([FIAObjectTranslator getMapFromSKStorefront:storefront]);
return;
} else {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: no need to wrap code after an early return in else.

NSLog(@"storefront is not avaialbe in iOS below 13.0.");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this should mention both iOS and macOS.

result(nil);
return;
}
}

- (void)handleProductRequestMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
if (![call.arguments isKindOfClass:[NSArray class]]) {
result([FlutterError errorWithCode:@"storekit_invalid_argument"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,71 @@ - (void)testGetPendingTransactions {
XCTAssertEqualObjects(resultArray, @[ transactionMap ]);
}

- (void)testPaymentQueueStorefront {
if (@available(iOS 13, *)) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or macOS 10.15

{

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: These should be two separate test methods, rather than sub-blocks of the same test. Unit tests should each test as few things as is feasible.

// storefront is not nil
XCTestExpectation *expectation = [self expectationWithDescription:@"expect success"];
FlutterMethodCall *call =
[FlutterMethodCall methodCallWithMethodName:@"-[SKPaymentQueue storefront]"
arguments:nil];
SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class);
NSDictionary *storefrontMap = @{
@"countryCode" : @"USA",
@"identifier" : @"unique_identifier",
};
OCMStub(mockQueue.storefront).andReturn([[SKStorefrontStub alloc] initWithMap:storefrontMap]);

__block NSDictionary *resultMap;
self.plugin.paymentQueueHandler =
[[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue
transactionsUpdated:nil
transactionRemoved:nil
restoreTransactionFailed:nil
restoreCompletedTransactionsFinished:nil
shouldAddStorePayment:nil
updatedDownloads:nil
transactionCache:OCMClassMock(FIATransactionCache.class)];
[self.plugin handleMethodCall:call
result:^(id r) {
resultMap = r;
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:5];
XCTAssertEqualObjects(resultMap, storefrontMap);
}
{
// storefront is nil
XCTestExpectation *expectation = [self expectationWithDescription:@"expect success"];
FlutterMethodCall *call =
[FlutterMethodCall methodCallWithMethodName:@"-[SKPaymentQueue storefront]"
arguments:nil];
SKPaymentQueue *mockQueue = OCMClassMock(SKPaymentQueue.class);
OCMStub(mockQueue.storefront).andReturn(nil);

__block NSDictionary *resultMap;
self.plugin.paymentQueueHandler =
[[FIAPaymentQueueHandler alloc] initWithQueue:mockQueue
transactionsUpdated:nil
transactionRemoved:nil
restoreTransactionFailed:nil
restoreCompletedTransactionsFinished:nil
shouldAddStorePayment:nil
updatedDownloads:nil
transactionCache:OCMClassMock(FIATransactionCache.class)];
[self.plugin handleMethodCall:call
result:^(id r) {
resultMap = r;
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:5];
XCTAssertNil(resultMap);
}
} else {
NSLog(@"Skip testPaymentQueueStorefront for iOS lower than 13.0.");
}
}

- (void)testStartObservingPaymentQueue {
XCTestExpectation *expectation =
[self expectationWithDescription:@"Should return success result"];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ class SKPaymentQueueWrapper {
SKPaymentQueueDelegateWrapper? _paymentQueueDelegate;
SKTransactionObserverWrapper? _observer;

/// Calls [`[SKPaymentQueue storefront]`](https://developer.apple.com/documentation/storekit/skpaymentqueue/3182430-storefront?language=objc)
Comment thread
stuartmorgan-g marked this conversation as resolved.
Outdated

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is missing a period.

Future<SKStorefrontWrapper?> storefront() async {
final Map<String, dynamic>? storefrontMap = await channel
.invokeMapMethod<String, dynamic>('-[SKPaymentQueue storefront]');
if (storefrontMap == null) {
return null;
}
return SKStorefrontWrapper.fromJson(storefrontMap);
}

/// Calls [`-[SKPaymentQueue transactions]`](https://developer.apple.com/documentation/storekit/skpaymentqueue/1506026-transactions?language=objc)
Future<List<SKPaymentTransactionWrapper>> transactions() async {
return _getTransactionList((await channel
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: in_app_purchase_storekit
description: An implementation for the iOS and macOS platforms of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework.
repository: https://github.com/flutter/packages/tree/main/packages/in_app_purchase/in_app_purchase_storekit
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22
version: 0.3.6+7
version: 0.3.7

environment:
sdk: ">=2.19.0 <4.0.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,22 @@ void main() {
expect(await SKPaymentQueueWrapper.canMakePayments(), false);
});

test('storefront returns valid SKStoreFrontWrapper object', () async {
final SKPaymentQueueWrapper queue = SKPaymentQueueWrapper();
expect(
await queue.storefront(),
SKStorefrontWrapper.fromJson(const <String, dynamic>{
'countryCode': 'USA',
'identifier': 'unique_identifier',
}));
});

test('storefront returns null', () async {
fakeStoreKitPlatform.testReturnNull = true;
final SKPaymentQueueWrapper queue = SKPaymentQueueWrapper();
expect(await queue.storefront(), isNull);
});

test('transactions should return a valid list of transactions', () async {
expect(await SKPaymentQueueWrapper().transactions(), isNotEmpty);
});
Expand Down Expand Up @@ -250,6 +266,14 @@ class FakeStoreKitPlatform {
case '-[SKPaymentQueue transactions]':
return Future<List<dynamic>>.value(
<dynamic>[buildTransactionMap(dummyTransaction)]);
case '-[SKPaymentQueue storefront]':
if (testReturnNull) {
return Future<dynamic>.value();
}
return Future<Map<String, dynamic>>.value(const <String, dynamic>{
'countryCode': 'USA',
'identifier': 'unique_identifier',
});
case '-[InAppPurchasePlugin addPayment:result:]':
payments.add(SKPaymentWrapper.fromJson(Map<String, dynamic>.from(
call.arguments as Map<dynamic, dynamic>)));
Expand Down