diff --git a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md index 46c455c8093..070f0753e8a 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md +++ b/packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.16 + +* Converts main plugin class to Swift + ## 0.3.15 * Replaces `getCountryCode` with `countryCode`. diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin+TestOnly.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin+TestOnly.h deleted file mode 100644 index ca090b6b992..00000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin+TestOnly.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import -#import "FIAPRequestHandler.h" -#import "InAppPurchasePlugin.h" - -@interface InAppPurchasePlugin () - -// Holding strong references to FIAPRequestHandlers. Remove the handlers from the set after -// the request is finished. -@property(strong, nonatomic, readonly) NSMutableSet *requestHandlers; - -// Callback channel to dart used for when a function from the transaction observer is triggered. -@property(strong, nonatomic) FlutterMethodChannel *transactionObserverCallbackChannel; - -// Callback channel to dart used for when a function from the transaction observer is triggered. -@property(copy, nonatomic) FIAPRequestHandler * (^handlerFactory)(SKRequest *); - -// Convenience initializer with dependancy injection -- (instancetype)initWithReceiptManager:(FIAPReceiptManager *)receiptManager - handlerFactory:(FIAPRequestHandler * (^)(SKRequest *))handlerFactory; - -// Transaction observer methods -- (void)handleTransactionsUpdated:(NSArray *)transactions; -- (void)handleTransactionsRemoved:(NSArray *)transactions; -- (void)handleTransactionRestoreFailed:(NSError *)error; -- (void)restoreCompletedTransactionsFinished; -- (BOOL)shouldAddStorePayment:(SKPayment *)payment product:(SKProduct *)product; - -@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.h deleted file mode 100644 index a2b9cb0eb93..00000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#if TARGET_OS_OSX -#import -#else -#import -#endif -#import "messages.g.h" - -@class FIAPaymentQueueHandler; -@class FIAPReceiptManager; - -@interface InAppPurchasePlugin : NSObject - -@property(strong, nonatomic) FIAPaymentQueueHandler *paymentQueueHandler; - -- (instancetype)initWithReceiptManager:(FIAPReceiptManager *)receiptManager - NS_DESIGNATED_INITIALIZER; -- (instancetype)init NS_UNAVAILABLE; - -@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m deleted file mode 100644 index 2e2abaaed35..00000000000 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m +++ /dev/null @@ -1,404 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "InAppPurchasePlugin.h" -#import -#import "FIAObjectTranslator.h" -#import "FIAPPaymentQueueDelegate.h" -#import "FIAPReceiptManager.h" -#import "FIAPRequestHandler.h" -#import "FIAPaymentQueueHandler.h" -#import "InAppPurchasePlugin+TestOnly.h" - -@interface InAppPurchasePlugin () - -// After querying the product, the available products will be saved in the map to be used -// for purchase. -@property(strong, nonatomic, readonly) NSMutableDictionary *productsCache; - -// Callback channel to dart used for when a function from the payment queue delegate is triggered. -@property(strong, nonatomic, readonly) FlutterMethodChannel *paymentQueueDelegateCallbackChannel; -@property(strong, nonatomic, readonly) NSObject *registrar; - -@property(strong, nonatomic, readonly) FIAPReceiptManager *receiptManager; -@property(strong, nonatomic, readonly) - FIAPPaymentQueueDelegate *paymentQueueDelegate API_AVAILABLE(ios(13)) - API_UNAVAILABLE(tvos, macos, watchos); - -@end - -@implementation InAppPurchasePlugin - -+ (void)registerWithRegistrar:(NSObject *)registrar { - FlutterMethodChannel *channel = - [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/in_app_purchase" - binaryMessenger:[registrar messenger]]; - - InAppPurchasePlugin *instance = [[InAppPurchasePlugin alloc] initWithRegistrar:registrar]; - [registrar addMethodCallDelegate:instance channel:channel]; - [registrar addApplicationDelegate:instance]; - SetUpInAppPurchaseAPI(registrar.messenger, instance); -} - -- (instancetype)initWithReceiptManager:(FIAPReceiptManager *)receiptManager { - self = [super init]; - _receiptManager = receiptManager; - _requestHandlers = [NSMutableSet new]; - _productsCache = [NSMutableDictionary new]; - _handlerFactory = ^FIAPRequestHandler *(SKRequest *request) { - return [[FIAPRequestHandler alloc] initWithRequest:request]; - }; - return self; -} - -- (instancetype)initWithReceiptManager:(FIAPReceiptManager *)receiptManager - handlerFactory:(FIAPRequestHandler * (^)(SKRequest *))handlerFactory { - self = [self initWithReceiptManager:receiptManager]; - _handlerFactory = [handlerFactory copy]; - return self; -} - -- (instancetype)initWithRegistrar:(NSObject *)registrar { - self = [self initWithReceiptManager:[FIAPReceiptManager new]]; - _registrar = registrar; - - __weak typeof(self) weakSelf = self; - _paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:[SKPaymentQueue defaultQueue] - transactionsUpdated:^(NSArray *_Nonnull transactions) { - [weakSelf handleTransactionsUpdated:transactions]; - } - transactionRemoved:^(NSArray *_Nonnull transactions) { - [weakSelf handleTransactionsRemoved:transactions]; - } - restoreTransactionFailed:^(NSError *_Nonnull error) { - [weakSelf handleTransactionRestoreFailed:error]; - } - restoreCompletedTransactionsFinished:^{ - [weakSelf restoreCompletedTransactionsFinished]; - } - shouldAddStorePayment:^BOOL(SKPayment *payment, SKProduct *product) { - return [weakSelf shouldAddStorePayment:payment product:product]; - } - updatedDownloads:^void(NSArray *_Nonnull downloads) { - [weakSelf updatedDownloads:downloads]; - } - transactionCache:[[FIATransactionCache alloc] init]]; - - _transactionObserverCallbackChannel = - [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/in_app_purchase" - binaryMessenger:[registrar messenger]]; - return self; -} - -- (nullable NSNumber *)canMakePaymentsWithError: - (FlutterError *_Nullable __autoreleasing *_Nonnull)error { - return @([SKPaymentQueue canMakePayments]); -} - -- (nullable NSArray *)transactionsWithError: - (FlutterError *_Nullable *_Nonnull)error { - NSArray *transactions = - [self.paymentQueueHandler getUnfinishedTransactions]; - NSMutableArray *transactionMaps = [[NSMutableArray alloc] init]; - for (SKPaymentTransaction *transaction in transactions) { - [transactionMaps addObject:[FIAObjectTranslator convertTransactionToPigeon:transaction]]; - } - return transactionMaps; -} - -- (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable *_Nonnull)error - API_AVAILABLE(ios(13.0), macos(10.15)) { - SKStorefront *storefront = self.paymentQueueHandler.storefront; - if (!storefront) { - return nil; - } - return [FIAObjectTranslator convertStorefrontToPigeon:storefront]; -} - -- (void)startProductRequestProductIdentifiers:(NSArray *)productIdentifiers - completion:(void (^)(SKProductsResponseMessage *_Nullable, - FlutterError *_Nullable))completion { - SKProductsRequest *request = - [self getProductRequestWithIdentifiers:[NSSet setWithArray:productIdentifiers]]; - FIAPRequestHandler *handler = self.handlerFactory(request); - [self.requestHandlers addObject:handler]; - __weak typeof(self) weakSelf = self; - - [handler startProductRequestWithCompletionHandler:^(SKProductsResponse *_Nullable response, - NSError *_Nullable startProductRequestError) { - FlutterError *error = nil; - if (startProductRequestError != nil) { - error = [FlutterError errorWithCode:@"storekit_getproductrequest_platform_error" - message:startProductRequestError.localizedDescription - details:startProductRequestError.description]; - completion(nil, error); - return; - } - if (!response) { - error = [FlutterError errorWithCode:@"storekit_platform_no_response" - message:@"Failed to get SKProductResponse in startRequest " - @"call. Error occured on iOS platform" - details:productIdentifiers]; - completion(nil, error); - return; - } - for (SKProduct *product in response.products) { - [self.productsCache setObject:product forKey:product.productIdentifier]; - } - - completion([FIAObjectTranslator convertProductsResponseToPigeon:response], error); - [weakSelf.requestHandlers removeObject:handler]; - }]; -} - -- (void)addPaymentPaymentMap:(nonnull NSDictionary *)paymentMap - error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { - NSString *productID = [paymentMap objectForKey:@"productIdentifier"]; - // When a product is already fetched, we create a payment object with - // the product to process the payment. - SKProduct *product = [self getProduct:productID]; - if (!product) { - *error = [FlutterError - errorWithCode:@"storekit_invalid_payment_object" - message: - @"You have requested a payment for an invalid product. Either the " - @"`productIdentifier` of the payment is not valid or the product has not been " - @"fetched before adding the payment to the payment queue." - details:paymentMap]; - return; - } - - SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product]; - payment.applicationUsername = [paymentMap objectForKey:@"applicationUsername"]; - NSNumber *quantity = [paymentMap objectForKey:@"quantity"]; - payment.quantity = (quantity != nil) ? quantity.integerValue : 1; - NSNumber *simulatesAskToBuyInSandbox = [paymentMap objectForKey:@"simulatesAskToBuyInSandbox"]; - payment.simulatesAskToBuyInSandbox = (id)simulatesAskToBuyInSandbox == (id)[NSNull null] - ? NO - : [simulatesAskToBuyInSandbox boolValue]; - - if (@available(iOS 12.2, *)) { - NSDictionary *paymentDiscountMap = [self getNonNullValueFromDictionary:paymentMap - forKey:@"paymentDiscount"]; - NSString *errorMsg = nil; - SKPaymentDiscount *paymentDiscount = - [FIAObjectTranslator getSKPaymentDiscountFromMap:paymentDiscountMap withError:&errorMsg]; - - if (errorMsg) { - *error = [FlutterError - errorWithCode:@"storekit_invalid_payment_discount_object" - message:[NSString stringWithFormat:@"You have requested a payment and specified a " - @"payment discount with invalid properties. %@", - errorMsg] - details:paymentMap]; - return; - } - - payment.paymentDiscount = paymentDiscount; - } - if (![self.paymentQueueHandler addPayment:payment]) { - *error = [FlutterError - errorWithCode:@"storekit_duplicate_product_object" - message:@"There is a pending transaction for the same product identifier. Please " - @"either wait for it to be finished or finish it manually using " - @"`completePurchase` to avoid edge cases." - - details:paymentMap]; - return; - } -} - -- (void)finishTransactionFinishMap:(nonnull NSDictionary *)finishMap - error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { - NSString *transactionIdentifier = [finishMap objectForKey:@"transactionIdentifier"]; - NSString *productIdentifier = [finishMap objectForKey:@"productIdentifier"]; - - NSArray *pendingTransactions = - [self.paymentQueueHandler getUnfinishedTransactions]; - - for (SKPaymentTransaction *transaction in pendingTransactions) { - // If the user cancels the purchase dialog we won't have a transactionIdentifier. - // So if it is null AND a transaction in the pendingTransactions list has - // also a null transactionIdentifier we check for equal product identifiers. - if ([transaction.transactionIdentifier isEqualToString:transactionIdentifier] || - ([transactionIdentifier isEqual:[NSNull null]] && - transaction.transactionIdentifier == nil && - [transaction.payment.productIdentifier isEqualToString:productIdentifier])) { - @try { - [self.paymentQueueHandler finishTransaction:transaction]; - } @catch (NSException *e) { - *error = [FlutterError errorWithCode:@"storekit_finish_transaction_exception" - message:e.name - details:e.description]; - return; - } - } - } -} - -- (void)restoreTransactionsApplicationUserName:(nullable NSString *)applicationUserName - error:(FlutterError *_Nullable __autoreleasing *_Nonnull) - error { - [self.paymentQueueHandler restoreTransactions:applicationUserName]; -} - -- (void)presentCodeRedemptionSheetWithError: - (FlutterError *_Nullable __autoreleasing *_Nonnull)error { -#if TARGET_OS_IOS - [self.paymentQueueHandler presentCodeRedemptionSheet]; -#endif -} - -- (nullable NSString *)retrieveReceiptDataWithError: - (FlutterError *_Nullable __autoreleasing *_Nonnull)error { - FlutterError *flutterError; - NSString *receiptData = [self.receiptManager retrieveReceiptWithError:&flutterError]; - if (flutterError) { - *error = flutterError; - return nil; - } - return receiptData; -} - -- (void)refreshReceiptReceiptProperties:(nullable NSDictionary *)receiptProperties - completion:(nonnull void (^)(FlutterError *_Nullable))completion { - SKReceiptRefreshRequest *request; - if (receiptProperties) { - // if recieptProperties is not null, this call is for testing. - NSMutableDictionary *properties = [NSMutableDictionary new]; - properties[SKReceiptPropertyIsExpired] = receiptProperties[@"isExpired"]; - properties[SKReceiptPropertyIsRevoked] = receiptProperties[@"isRevoked"]; - properties[SKReceiptPropertyIsVolumePurchase] = receiptProperties[@"isVolumePurchase"]; - request = [self getRefreshReceiptRequest:properties]; - } else { - request = [self getRefreshReceiptRequest:nil]; - } - - FIAPRequestHandler *handler = self.handlerFactory(request); - [self.requestHandlers addObject:handler]; - __weak typeof(self) weakSelf = self; - [handler startProductRequestWithCompletionHandler:^(SKProductsResponse *_Nullable response, - NSError *_Nullable error) { - FlutterError *requestError; - if (error) { - requestError = [FlutterError errorWithCode:@"storekit_refreshreceiptrequest_platform_error" - message:error.localizedDescription - details:error.description]; - completion(requestError); - return; - } - completion(nil); - [weakSelf.requestHandlers removeObject:handler]; - }]; -} - -- (void)startObservingPaymentQueueWithError: - (FlutterError *_Nullable __autoreleasing *_Nonnull)error { - [_paymentQueueHandler startObservingPaymentQueue]; -} - -- (void)stopObservingPaymentQueueWithError: - (FlutterError *_Nullable __autoreleasing *_Nonnull)error { - [_paymentQueueHandler stopObservingPaymentQueue]; -} - -- (void)registerPaymentQueueDelegateWithError: - (FlutterError *_Nullable __autoreleasing *_Nonnull)error { -#if TARGET_OS_IOS - if (@available(iOS 13.0, *)) { - _paymentQueueDelegateCallbackChannel = [FlutterMethodChannel - methodChannelWithName:@"plugins.flutter.io/in_app_purchase_payment_queue_delegate" - binaryMessenger:[_registrar messenger]]; - - _paymentQueueDelegate = [[FIAPPaymentQueueDelegate alloc] - initWithMethodChannel:_paymentQueueDelegateCallbackChannel]; - _paymentQueueHandler.delegate = _paymentQueueDelegate; - } -#endif -} - -- (void)removePaymentQueueDelegateWithError: - (FlutterError *_Nullable __autoreleasing *_Nonnull)error { - if (@available(iOS 13.0, *)) { - _paymentQueueHandler.delegate = nil; - } - _paymentQueueDelegate = nil; - _paymentQueueDelegateCallbackChannel = nil; -} - -- (void)showPriceConsentIfNeededWithError:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { -#if TARGET_OS_IOS - if (@available(iOS 13.4, *)) { - [_paymentQueueHandler showPriceConsentIfNeeded]; - } -#endif -} - -- (id)getNonNullValueFromDictionary:(NSDictionary *)dictionary forKey:(NSString *)key { - id value = dictionary[key]; - return [value isKindOfClass:[NSNull class]] ? nil : value; -} - -#pragma mark - transaction observer: - -- (void)handleTransactionsUpdated:(NSArray *)transactions { - NSMutableArray *maps = [NSMutableArray new]; - for (SKPaymentTransaction *transaction in transactions) { - [maps addObject:[FIAObjectTranslator getMapFromSKPaymentTransaction:transaction]]; - } - [self.transactionObserverCallbackChannel invokeMethod:@"updatedTransactions" arguments:maps]; -} - -- (void)handleTransactionsRemoved:(NSArray *)transactions { - NSMutableArray *maps = [NSMutableArray new]; - for (SKPaymentTransaction *transaction in transactions) { - [maps addObject:[FIAObjectTranslator getMapFromSKPaymentTransaction:transaction]]; - } - [self.transactionObserverCallbackChannel invokeMethod:@"removedTransactions" arguments:maps]; -} - -- (void)handleTransactionRestoreFailed:(NSError *)error { - [self.transactionObserverCallbackChannel - invokeMethod:@"restoreCompletedTransactionsFailed" - arguments:[FIAObjectTranslator getMapFromNSError:error]]; -} - -- (void)restoreCompletedTransactionsFinished { - [self.transactionObserverCallbackChannel - invokeMethod:@"paymentQueueRestoreCompletedTransactionsFinished" - arguments:nil]; -} - -- (void)updatedDownloads:(NSArray *)downloads { - NSLog(@"Received an updatedDownloads callback, but downloads are not supported."); -} - -- (BOOL)shouldAddStorePayment:(SKPayment *)payment product:(SKProduct *)product { - // We always return NO here. And we send the message to dart to process the payment; and we will - // have a interception method that deciding if the payment should be processed (implemented by the - // programmer). - [self.productsCache setObject:product forKey:product.productIdentifier]; - [self.transactionObserverCallbackChannel - invokeMethod:@"shouldAddStorePayment" - arguments:@{ - @"payment" : [FIAObjectTranslator getMapFromSKPayment:payment], - @"product" : [FIAObjectTranslator getMapFromSKProduct:product] - }]; - return NO; -} - -#pragma mark - dependency injection (for unit testing) - -- (SKProductsRequest *)getProductRequestWithIdentifiers:(NSSet *)identifiers { - return [[SKProductsRequest alloc] initWithProductIdentifiers:identifiers]; -} - -- (SKProduct *)getProduct:(NSString *)productID { - return [self.productsCache objectForKey:productID]; -} - -- (SKReceiptRefreshRequest *)getRefreshReceiptRequest:(NSDictionary *)properties { - return [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:properties]; -} -@end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift new file mode 100644 index 00000000000..511fc264c0a --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.swift @@ -0,0 +1,430 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Foundation +import StoreKit + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#endif + +public class InAppPurchasePlugin: NSObject, FlutterPlugin, InAppPurchaseAPI { + private let receiptManager: FIAPReceiptManager + private var productsCache: NSMutableDictionary = [:] + private var paymentQueueDelegateCallbackChannel: FlutterMethodChannel? + // note - the type should be FIAPPaymentQueueDelegate, but this is only available >= iOS 13, + // FIAPPaymentQueueDelegate only gets set/used in registerPaymentQueueDelegateWithError or removePaymentQueueDelegateWithError, which both are ios13+ only + private var paymentQueueDelegate: Any? + private var requestHandlers = Set() + private var handlerFactory: ((SKRequest) -> FIAPRequestHandler) + // TODO(louisehsu): Once tests are migrated to swift, we can use @testable import, and make theses vars private again and remove all instances of @objc + @objc + public var registrar: FlutterPluginRegistrar? + // This property is optional, as it requires self to exist to be initialized. + @objc + public var paymentQueueHandler: FIAPaymentQueueHandler? + // This property is optional, as it needs to be set during plugin registration, and can't be directly initialized. + @objc + public var transactionObserverCallbackChannel: FlutterMethodChannel? + + public static func register(with registrar: FlutterPluginRegistrar) { + #if os(iOS) + let messenger = registrar.messenger() + #endif + #if os(macOS) + let messenger = registrar.messenger + #endif + let channel = FlutterMethodChannel( + name: "plugins.flutter.io/in_app_purchase", + binaryMessenger: messenger) + let instance = InAppPurchasePlugin(registrar: registrar) + registrar.addMethodCallDelegate(instance, channel: channel) + registrar.addApplicationDelegate(instance) + SetUpInAppPurchaseAPI(messenger, instance) + } + + @objc + // This init is used for tests + public init( + receiptManager: FIAPReceiptManager, + handlerFactory: @escaping (SKRequest) -> FIAPRequestHandler = { + FIAPRequestHandler(request: $0) + } + ) { + self.receiptManager = receiptManager + self.handlerFactory = handlerFactory + super.init() + } + + // This init gets called during plugin registration + public convenience init(registrar: FlutterPluginRegistrar) { + self.init(receiptManager: FIAPReceiptManager()) + self.registrar = registrar + + self.paymentQueueHandler = FIAPaymentQueueHandler( + queue: SKPaymentQueue.default(), + transactionsUpdated: { [weak self] transactions in + self?.handleTransactionsUpdated(transactions) + }, + transactionRemoved: { [weak self] transactions in + self?.handleTransactionsRemoved(transactions) + }, + restoreTransactionFailed: { [weak self] error in + self?.handleTransactionRestoreFailed(error as NSError) + }, + restoreCompletedTransactionsFinished: { [weak self] in + self?.restoreCompletedTransactionsFinished() + }, + shouldAddStorePayment: { [weak self] (payment, product) -> Bool in + return self?.shouldAddStorePayment(payment: payment, product: product) ?? false + }, + updatedDownloads: { [weak self] _ in + self?.updatedDownloads() + }, + transactionCache: FIATransactionCache()) + #if os(iOS) + let messenger = registrar.messenger() + #endif + #if os(macOS) + let messenger = registrar.messenger + #endif + transactionObserverCallbackChannel = FlutterMethodChannel( + name: "plugins.flutter.io/in_app_purchase", binaryMessenger: messenger) + } + + // MARK: - Pigeon Functions + + public func canMakePaymentsWithError(_ error: AutoreleasingUnsafeMutablePointer) + -> NSNumber? + { + return SKPaymentQueue.canMakePayments() as NSNumber + } + + public func transactionsWithError(_ error: AutoreleasingUnsafeMutablePointer) + -> [SKPaymentTransactionMessage]? + { + return getPaymentQueueHandler() + .getUnfinishedTransactions() + .compactMap { + FIAObjectTranslator.convertTransaction(toPigeon: $0) + } + } + + public func storefrontWithError(_ error: AutoreleasingUnsafeMutablePointer) + -> SKStorefrontMessage? + { + if #available(iOS 13.0, *), let storefront = getPaymentQueueHandler().storefront { + return FIAObjectTranslator.convertStorefront(toPigeon: storefront) + } + return nil + } + + public func startProductRequestProductIdentifiers( + _ productIdentifiers: [String], + completion: @escaping (SKProductsResponseMessage?, FlutterError?) -> Void + ) { + let request = getProductRequest(withIdentifiers: Set(productIdentifiers)) + let handler = handlerFactory(request) + requestHandlers.insert(handler) + + handler.startProductRequest { [weak self] response, startProductRequestError in + guard let self = self else { return } + if let startProductRequestError = startProductRequestError { + let error = FlutterError( + code: "storekit_getproductrequest_platform_error", + message: startProductRequestError.localizedDescription, + details: startProductRequestError.localizedDescription) + completion(nil, error) + return + } + + guard let response = response else { + let error = FlutterError( + code: "storekit_platform_no_response", + message: + "Failed to get SKProductResponse in startRequest call. Error occurred on iOS platform", + details: productIdentifiers) + completion(nil, error) + return + } + + for product in response.products { + self.productsCache[product.productIdentifier] = product + } + + if #available(iOS 12.2, *) { + if let responseMessage = FIAObjectTranslator.convertProductsResponse(toPigeon: response) { + completion(responseMessage, nil) + } + } + self.requestHandlers.remove(handler) + } + } + + public func addPaymentPaymentMap( + _ paymentMap: [String: Any], error: AutoreleasingUnsafeMutablePointer + ) { + guard let productID = paymentMap["productIdentifier"] as? String else { + error.pointee = FlutterError( + code: "storekit_missing_product_identifier", + message: "The `productIdentifier` is missing from the payment map.", + details: paymentMap) + return + } + + guard let product = self.getProduct(productID: productID) else { + error.pointee = FlutterError( + code: "storekit_invalid_payment_object", + message: + "You have requested a payment for an invalid product. Either the `productIdentifier` of the payment is not valid or the product has not been fetched before adding the payment to the payment queue.", + details: paymentMap) + return + } + + let payment = SKMutablePayment(product: product) + payment.applicationUsername = paymentMap["applicationUsername"] as? String + payment.quantity = paymentMap["quantity"] as? Int ?? 1 + payment.simulatesAskToBuyInSandbox = paymentMap["simulatesAskToBuyInSandbox"] as? Bool ?? false + + if #available(iOS 12.2, *) { + if let paymentDiscountMap = paymentMap["paymentDiscount"] as? [String: Any], + !paymentDiscountMap.isEmpty + { + var invalidError: NSString? + if let paymentDiscount = FIAObjectTranslator.getSKPaymentDiscount( + fromMap: paymentDiscountMap, withError: &invalidError) + { + payment.paymentDiscount = paymentDiscount + } else if let invalidError = invalidError { + error.pointee = FlutterError( + code: "storekit_invalid_payment_discount_object", + message: + "You have requested a payment and specified a payment discount with invalid properties. \(invalidError)", + details: paymentMap) + return + } + } + } + + if !getPaymentQueueHandler().add(payment) { + error.pointee = FlutterError( + code: "storekit_duplicate_product_object", + message: + "There is a pending transaction for the same product identifier. Please either wait for it to be finished or finish it manually using `completePurchase` to avoid edge cases.", + details: paymentMap) + } + } + + // TODO(louisehsu): Once tests and pigeon are migrated to Swift, ensure the param type is [String:String] instead of [String:String?] + public func finishTransactionFinishMap( + _ finishMap: [String: Any], error: AutoreleasingUnsafeMutablePointer + ) { + + // TODO(louisehsu): This is a workaround for objc pigeon's NSNull support. Once we move to swift pigeon, this can be removed. + let castedFinishMap: [String: String] = finishMap.compactMapValues { value in + if let _ = value as? NSNull { + return nil + } else if let stringValue = value as? String { + return stringValue + } + fatalError("This dict should only contain either NSNull or String") + } + let productIdentifier = castedFinishMap["productIdentifier"] + let transactionIdentifier = castedFinishMap["transactionIdentifier"] + let pendingTransactions = getPaymentQueueHandler().getUnfinishedTransactions() + + for transaction in pendingTransactions { + // If the user cancels the purchase dialog we won't have a transactionIdentifier. + // So if it is null AND a transaction in the pendingTransactions list has + // also a null transactionIdentifier we check for equal product identifiers. + if transaction.transactionIdentifier == transactionIdentifier + || (transactionIdentifier == nil + && transaction.transactionIdentifier == nil + && transaction.payment.productIdentifier == productIdentifier) + { + getPaymentQueueHandler().finish(transaction) + } + } + } + + public func restoreTransactionsApplicationUserName( + _ applicationUserName: String?, error: AutoreleasingUnsafeMutablePointer + ) { + getPaymentQueueHandler().restoreTransactions(applicationUserName) + } + + public func presentCodeRedemptionSheetWithError( + _ error: AutoreleasingUnsafeMutablePointer + ) { + #if os(iOS) + getPaymentQueueHandler().presentCodeRedemptionSheet() + #endif + } + + public func retrieveReceiptDataWithError( + _ error: AutoreleasingUnsafeMutablePointer + ) -> String? { + var flutterError: FlutterError? = nil + if let receiptData = receiptManager.retrieveReceiptWithError(&flutterError) { + return receiptData + } else { + error.pointee = flutterError + return nil + } + } + + public func refreshReceiptReceiptProperties( + _ receiptProperties: [String: Any]?, completion: @escaping (FlutterError?) -> Void + ) { + let properties = receiptProperties?.compactMapValues { $0 } ?? [:] + let request = getRefreshReceiptRequest(properties: properties.isEmpty ? nil : properties) + let handler = handlerFactory(request) + requestHandlers.insert(handler) + handler.startProductRequest { [weak self] response, error in + if let error = error { + let requestError = FlutterError( + code: "storekit_refreshreceiptrequest_platform_error", + message: error.localizedDescription, + details: error.localizedDescription) + completion(requestError) + return + } + completion(nil) + self?.requestHandlers.remove(handler) + } + } + + public func startObservingPaymentQueueWithError( + _ error: AutoreleasingUnsafeMutablePointer + ) { + getPaymentQueueHandler().startObservingPaymentQueue() + } + + public func stopObservingPaymentQueueWithError( + _ error: AutoreleasingUnsafeMutablePointer + ) { + getPaymentQueueHandler().stopObservingPaymentQueue() + } + + public func registerPaymentQueueDelegateWithError( + _ error: AutoreleasingUnsafeMutablePointer + ) { + #if os(iOS) + if #available(iOS 13.0, *) { + guard let messenger = registrar?.messenger() else { + fatalError("registrar.messenger can not be nil.") + } + paymentQueueDelegateCallbackChannel = FlutterMethodChannel( + name: "plugins.flutter.io/in_app_purchase_payment_queue_delegate", + binaryMessenger: messenger) + + guard let unwrappedChannel = paymentQueueDelegateCallbackChannel else { + fatalError("registrar.messenger can not be nil.") + } + paymentQueueDelegate = FIAPPaymentQueueDelegate( + methodChannel: unwrappedChannel) + + getPaymentQueueHandler().delegate = paymentQueueDelegate as? SKPaymentQueueDelegate + } + #endif + } + + public func removePaymentQueueDelegateWithError( + _ error: AutoreleasingUnsafeMutablePointer + ) { + #if os(iOS) + if #available(iOS 13.0, *) { + paymentQueueDelegateCallbackChannel = nil + getPaymentQueueHandler().delegate = nil + paymentQueueDelegate = nil + } + #endif + } + + public func showPriceConsentIfNeededWithError( + _ error: AutoreleasingUnsafeMutablePointer + ) { + #if os(iOS) + if #available(iOS 13.4, *) { + getPaymentQueueHandler().showPriceConsentIfNeeded() + } + #endif + } + + @objc + public func handleTransactionsUpdated(_ transactions: [SKPaymentTransaction]) { + let translatedTransactions = transactions.map { + FIAObjectTranslator.getMapFrom($0) + } + transactionObserverCallbackChannel?.invokeMethod( + "updatedTransactions", arguments: translatedTransactions) + } + + @objc + public func handleTransactionsRemoved(_ transactions: [SKPaymentTransaction]) { + let translatedTransactions = transactions.map { + FIAObjectTranslator.getMapFrom($0) + } + transactionObserverCallbackChannel?.invokeMethod( + "removedTransactions", arguments: translatedTransactions) + } + + @objc + public func handleTransactionRestoreFailed(_ error: NSError) { + transactionObserverCallbackChannel?.invokeMethod( + "restoreCompletedTransactionsFailed", arguments: FIAObjectTranslator.getMapFrom(error)) + } + + @objc + public func restoreCompletedTransactionsFinished() { + transactionObserverCallbackChannel?.invokeMethod( + "paymentQueueRestoreCompletedTransactionsFinished", arguments: nil) + } + + @objc + public func shouldAddStorePayment(payment: SKPayment, product: SKProduct) -> Bool { + productsCache[product.productIdentifier] = product + transactionObserverCallbackChannel?.invokeMethod( + "shouldAddStorePayment", + arguments: [ + "payment": FIAObjectTranslator.getMapFrom(payment), + "product": FIAObjectTranslator.getMapFrom(product), + ]) + return false + } + + public func updatedDownloads() { + NSLog("Received an updatedDownloads callback, but downloads are not supported.") + } + + // MARK: - Methods exposed for testing + func getProduct(productID: String) -> SKProduct? { + return self.productsCache[productID] as? SKProduct + } + + func getProductRequest(withIdentifiers productIdentifiers: Set) -> SKProductsRequest { + return SKProductsRequest(productIdentifiers: productIdentifiers) + } + + func getRefreshReceiptRequest(properties: [String: Any]?) -> SKReceiptRefreshRequest { + return SKReceiptRefreshRequest(receiptProperties: properties) + } + + // MARK: - Private convenience methods + private func getNonNullValue(from dictionary: [String: Any], forKey key: String) -> Any? { + let value = dictionary[key] + return value is NSNull ? nil : value + } + + private func getPaymentQueueHandler() -> FIAPaymentQueueHandler { + guard let paymentQueueHandler = self.paymentQueueHandler else { + fatalError( + "paymentQueueHandler can't be nil. Please ensure you're using init(registrar: FlutterPluginRegistrar)" + ) + } + return paymentQueueHandler + } +} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit-Bridging-Header.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit-Bridging-Header.h new file mode 100644 index 00000000000..f57780d9516 --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit-Bridging-Header.h @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FIAObjectTranslator.h" +#import "FIAPPaymentQueueDelegate.h" +#import "FIAPReceiptManager.h" +#import "FIAPRequestHandler.h" +#import "FIAPaymentQueueHandler.h" +#import "FIATransactionCache.h" +#import "messages.g.h" diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit.h new file mode 100644 index 00000000000..7abed15966e --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/in_app_purchase_storekit.h @@ -0,0 +1,16 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This has to be here b/c of this cocoapods issue - +// https://github.com/CocoaPods/CocoaPods/issues/3767 +// Without this file, the generated "in_app_purchase_storekit-Swift.h" will keep +// trying to import an "in_app_purchase_storekit.h" which doesn't exist. +#ifndef in_app_purchase_storekit_h +#define in_app_purchase_storekit_h + +#if __has_include("in_app_purchase_storekit-umbrella.h") +#import "in_app_purchase_storekit-umbrella.h" +#endif + +#endif /* in_app_purchase_storekit_h */ diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h index bc315e781cd..79bdffc76af 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.h @@ -265,7 +265,7 @@ NSObject *InAppPurchaseAPIGetCodec(void); - (void)startProductRequestProductIdentifiers:(NSArray *)productIdentifiers completion:(void (^)(SKProductsResponseMessage *_Nullable, FlutterError *_Nullable))completion; -- (void)finishTransactionFinishMap:(NSDictionary *)finishMap +- (void)finishTransactionFinishMap:(NSDictionary *)finishMap error:(FlutterError *_Nullable *_Nonnull)error; - (void)restoreTransactionsApplicationUserName:(nullable NSString *)applicationUserName error:(FlutterError *_Nullable *_Nonnull)error; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m index 9588c883b82..f013f720511 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/messages.g.m @@ -700,7 +700,7 @@ void SetUpInAppPurchaseAPI(id binaryMessenger, api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; - NSDictionary *arg_finishMap = GetNullableObjectAtIndex(args, 0); + NSDictionary *arg_finishMap = GetNullableObjectAtIndex(args, 0); FlutterError *error; [api finishTransactionFinishMap:arg_finishMap error:&error]; callback(wrapResult(nil, error)); diff --git a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit.podspec b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit.podspec index 428c612543e..fed7865a615 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit.podspec +++ b/packages/in_app_purchase/in_app_purchase_storekit/darwin/in_app_purchase_storekit.podspec @@ -16,7 +16,8 @@ Downloaded by pub (not CocoaPods). # TODO(mvanbeusekom): update URL when in_app_purchase_storekit package is published. # Updating it before the package is published will cause a lint error and block the tree. s.documentation_url = 'https://pub.dev/packages/in_app_purchase' - s.source_files = 'Classes/**/*' + s.swift_version = '5.0' + s.source_files = 'Classes/**/*.{h,m,swift}' s.public_header_files = 'Classes/**/*.h' s.ios.dependency 'Flutter' s.osx.dependency 'FlutterMacOS' @@ -24,4 +25,8 @@ Downloaded by pub (not CocoaPods). s.osx.deployment_target = '10.15' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } s.resource_bundles = {'in_app_purchase_storekit_privacy' => ['Resources/PrivacyInfo.xcprivacy']} + s.xcconfig = { + 'LIBRARY_SEARCH_PATHS' => '$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)/ $(SDKROOT)/usr/lib/swift', + 'LD_RUNPATH_SEARCH_PATHS' => '/usr/lib/swift', + } end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj index 06e0b3ac947..5b7ba614979 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/Runner.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; A5279298219369C600FF69E6 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5279297219369C600FF69E6 /* StoreKit.framework */; }; A59001A721E69658004A3E5E /* InAppPurchasePluginTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A59001A621E69658004A3E5E /* InAppPurchasePluginTests.m */; }; + F22BF91C2BC9B40B00713878 /* SwiftStubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = F22BF91B2BC9B40B00713878 /* SwiftStubs.swift */; }; F67646F82681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F67646F72681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m */; }; F6995BDD27CF73000050EA78 /* FIATransactionCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F6995BDC27CF73000050EA78 /* FIATransactionCacheTests.m */; }; F78AF3142342BC89008449C7 /* PaymentQueueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F78AF3132342BC89008449C7 /* PaymentQueueTests.m */; }; @@ -78,6 +79,8 @@ A59001A621E69658004A3E5E /* InAppPurchasePluginTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = InAppPurchasePluginTests.m; sourceTree = ""; }; A59001A821E69658004A3E5E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; E4F9651425A612301059769C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + F22BF91A2BC9B40B00713878 /* RunnerTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RunnerTests-Bridging-Header.h"; sourceTree = ""; }; + F22BF91B2BC9B40B00713878 /* SwiftStubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftStubs.swift; sourceTree = ""; }; F67646F72681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIAPPaymentQueueDeleteTests.m; sourceTree = ""; }; F6995BDC27CF73000050EA78 /* FIATransactionCacheTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIATransactionCacheTests.m; sourceTree = ""; }; F6E5D5F926131C4800C68BED /* Configuration.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = Configuration.storekit; sourceTree = ""; }; @@ -193,6 +196,8 @@ 688DE35021F2A5A100EA2684 /* TranslatorTests.m */, F67646F72681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m */, F6995BDC27CF73000050EA78 /* FIATransactionCacheTests.m */, + F22BF91B2BC9B40B00713878 /* SwiftStubs.swift */, + F22BF91A2BC9B40B00713878 /* RunnerTests-Bridging-Header.h */, ); path = RunnerTests; sourceTree = ""; @@ -271,6 +276,7 @@ }; A59001A321E69658004A3E5E = { CreatedOnToolsVersion = 10.0; + LastSwiftMigration = 1530; ProvisioningStyle = Automatic; TestTargetID = 97C146ED1CF9000F007C117D; }; @@ -331,7 +337,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n"; }; 67CBAA37FA50343E43E988F6 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; @@ -427,6 +433,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F22BF91C2BC9B40B00713878 /* SwiftStubs.swift in Sources */, F78AF3142342BC89008449C7 /* PaymentQueueTests.m in Sources */, F67646F82681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m in Sources */, 6896B34621E9363700D37AEF /* ProductRequestHandlerTests.m in Sources */, @@ -633,6 +640,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; @@ -650,6 +658,9 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "RunnerTests/RunnerTests-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; }; name = Debug; @@ -660,6 +671,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; @@ -676,6 +688,8 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "RunnerTests/RunnerTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; }; name = Release; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/RunnerTests-Bridging-Header.h b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/RunnerTests-Bridging-Header.h new file mode 100644 index 00000000000..f85b226b93f --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/RunnerTests-Bridging-Header.h @@ -0,0 +1,5 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "Stubs.h" diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/SwiftStubs.swift b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/SwiftStubs.swift new file mode 100644 index 00000000000..b61b83b229c --- /dev/null +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/SwiftStubs.swift @@ -0,0 +1,26 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Foundation +import StoreKitTest + +@testable import in_app_purchase_storekit + +class InAppPurchasePluginStub: InAppPurchasePlugin { + override func getProductRequest(withIdentifiers productIdentifiers: Set) + -> SKProductsRequest + { + return SKProductRequestStub.init(productIdentifiers: productIdentifiers) + } + + override func getProduct(productID: String) -> SKProduct? { + if productID == "" { + return nil + } + return SKProductStub.init(productID: productID) + } + override func getRefreshReceiptRequest(properties: [String: Any]?) -> SKReceiptRefreshRequest { + return SKReceiptRefreshRequest(receiptProperties: properties) + } +} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj index 76173704741..e4eb1fae600 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/project.pbxproj @@ -26,8 +26,9 @@ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; - A2C6CD5797E6A6721FDBCA1C /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36DEEA66738F64D983F76848 /* Pods_Runner.framework */; }; - C51E64432925727D7AC7BBFF /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE8A421F08C80BE6E90142D5 /* Pods_RunnerTests.framework */; }; + 45146735C2BCBA6C4526CAA0 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 73F6308C9AC0BA52F286AE52 /* Pods_Runner.framework */; }; + 4B50788839EDAFEFFC4A752E /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A207547725A30AEC178E886 /* Pods_RunnerTests.framework */; }; + F2C3A7412BD9D33D000D35F2 /* Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C3A7402BD9D33D000D35F2 /* Stubs.swift */; }; F79BDC102905FBE300E3999D /* FIAPPaymentQueueDeleteTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F79BDC0F2905FBE300E3999D /* FIAPPaymentQueueDeleteTests.m */; }; F79BDC122905FBF700E3999D /* FIATransactionCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F79BDC112905FBF700E3999D /* FIATransactionCacheTests.m */; }; F79BDC142905FBFE00E3999D /* InAppPurchasePluginTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F79BDC132905FBFE00E3999D /* InAppPurchasePluginTests.m */; }; @@ -68,6 +69,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 1A207547725A30AEC178E886 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -81,16 +83,17 @@ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 36DEEA66738F64D983F76848 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 4E423AE82F466005587C3567 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; - 5E5D46173E3025B0DB32A1BE /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; - 5EBC5A8BA44B08330BA605AB /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; - 62F1680C5AE033907C1DF7AB /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 7172FBE7DF73E41E9FC6E6D7 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 73F6308C9AC0BA52F286AE52 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 79C769808042591E28A245B8 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 89C4EE02AA38CF7BF853991B /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 8D6AA81A407E58E8954F145B /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - 9A4FEABF1DEF0D106FEB7974 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - B6C8FD76BB3278AA51FED870 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - EE8A421F08C80BE6E90142D5 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BEE5894B3A0A82FD8D495BDD /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + DAB7E6DD38EB6FA87E605270 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + F2C3A73F2BD9D33D000D35F2 /* RunnerTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RunnerTests-Bridging-Header.h"; sourceTree = ""; }; + F2C3A7402BD9D33D000D35F2 /* Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stubs.swift; sourceTree = ""; }; F700DD0228E652A10004836B /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; F79BDC0F2905FBE300E3999D /* FIAPPaymentQueueDeleteTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FIAPPaymentQueueDeleteTests.m; path = ../../shared/RunnerTests/FIAPPaymentQueueDeleteTests.m; sourceTree = ""; }; F79BDC112905FBF700E3999D /* FIATransactionCacheTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FIATransactionCacheTests.m; path = ../../shared/RunnerTests/FIATransactionCacheTests.m; sourceTree = ""; }; @@ -108,7 +111,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - A2C6CD5797E6A6721FDBCA1C /* Pods_Runner.framework in Frameworks */, + 45146735C2BCBA6C4526CAA0 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -116,7 +119,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - C51E64432925727D7AC7BBFF /* Pods_RunnerTests.framework in Frameworks */, + 4B50788839EDAFEFFC4A752E /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -126,12 +129,12 @@ 09D47623A8E19B84FF0453EE /* Pods */ = { isa = PBXGroup; children = ( - B6C8FD76BB3278AA51FED870 /* Pods-Runner.debug.xcconfig */, - 9A4FEABF1DEF0D106FEB7974 /* Pods-Runner.release.xcconfig */, - 62F1680C5AE033907C1DF7AB /* Pods-Runner.profile.xcconfig */, - 5E5D46173E3025B0DB32A1BE /* Pods-RunnerTests.debug.xcconfig */, - 5EBC5A8BA44B08330BA605AB /* Pods-RunnerTests.release.xcconfig */, - 4E423AE82F466005587C3567 /* Pods-RunnerTests.profile.xcconfig */, + 8D6AA81A407E58E8954F145B /* Pods-Runner.debug.xcconfig */, + 7172FBE7DF73E41E9FC6E6D7 /* Pods-Runner.release.xcconfig */, + DAB7E6DD38EB6FA87E605270 /* Pods-Runner.profile.xcconfig */, + 89C4EE02AA38CF7BF853991B /* Pods-RunnerTests.debug.xcconfig */, + 79C769808042591E28A245B8 /* Pods-RunnerTests.release.xcconfig */, + BEE5894B3A0A82FD8D495BDD /* Pods-RunnerTests.profile.xcconfig */, ); path = Pods; sourceTree = ""; @@ -153,8 +156,8 @@ 33CEB47122A05771004F2AC0 /* Flutter */, F700DD0328E652A10004836B /* RunnerTests */, 33CC10EE2044A3C60003C045 /* Products */, - D73912EC22F37F3D000D13A0 /* Frameworks */, 09D47623A8E19B84FF0453EE /* Pods */, + E89234C8EA67B35E702D54F6 /* Frameworks */, ); sourceTree = ""; }; @@ -202,11 +205,11 @@ path = Runner; sourceTree = ""; }; - D73912EC22F37F3D000D13A0 /* Frameworks */ = { + E89234C8EA67B35E702D54F6 /* Frameworks */ = { isa = PBXGroup; children = ( - 36DEEA66738F64D983F76848 /* Pods_Runner.framework */, - EE8A421F08C80BE6E90142D5 /* Pods_RunnerTests.framework */, + 73F6308C9AC0BA52F286AE52 /* Pods_Runner.framework */, + 1A207547725A30AEC178E886 /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -223,6 +226,8 @@ F79BDC1D2905FC3900E3999D /* TranslatorTests.m */, F79BDC192905FC1F00E3999D /* ProductRequestHandlerTests.m */, F79BDC112905FBF700E3999D /* FIATransactionCacheTests.m */, + F2C3A7402BD9D33D000D35F2 /* Stubs.swift */, + F2C3A73F2BD9D33D000D35F2 /* RunnerTests-Bridging-Header.h */, ); path = RunnerTests; sourceTree = ""; @@ -234,13 +239,13 @@ isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 377E3E3C5CA24E98C4B6A4BB /* [CP] Check Pods Manifest.lock */, + F83C62E1BF4D0A86747FA7CF /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, - 23A80E9A6DAA80757416464A /* [CP] Embed Pods Frameworks */, + 2C0BDBFDB384E45F10121440 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -256,11 +261,11 @@ isa = PBXNativeTarget; buildConfigurationList = F700DD0B28E652A10004836B /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( - 959FA4942EA5DA018C52D3DA /* [CP] Check Pods Manifest.lock */, + 75DE29BFD3B3C1D676C22160 /* [CP] Check Pods Manifest.lock */, F700DCFE28E652A10004836B /* Sources */, F700DCFF28E652A10004836B /* Frameworks */, F700DD0028E652A10004836B /* Resources */, - 1FAA0D39365CA43DED71E657 /* [CP] Embed Pods Frameworks */, + E318947BE753B7BBEFC3782A /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -279,7 +284,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1400; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { @@ -298,7 +303,7 @@ }; F700DD0128E652A10004836B = { CreatedOnToolsVersion = 14.0.1; - LastSwiftMigration = 1400; + LastSwiftMigration = 1530; TestTargetID = 33CC10EC2044A3C60003C045; }; }; @@ -343,24 +348,7 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 1FAA0D39365CA43DED71E657 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 23A80E9A6DAA80757416464A /* [CP] Embed Pods Frameworks */ = { + 2C0BDBFDB384E45F10121440 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -415,7 +403,7 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; - 377E3E3C5CA24E98C4B6A4BB /* [CP] Check Pods Manifest.lock */ = { + 75DE29BFD3B3C1D676C22160 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -430,14 +418,31 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 959FA4942EA5DA018C52D3DA /* [CP] Check Pods Manifest.lock */ = { + E318947BE753B7BBEFC3782A /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-RunnerTests/Pods-RunnerTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + F83C62E1BF4D0A86747FA7CF /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -452,7 +457,7 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -483,6 +488,7 @@ F79BDC102905FBE300E3999D /* FIAPPaymentQueueDeleteTests.m in Sources */, F79BDC142905FBFE00E3999D /* InAppPurchasePluginTests.m in Sources */, F79BDC122905FBF700E3999D /* FIATransactionCacheTests.m in Sources */, + F2C3A7412BD9D33D000D35F2 /* Stubs.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -746,7 +752,7 @@ }; F700DD0828E652A10004836B /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5E5D46173E3025B0DB32A1BE /* Pods-RunnerTests.debug.xcconfig */; + baseConfigurationReference = 89C4EE02AA38CF7BF853991B /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; @@ -767,17 +773,19 @@ MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS -Xcc -fmodule-map-file=\"${PODS_ROOT}/Headers/Public/in_app_purchase_storekit/in_app_purchase_storekit.modulemap\" -Xcc -fmodule-map-file=\"${PODS_ROOT}/Headers/Public/integration_test/integration_test.modulemap\" -Xcc -fmodule-map-file=\"${PODS_ROOT}/Headers/Public/shared_preferences_macos/shared_preferences_macos.modulemap\""; PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OBJC_BRIDGING_HEADER = "RunnerTests/RunnerTests-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example"; }; name = Debug; }; F700DD0928E652A10004836B /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5EBC5A8BA44B08330BA605AB /* Pods-RunnerTests.release.xcconfig */; + baseConfigurationReference = 79C769808042591E28A245B8 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; @@ -797,17 +805,18 @@ MACOSX_DEPLOYMENT_TARGET = 10.15; MARKETING_VERSION = 1.0; MTL_FAST_MATH = YES; - OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS -Xcc -fmodule-map-file=\"${PODS_ROOT}/Headers/Public/in_app_purchase_storekit/in_app_purchase_storekit.modulemap\" -Xcc -fmodule-map-file=\"${PODS_ROOT}/Headers/Public/integration_test/integration_test.modulemap\" -Xcc -fmodule-map-file=\"${PODS_ROOT}/Headers/Public/shared_preferences_macos/shared_preferences_macos.modulemap\""; PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OBJC_BRIDGING_HEADER = "RunnerTests/RunnerTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example"; }; name = Release; }; F700DD0A28E652A10004836B /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 4E423AE82F466005587C3567 /* Pods-RunnerTests.profile.xcconfig */; + baseConfigurationReference = BEE5894B3A0A82FD8D495BDD /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; @@ -827,10 +836,11 @@ MACOSX_DEPLOYMENT_TARGET = 10.15; MARKETING_VERSION = 1.0; MTL_FAST_MATH = YES; - OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS -Xcc -fmodule-map-file=\"${PODS_ROOT}/Headers/Public/in_app_purchase_storekit/in_app_purchase_storekit.modulemap\" -Xcc -fmodule-map-file=\"${PODS_ROOT}/Headers/Public/integration_test/integration_test.modulemap\" -Xcc -fmodule-map-file=\"${PODS_ROOT}/Headers/Public/shared_preferences_macos/shared_preferences_macos.modulemap\""; PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OBJC_BRIDGING_HEADER = "RunnerTests/RunnerTests-Bridging-Header.h"; + SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example"; }; name = Profile; diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index dd063527309..53f3a37403a 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ ) + -> SKProductsRequest + { + return SKProductRequestStub.init(productIdentifiers: productIdentifiers) + } + + override func getProduct(productID: String) -> SKProduct? { + if productID == "" { + return nil + } + return SKProductStub.init(productID: productID) + } + override func getRefreshReceiptRequest(properties: [String: Any]?) -> SKReceiptRefreshRequest { + return SKReceiptRefreshRequest(receiptProperties: properties) + } +} diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m index 905903df056..8839bd29402 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/InAppPurchasePluginTests.m @@ -5,7 +5,7 @@ #import #import #import "FIAPaymentQueueHandler.h" -#import "InAppPurchasePlugin+TestOnly.h" +#import "RunnerTests-Swift.h" #import "Stubs.h" @import in_app_purchase_storekit; @@ -21,7 +21,11 @@ @implementation InAppPurchasePluginTest - (void)setUp { self.receiptManagerStub = [FIAPReceiptManagerStub new]; - self.plugin = [[InAppPurchasePluginStub alloc] initWithReceiptManager:self.receiptManagerStub]; + self.plugin = [[InAppPurchasePluginStub alloc] + initWithReceiptManager:self.receiptManagerStub + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return [[FIAPRequestHandler alloc] initWithRequest:request]; + }]; } - (void)tearDown { @@ -184,7 +188,7 @@ - (void)testGetProductResponseWithRequestError { id mockHandler = OCMClassMock([FIAPRequestHandler class]); InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] - initWithReceiptManager:nil + initWithReceiptManager:_receiptManagerStub handlerFactory:^FIAPRequestHandler *(SKRequest *request) { return mockHandler; }]; @@ -219,7 +223,7 @@ - (void)testGetProductResponseWithNoResponse { id mockHandler = OCMClassMock([FIAPRequestHandler class]); InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] - initWithReceiptManager:nil + initWithReceiptManager:_receiptManagerStub handlerFactory:^FIAPRequestHandler *(SKRequest *request) { return mockHandler; }]; @@ -475,10 +479,26 @@ - (void)testRetrieveReceiptDataError { - (void)testRefreshReceiptRequest { XCTestExpectation *expectation = [self expectationWithDescription:@"completion handler successfully called"]; - [self.plugin refreshReceiptReceiptProperties:nil - completion:^(FlutterError *_Nullable error) { - [expectation fulfill]; - }]; + + id mockHandler = OCMClassMock([FIAPRequestHandler class]); + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] + initWithReceiptManager:_receiptManagerStub + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return mockHandler; + }]; + + NSError *recieptError = [NSError errorWithDomain:@"errorDomain" + code:0 + userInfo:@{NSLocalizedDescriptionKey : @"description"}]; + + OCMStub([mockHandler + startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], + recieptError, nil])]); + + [plugin refreshReceiptReceiptProperties:nil + completion:^(FlutterError *_Nullable error) { + [expectation fulfill]; + }]; [self waitForExpectations:@[ expectation ] timeout:5]; } @@ -491,10 +511,27 @@ - (void)testRefreshReceiptRequestWithParams { XCTestExpectation *expectation = [self expectationWithDescription:@"completion handler successfully called"]; - [self.plugin refreshReceiptReceiptProperties:properties - completion:^(FlutterError *_Nullable error) { - [expectation fulfill]; - }]; + + id mockHandler = OCMClassMock([FIAPRequestHandler class]); + InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] + initWithReceiptManager:_receiptManagerStub + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return mockHandler; + }]; + + NSError *recieptError = [NSError errorWithDomain:@"errorDomain" + code:0 + userInfo:@{NSLocalizedDescriptionKey : @"description"}]; + + OCMStub([mockHandler + startProductRequestWithCompletionHandler:([OCMArg invokeBlockWithArgs:[NSNull null], + recieptError, nil])]); + + [plugin refreshReceiptReceiptProperties:properties + completion:^(FlutterError *_Nullable error) { + [expectation fulfill]; + }]; + [self waitForExpectations:@[ expectation ] timeout:5]; } @@ -509,7 +546,7 @@ - (void)testRefreshReceiptRequestWithError { id mockHandler = OCMClassMock([FIAPRequestHandler class]); InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] - initWithReceiptManager:nil + initWithReceiptManager:_receiptManagerStub handlerFactory:^FIAPRequestHandler *(SKRequest *request) { return mockHandler; }]; @@ -614,6 +651,14 @@ - (void)testRegisterPaymentQueueDelegate { updatedDownloads:nil transactionCache:OCMClassMock(FIATransactionCache.class)]; + self.plugin.registrar = OCMProtocolMock(@protocol(FlutterPluginRegistrar)); + + id registrarMock = OCMProtocolMock(@protocol(FlutterPluginRegistrar)); + self.plugin.registrar = registrarMock; + + id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); + OCMStub([registrarMock messenger]).andReturn(binaryMessengerMock); + // Verify the delegate is nil before we register one. XCTAssertNil(self.plugin.paymentQueueHandler.delegate); @@ -624,7 +669,6 @@ - (void)testRegisterPaymentQueueDelegate { XCTAssertNotNil(self.plugin.paymentQueueHandler.delegate); } } -#endif - (void)testRemovePaymentQueueDelegate { if (@available(iOS 13, *)) { @@ -649,6 +693,7 @@ - (void)testRemovePaymentQueueDelegate { XCTAssertNil(self.plugin.paymentQueueHandler.delegate); } } +#endif - (void)testHandleTransactionsUpdated { NSDictionary *transactionMap = @{ @@ -661,7 +706,11 @@ - (void)testHandleTransactionsUpdated { @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), }; - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + InAppPurchasePlugin *plugin = [[InAppPurchasePluginStub alloc] + initWithReceiptManager:self.receiptManagerStub + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return [[FIAPRequestHandler alloc] initWithRequest:request]; + }]; FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); plugin.transactionObserverCallbackChannel = mockChannel; OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); @@ -687,7 +736,11 @@ - (void)testHandleTransactionsRemoved { @"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970), }; - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + InAppPurchasePlugin *plugin = [[InAppPurchasePluginStub alloc] + initWithReceiptManager:self.receiptManagerStub + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return [[FIAPRequestHandler alloc] initWithRequest:request]; + }]; FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); plugin.transactionObserverCallbackChannel = mockChannel; OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); @@ -703,19 +756,27 @@ - (void)testHandleTransactionsRemoved { } - (void)testHandleTransactionRestoreFailed { - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + InAppPurchasePlugin *plugin = [[InAppPurchasePluginStub alloc] + initWithReceiptManager:self.receiptManagerStub + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return [[FIAPRequestHandler alloc] initWithRequest:request]; + }]; FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); plugin.transactionObserverCallbackChannel = mockChannel; OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); - NSError *error; + NSError *error = [NSError errorWithDomain:@"error" code:0 userInfo:nil]; [plugin handleTransactionRestoreFailed:error]; OCMVerify(times(1), [mockChannel invokeMethod:@"restoreCompletedTransactionsFailed" arguments:[FIAObjectTranslator getMapFromNSError:error]]); } - (void)testRestoreCompletedTransactionsFinished { - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + InAppPurchasePlugin *plugin = [[InAppPurchasePluginStub alloc] + initWithReceiptManager:self.receiptManagerStub + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return [[FIAPRequestHandler alloc] initWithRequest:request]; + }]; FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); plugin.transactionObserverCallbackChannel = mockChannel; OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); @@ -745,7 +806,11 @@ - (void)testShouldAddStorePayment { SKMutablePayment *payment = [FIAObjectTranslator getSKMutablePaymentFromMap:paymentMap]; SKProductStub *product = [[SKProductStub alloc] initWithMap:productMap]; - InAppPurchasePlugin *plugin = [[InAppPurchasePlugin alloc] initWithReceiptManager:nil]; + InAppPurchasePlugin *plugin = [[InAppPurchasePluginStub alloc] + initWithReceiptManager:self.receiptManagerStub + handlerFactory:^FIAPRequestHandler *(SKRequest *request) { + return [[FIAPRequestHandler alloc] initWithRequest:request]; + }]; FlutterMethodChannel *mockChannel = OCMClassMock([FlutterMethodChannel class]); plugin.transactionObserverCallbackChannel = mockChannel; OCMStub([mockChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); @@ -755,7 +820,7 @@ - (void)testShouldAddStorePayment { @"product" : [FIAObjectTranslator getMapFromSKProduct:product] }; - BOOL result = [plugin shouldAddStorePayment:payment product:product]; + BOOL result = [plugin shouldAddStorePaymentWithPayment:payment product:product]; XCTAssertEqual(result, NO); OCMVerify(times(1), [mockChannel invokeMethod:@"shouldAddStorePayment" arguments:args]); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.h b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.h index 2ef8e23181a..daad506e79c 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.h +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.h @@ -20,6 +20,7 @@ API_AVAILABLE(ios(11.2), macos(10.13.2)) @interface SKProductStub : SKProduct - (instancetype)initWithMap:(NSDictionary *)map; +- (instancetype)initWithProductID:(NSString *)productIdentifier; @end @interface SKProductRequestStub : SKProductsRequest @@ -32,9 +33,6 @@ API_AVAILABLE(ios(11.2), macos(10.13.2)) - (instancetype)initWithMap:(NSDictionary *)map; @end -@interface InAppPurchasePluginStub : InAppPurchasePlugin -@end - @interface SKRequestStub : SKRequest @end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.m b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.m index b4dba710f02..8c8b2973ebc 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.m +++ b/packages/in_app_purchase/in_app_purchase_storekit/example/shared/RunnerTests/Stubs.m @@ -144,28 +144,6 @@ - (instancetype)initWithMap:(NSDictionary *)map { @end -@interface InAppPurchasePluginStub () -@end - -@implementation InAppPurchasePluginStub - -- (SKProductRequestStub *)getProductRequestWithIdentifiers:(NSSet *)identifiers { - return [[SKProductRequestStub alloc] initWithProductIdentifiers:identifiers]; -} - -- (SKProduct *)getProduct:(NSString *)productID { - if ([productID isEqualToString:@""]) { - return nil; - } - return [[SKProductStub alloc] initWithProductID:productID]; -} - -- (SKReceiptRefreshRequestStub *)getRefreshReceiptRequest:(NSDictionary *)properties { - return [[SKReceiptRefreshRequestStub alloc] initWithReceiptProperties:properties]; -} - -@end - @interface SKPaymentQueueStub () @end diff --git a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart index 7ce35bded2b..e136e05b5d1 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/lib/src/messages.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v16.0.4), do not edit directly. +// Autogenerated from Pigeon (v16.0.5), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers @@ -720,7 +720,7 @@ class InAppPurchaseAPI { } } - Future finishTransaction(Map finishMap) async { + Future finishTransaction(Map finishMap) async { const String __pigeon_channelName = 'dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction'; final BasicMessageChannel __pigeon_channel = diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart index fe1042c8037..6351c889cc3 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/pigeons/messages.dart @@ -238,7 +238,7 @@ abstract class InAppPurchaseAPI { SKProductsResponseMessage startProductRequest( List productIdentifiers); - void finishTransaction(Map finishMap); + void finishTransaction(Map finishMap); void restoreTransactions(String? applicationUserName); diff --git a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml index a2b005787b3..b960bebd082 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml +++ b/packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml @@ -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.15 +version: 0.3.16 environment: sdk: ^3.2.3 diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart index f5a755d83b5..082d62939d9 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/fakes/fake_storekit_platform.dart @@ -185,9 +185,10 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { } @override - void finishTransaction(Map finishMap) { + void finishTransaction(Map finishMap) { finishedTransactions.add(createPurchasedTransaction( - finishMap['productIdentifier']!, finishMap['transactionIdentifier']!, + finishMap['productIdentifier']! as String, + finishMap['transactionIdentifier']! as String, quantity: transactionList.first.payment.quantity)); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart index 82775f6b2e2..f7fe5283d94 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_methodchannel_apis_test.dart @@ -239,7 +239,7 @@ class FakeStoreKitPlatform implements TestInAppPurchaseApi { [dummyTransactionMessage]; @override - void finishTransaction(Map finishMap) { + void finishTransaction(Map finishMap) { transactionsFinished.add(Map.from(finishMap)); } diff --git a/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart b/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart index 65cd6e0bcc2..6b0c9a77ab4 100644 --- a/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart +++ b/packages/in_app_purchase/in_app_purchase_storekit/test/test_api.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v16.0.4), do not edit directly. +// Autogenerated from Pigeon (v16.0.5), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import, no_leading_underscores_for_local_identifiers // ignore_for_file: avoid_relative_lib_imports @@ -99,7 +99,7 @@ abstract class TestInAppPurchaseApi { Future startProductRequest( List productIdentifiers); - void finishTransaction(Map finishMap); + void finishTransaction(Map finishMap); void restoreTransactions(String? applicationUserName); @@ -278,10 +278,10 @@ abstract class TestInAppPurchaseApi { assert(message != null, 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction was null.'); final List args = (message as List?)!; - final Map? arg_finishMap = - (args[0] as Map?)?.cast(); + final Map? arg_finishMap = + (args[0] as Map?)?.cast(); assert(arg_finishMap != null, - 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction was null, expected non-null Map.'); + 'Argument for dev.flutter.pigeon.in_app_purchase_storekit.InAppPurchaseAPI.finishTransaction was null, expected non-null Map.'); try { api.finishTransaction(arg_finishMap!); return wrapResponse(empty: true);