blob: 6c7d246cfede242258f04e8931f7fd515ee22c2d [file] [log] [blame]
// 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"
#import <Foundation/Foundation.h>
#import <StoreKit/StoreKit.h>
#if TARGET_OS_OSX
#import <FlutterMacOS/FlutterMacOS.h>
#else
#import <Flutter/Flutter.h>
#endif
@implementation SKProductSubscriptionPeriodStub
- (instancetype)initWithMap:(NSDictionary *)map {
self = [super init];
if (self) {
[self setValue:map[@"numberOfUnits"] ?: @(0) forKey:@"numberOfUnits"];
[self setValue:map[@"unit"] ?: @(0) forKey:@"unit"];
}
return self;
}
@end
@implementation SKProductDiscountStub
- (instancetype)initWithMap:(NSDictionary *)map {
self = [super init];
if (self) {
[self setValue:[[NSDecimalNumber alloc] initWithString:map[@"price"]] ?: [NSNull null]
forKey:@"price"];
NSLocale *locale = NSLocale.systemLocale;
[self setValue:locale ?: [NSNull null] forKey:@"priceLocale"];
[self setValue:map[@"numberOfPeriods"] ?: @(0) forKey:@"numberOfPeriods"];
SKProductSubscriptionPeriodStub *subscriptionPeriodSub =
[[SKProductSubscriptionPeriodStub alloc] initWithMap:map[@"subscriptionPeriod"]];
[self setValue:subscriptionPeriodSub forKey:@"subscriptionPeriod"];
[self setValue:map[@"paymentMode"] ?: @(0) forKey:@"paymentMode"];
if (@available(iOS 12.2, *)) {
[self setValue:map[@"identifier"] ?: [NSNull null] forKey:@"identifier"];
[self setValue:map[@"type"] ?: @(0) forKey:@"type"];
}
}
return self;
}
@end
@implementation SKProductStub
- (instancetype)initWithMap:(NSDictionary *)map {
self = [super init];
if (self) {
[self setValue:map[@"productIdentifier"] ?: [NSNull null] forKey:@"productIdentifier"];
[self setValue:map[@"localizedDescription"] ?: [NSNull null] forKey:@"localizedDescription"];
[self setValue:map[@"localizedTitle"] ?: [NSNull null] forKey:@"localizedTitle"];
[self setValue:map[@"downloadable"] ?: @NO forKey:@"downloadable"];
[self setValue:[[NSDecimalNumber alloc] initWithString:map[@"price"]] ?: [NSNull null]
forKey:@"price"];
NSLocale *locale = NSLocale.systemLocale;
[self setValue:locale ?: [NSNull null] forKey:@"priceLocale"];
[self setValue:map[@"downloadContentLengths"] ?: @(0) forKey:@"downloadContentLengths"];
SKProductSubscriptionPeriodStub *period =
[[SKProductSubscriptionPeriodStub alloc] initWithMap:map[@"subscriptionPeriod"]];
[self setValue:period ?: [NSNull null] forKey:@"subscriptionPeriod"];
SKProductDiscountStub *discount =
[[SKProductDiscountStub alloc] initWithMap:map[@"introductoryPrice"]];
[self setValue:discount ?: [NSNull null] forKey:@"introductoryPrice"];
[self setValue:map[@"subscriptionGroupIdentifier"] ?: [NSNull null]
forKey:@"subscriptionGroupIdentifier"];
if (@available(iOS 12.2, *)) {
NSMutableArray *discounts = [[NSMutableArray alloc] init];
for (NSDictionary *discountMap in map[@"discounts"]) {
[discounts addObject:[[SKProductDiscountStub alloc] initWithMap:discountMap]];
}
[self setValue:discounts forKey:@"discounts"];
}
}
return self;
}
- (instancetype)initWithProductID:(NSString *)productIdentifier {
self = [super init];
if (self) {
[self setValue:productIdentifier forKey:@"productIdentifier"];
}
return self;
}
@end
@interface SKProductRequestStub ()
@property(nonatomic, strong) NSSet *identifers;
@property(nonatomic, strong) NSError *error;
@end
@implementation SKProductRequestStub
- (instancetype)initWithProductIdentifiers:(NSSet<NSString *> *)productIdentifiers {
self = [super initWithProductIdentifiers:productIdentifiers];
self.identifers = productIdentifiers;
return self;
}
- (instancetype)initWithFailureError:(NSError *)error {
self = [super init];
self.error = error;
return self;
}
- (void)start {
NSMutableArray *productArray = [NSMutableArray new];
for (NSString *identifier in self.identifers) {
[productArray addObject:@{@"productIdentifier" : identifier}];
}
SKProductsResponseStub *response;
if (self.returnError) {
response = nil;
} else {
response = [[SKProductsResponseStub alloc] initWithMap:@{@"products" : productArray}];
}
if (self.error) {
[self.delegate request:self didFailWithError:self.error];
} else {
[self.delegate productsRequest:self didReceiveResponse:response];
}
}
@end
@implementation SKProductsResponseStub
- (instancetype)initWithMap:(NSDictionary *)map {
self = [super init];
if (self) {
NSMutableArray *products = [NSMutableArray new];
for (NSDictionary *productMap in map[@"products"]) {
SKProductStub *product = [[SKProductStub alloc] initWithMap:productMap];
[products addObject:product];
}
[self setValue:products forKey:@"products"];
}
return self;
}
@end
@interface SKPaymentQueueStub ()
@end
@implementation SKPaymentQueueStub
- (void)addTransactionObserver:(id<SKPaymentTransactionObserver>)observer {
self.observer = observer;
}
- (void)removeTransactionObserver:(id<SKPaymentTransactionObserver>)observer {
self.observer = nil;
}
- (void)addPayment:(SKPayment *)payment {
SKPaymentTransactionStub *transaction =
[[SKPaymentTransactionStub alloc] initWithState:self.testState payment:payment];
[self.observer paymentQueue:self updatedTransactions:@[ transaction ]];
}
- (void)restoreCompletedTransactions {
if ([self.observer
respondsToSelector:@selector(paymentQueueRestoreCompletedTransactionsFinished:)]) {
[self.observer paymentQueueRestoreCompletedTransactionsFinished:self];
}
}
- (void)finishTransaction:(SKPaymentTransaction *)transaction {
if ([self.observer respondsToSelector:@selector(paymentQueue:removedTransactions:)]) {
[self.observer paymentQueue:self removedTransactions:@[ transaction ]];
}
}
@end
@implementation SKPaymentTransactionStub {
SKPayment *_payment;
}
- (instancetype)initWithID:(NSString *)identifier {
self = [super init];
if (self) {
[self setValue:identifier forKey:@"transactionIdentifier"];
}
return self;
}
- (instancetype)initWithMap:(NSDictionary *)map {
self = [super init];
if (self) {
[self setValue:map[@"transactionIdentifier"] forKey:@"transactionIdentifier"];
[self setValue:map[@"transactionState"] forKey:@"transactionState"];
if (![map[@"originalTransaction"] isKindOfClass:[NSNull class]] &&
map[@"originalTransaction"]) {
[self setValue:[[SKPaymentTransactionStub alloc] initWithMap:map[@"originalTransaction"]]
forKey:@"originalTransaction"];
}
[self setValue:map[@"error"] ? [[NSErrorStub alloc] initWithMap:map[@"error"]] : [NSNull null]
forKey:@"error"];
[self setValue:[NSDate dateWithTimeIntervalSince1970:[map[@"transactionTimeStamp"] doubleValue]]
forKey:@"transactionDate"];
}
return self;
}
- (instancetype)initWithState:(SKPaymentTransactionState)state {
self = [super init];
if (self) {
// Only purchased and restored transactions have transactionIdentifier:
// https://developer.apple.com/documentation/storekit/skpaymenttransaction/1411288-transactionidentifier?language=objc
if (state == SKPaymentTransactionStatePurchased || state == SKPaymentTransactionStateRestored) {
[self setValue:@"fakeID" forKey:@"transactionIdentifier"];
}
[self setValue:@(state) forKey:@"transactionState"];
}
return self;
}
- (instancetype)initWithState:(SKPaymentTransactionState)state payment:(SKPayment *)payment {
self = [super init];
if (self) {
// Only purchased and restored transactions have transactionIdentifier:
// https://developer.apple.com/documentation/storekit/skpaymenttransaction/1411288-transactionidentifier?language=objc
if (state == SKPaymentTransactionStatePurchased || state == SKPaymentTransactionStateRestored) {
[self setValue:@"fakeID" forKey:@"transactionIdentifier"];
}
[self setValue:@(state) forKey:@"transactionState"];
_payment = payment;
}
return self;
}
- (SKPayment *)payment {
return _payment;
}
@end
@implementation NSErrorStub
- (instancetype)initWithMap:(NSDictionary *)map {
return [self initWithDomain:[map objectForKey:@"domain"]
code:[[map objectForKey:@"code"] integerValue]
userInfo:[map objectForKey:@"userInfo"]];
}
@end
@implementation FIAPReceiptManagerStub : FIAPReceiptManager
- (NSData *)getReceiptData:(NSURL *)url error:(NSError **)error {
if (self.returnError) {
*error = [NSError errorWithDomain:@"test"
code:1
userInfo:@{
@"name" : @"test",
@"houseNr" : @5,
@"error" : [[NSError alloc] initWithDomain:@"internalTestDomain"
code:99
userInfo:nil]
}];
return nil;
}
NSString *originalString = [NSString stringWithFormat:@"test"];
return [[NSData alloc] initWithBase64EncodedString:originalString options:kNilOptions];
}
- (NSURL *)receiptURL {
if (self.returnNilURL) {
return nil;
} else {
return [[NSBundle mainBundle] appStoreReceiptURL];
}
}
@end
@implementation SKReceiptRefreshRequestStub {
NSError *_error;
}
- (instancetype)initWithReceiptProperties:(NSDictionary<NSString *, id> *)properties {
self = [super initWithReceiptProperties:properties];
return self;
}
- (instancetype)initWithFailureError:(NSError *)error {
self = [super init];
_error = error;
return self;
}
- (void)start {
if (_error) {
[self.delegate request:self didFailWithError:_error];
} else {
[self.delegate requestDidFinish:self];
}
}
@end
@implementation SKStorefrontStub
- (instancetype)initWithMap:(NSDictionary *)map {
self = [super init];
if (self) {
// Set stub values
[self setValue:map[@"countryCode"] forKey:@"countryCode"];
[self setValue:map[@"identifier"] forKey:@"identifier"];
}
return self;
}
@end
@implementation PaymentQueueStub
@synthesize transactions;
@synthesize delegate;
- (void)finishTransaction:(SKPaymentTransaction *)transaction {
[self.observer paymentQueue:self.realQueue removedTransactions:@[ transaction ]];
}
- (void)addPayment:(SKPayment *_Nonnull)payment {
SKPaymentTransactionStub *transaction =
[[SKPaymentTransactionStub alloc] initWithState:self.testState payment:payment];
[self.observer paymentQueue:self.realQueue updatedTransactions:@[ transaction ]];
}
- (void)addTransactionObserver:(nonnull id<SKPaymentTransactionObserver>)observer {
self.observer = observer;
}
- (void)restoreCompletedTransactions {
[self.observer paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)self];
}
- (void)restoreCompletedTransactionsWithApplicationUsername:(nullable NSString *)username {
[self.observer paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)self];
}
- (NSArray<SKPaymentTransaction *> *_Nonnull)getUnfinishedTransactions {
if (self.getUnfinishedTransactionsStub) {
return self.getUnfinishedTransactionsStub();
} else {
return @[];
}
}
#if TARGET_OS_IOS
- (void)presentCodeRedemptionSheet {
if (self.presentCodeRedemptionSheetStub) {
self.presentCodeRedemptionSheetStub();
}
}
#endif
#if TARGET_OS_IOS
- (void)showPriceConsentIfNeeded {
if (self.showPriceConsentIfNeededStub) {
self.showPriceConsentIfNeededStub();
}
}
#endif
- (void)restoreTransactions:(nullable NSString *)applicationName {
if (self.restoreTransactionsStub) {
self.restoreTransactionsStub(applicationName);
}
}
- (void)startObservingPaymentQueue {
if (self.startObservingPaymentQueueStub) {
self.startObservingPaymentQueueStub();
}
}
- (void)stopObservingPaymentQueue {
if (self.stopObservingPaymentQueueStub) {
self.stopObservingPaymentQueueStub();
}
}
- (void)removeTransactionObserver:(id<SKPaymentTransactionObserver>)observer {
self.observer = nil;
}
@end
@implementation MethodChannelStub
- (void)invokeMethod:(nonnull NSString *)method arguments:(id _Nullable)arguments {
if (self.invokeMethodChannelStub) {
self.invokeMethodChannelStub(method, arguments);
}
}
- (void)invokeMethod:(nonnull NSString *)method
arguments:(id _Nullable)arguments
result:(FlutterResult _Nullable)callback {
if (self.invokeMethodChannelWithResultsStub) {
self.invokeMethodChannelWithResultsStub(method, arguments, callback);
}
}
@end
@implementation TransactionCacheStub
- (void)addObjects:(nonnull NSArray *)objects forKey:(TransactionCacheKey)key {
if (self.addObjectsStub) {
self.addObjectsStub(objects, key);
}
}
- (void)clear {
if (self.clearStub) {
self.clearStub();
}
}
- (nonnull NSArray *)getObjectsForKey:(TransactionCacheKey)key {
if (self.getObjectsForKeyStub) {
return self.getObjectsForKeyStub(key);
}
return @[];
}
@end
@implementation PaymentQueueHandlerStub
@synthesize storefront;
@synthesize delegate;
- (void)paymentQueue:(nonnull SKPaymentQueue *)queue
updatedTransactions:(nonnull NSArray<SKPaymentTransaction *> *)transactions {
if (self.paymentQueueUpdatedTransactionsStub) {
self.paymentQueueUpdatedTransactionsStub(queue, transactions);
}
}
#if TARGET_OS_IOS
- (void)showPriceConsentIfNeeded {
if (self.showPriceConsentIfNeededStub) {
self.showPriceConsentIfNeededStub();
}
}
#endif
- (BOOL)addPayment:(nonnull SKPayment *)payment {
if (self.addPaymentStub) {
return self.addPaymentStub(payment);
} else {
return NO;
}
}
- (void)finishTransaction:(nonnull SKPaymentTransaction *)transaction {
if (self.finishTransactionStub) {
self.finishTransactionStub(transaction);
}
}
- (nonnull NSArray<SKPaymentTransaction *> *)getUnfinishedTransactions {
if (self.getUnfinishedTransactionsStub) {
return self.getUnfinishedTransactionsStub();
} else {
return @[];
}
}
- (nonnull instancetype)initWithQueue:(nonnull id<FLTPaymentQueueProtocol>)queue
transactionsUpdated:(nullable TransactionsUpdated)transactionsUpdated
transactionRemoved:(nullable TransactionsRemoved)transactionsRemoved
restoreTransactionFailed:(nullable RestoreTransactionFailed)restoreTransactionFailed
restoreCompletedTransactionsFinished:
(nullable RestoreCompletedTransactionsFinished)restoreCompletedTransactionsFinished
shouldAddStorePayment:(nullable ShouldAddStorePayment)shouldAddStorePayment
updatedDownloads:(nullable UpdatedDownloads)updatedDownloads
transactionCache:(nonnull id<FLTTransactionCacheProtocol>)transactionCache {
return [[PaymentQueueHandlerStub alloc] init];
}
#if TARGET_OS_IOS
- (void)presentCodeRedemptionSheet {
if (self.presentCodeRedemptionSheetStub) {
self.presentCodeRedemptionSheetStub();
}
}
#endif
- (void)restoreTransactions:(nullable NSString *)applicationName {
if (self.restoreTransactions) {
self.restoreTransactions(applicationName);
}
}
- (void)startObservingPaymentQueue {
if (self.startObservingPaymentQueueStub) {
self.startObservingPaymentQueueStub();
}
}
- (void)stopObservingPaymentQueue {
if (self.stopObservingPaymentQueueStub) {
self.stopObservingPaymentQueueStub();
}
}
- (nonnull instancetype)initWithQueue:(nonnull id<FLTPaymentQueueProtocol>)queue
transactionsUpdated:(nullable TransactionsUpdated)transactionsUpdated
transactionRemoved:(nullable TransactionsRemoved)transactionsRemoved
restoreTransactionFailed:(nullable RestoreTransactionFailed)restoreTransactionFailed
restoreCompletedTransactionsFinished:
(nullable RestoreCompletedTransactionsFinished)restoreCompletedTransactionsFinished
shouldAddStorePayment:(nullable ShouldAddStorePayment)shouldAddStorePayment
updatedDownloads:(nullable UpdatedDownloads)updatedDownloads {
return [[PaymentQueueHandlerStub alloc] init];
}
@end
@implementation RequestHandlerStub
- (void)startProductRequestWithCompletionHandler:(nonnull ProductRequestCompletion)completion {
if (self.startProductRequestWithCompletionHandlerStub) {
self.startProductRequestWithCompletionHandlerStub(completion);
}
}
@end
/// This mock is only used in iOS tests
#if TARGET_OS_IOS
// This FlutterPluginRegistrar is a protocol, so to make a stub it has to be implemented.
@implementation FlutterPluginRegistrarStub
- (void)addApplicationDelegate:(nonnull NSObject<FlutterPlugin> *)delegate {
if (self.addApplicationDelegateStub) {
self.addApplicationDelegateStub(delegate);
}
}
- (void)addMethodCallDelegate:(nonnull NSObject<FlutterPlugin> *)delegate
channel:(nonnull FlutterMethodChannel *)channel {
if (self.addMethodCallDelegateStub) {
self.addMethodCallDelegateStub(delegate, channel);
}
}
- (nonnull NSString *)lookupKeyForAsset:(nonnull NSString *)asset {
if (self.lookupKeyForAssetStub) {
return self.lookupKeyForAssetStub(asset);
}
return nil;
}
- (nonnull NSString *)lookupKeyForAsset:(nonnull NSString *)asset
fromPackage:(nonnull NSString *)package {
if (self.lookupKeyForAssetFromPackageStub) {
return self.lookupKeyForAssetFromPackageStub(asset, package);
}
return nil;
}
- (nonnull NSObject<FlutterBinaryMessenger> *)messenger {
if (self.messengerStub) {
return self.messengerStub();
}
return [[FlutterBinaryMessengerStub alloc] init]; // Or default behavior
}
- (void)publish:(nonnull NSObject *)value {
if (self.publishStub) {
self.publishStub(value);
}
}
- (void)registerViewFactory:(nonnull NSObject<FlutterPlatformViewFactory> *)factory
withId:(nonnull NSString *)factoryId {
if (self.registerViewFactoryStub) {
self.registerViewFactoryStub(factory, factoryId);
}
}
- (nonnull NSObject<FlutterTextureRegistry> *)textures {
if (self.texturesStub) {
return self.texturesStub();
}
return nil;
}
- (void)registerViewFactory:(nonnull NSObject<FlutterPlatformViewFactory> *)factory
withId:(nonnull NSString *)factoryId
gestureRecognizersBlockingPolicy:
(FlutterPlatformViewGestureRecognizersBlockingPolicy)gestureRecognizersBlockingPolicy {
if (self.registerViewFactoryWithGestureRecognizersBlockingPolicyStub) {
self.registerViewFactoryWithGestureRecognizersBlockingPolicyStub(
factory, factoryId, gestureRecognizersBlockingPolicy);
}
}
@end
// This FlutterBinaryMessenger is a protocol, so to make a stub it has to be implemented.
@implementation FlutterBinaryMessengerStub
- (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection {
if (self.cleanUpConnectionStub) {
self.cleanUpConnectionStub(connection);
}
}
- (void)sendOnChannel:(nonnull NSString *)channel message:(NSData *_Nullable)message {
if (self.sendOnChannelMessageStub) {
self.sendOnChannelMessageStub(channel, message);
}
}
- (void)sendOnChannel:(nonnull NSString *)channel
message:(NSData *_Nullable)message
binaryReply:(FlutterBinaryReply _Nullable)callback {
if (self.sendOnChannelMessageBinaryReplyStub) {
self.sendOnChannelMessageBinaryReplyStub(channel, message, callback);
}
}
- (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(nonnull NSString *)channel
binaryMessageHandler:
(FlutterBinaryMessageHandler _Nullable)handler {
if (self.setMessageHandlerOnChannelBinaryMessageHandlerStub) {
return self.setMessageHandlerOnChannelBinaryMessageHandlerStub(channel, handler);
}
return 0;
}
@end
#endif