Skip to content

Commit

Permalink
[in_app_purchase_storekit] Add identifier, type fields to SKProductDi…
Browse files Browse the repository at this point in the history
…scountWrapper (flutter#6205)
  • Loading branch information
Chris Yang committed Aug 10, 2022
1 parent 64ce54d commit 6c9738e
Show file tree
Hide file tree
Showing 11 changed files with 148 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.3.2

* Adds the `identifier` and `type` fields to the `SKProductDiscountWrapper` to reflect the changes in the [SKProductDiscount](https://developer.apple.com/documentation/storekit/skproductdiscount?language=objc) in iOS 12.2.

## 0.3.1+1

* Fixes avoid_redundant_argument_values lint warnings and minor typos.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ - (instancetype)initWithMap:(NSDictionary *)map {
[[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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
@interface TranslatorTest : XCTestCase

@property(strong, nonatomic) NSDictionary *periodMap;
@property(strong, nonatomic) NSDictionary *discountMap;
@property(strong, nonatomic) NSMutableDictionary *discountMap;
@property(strong, nonatomic) NSMutableDictionary *discountMissingIdentifierMap;
@property(strong, nonatomic) NSMutableDictionary *productMap;
@property(strong, nonatomic) NSDictionary *productResponseMap;
@property(strong, nonatomic) NSDictionary *paymentMap;
Expand All @@ -27,13 +28,27 @@ @implementation TranslatorTest

- (void)setUp {
self.periodMap = @{@"numberOfUnits" : @(0), @"unit" : @(0)};
self.discountMap = @{

self.discountMap = [[NSMutableDictionary alloc] initWithDictionary:@{
@"price" : @"1",
@"priceLocale" : [FIAObjectTranslator getMapFromNSLocale:NSLocale.systemLocale],
@"numberOfPeriods" : @1,
@"subscriptionPeriod" : self.periodMap,
@"paymentMode" : @1
};
@"paymentMode" : @1,
}];
if (@available(iOS 12.2, *)) {
self.discountMap[@"identifier"] = @"test offer id";
self.discountMap[@"type"] = @(SKProductDiscountTypeIntroductory);
}
self.discountMissingIdentifierMap = [[NSMutableDictionary alloc] initWithDictionary:@{
@"price" : @"1",
@"priceLocale" : [FIAObjectTranslator getMapFromNSLocale:NSLocale.systemLocale],
@"numberOfPeriods" : @1,
@"subscriptionPeriod" : self.periodMap,
@"paymentMode" : @1,
@"identifier" : [NSNull null],
@"type" : @0,
}];

self.productMap = [[NSMutableDictionary alloc] initWithDictionary:@{
@"price" : @"1",
Expand Down Expand Up @@ -274,6 +289,15 @@ - (void)testSKPaymentDiscountFromMapMissingIdentifier {
}
}

- (void)testGetMapFromSKProductDiscountMissingIdentifier {
if (@available(iOS 12.2, *)) {
SKProductDiscountStub *discount =
[[SKProductDiscountStub alloc] initWithMap:self.discountMissingIdentifierMap];
NSDictionary *map = [FIAObjectTranslator getMapFromSKProductDiscount:discount];
XCTAssertEqualObjects(map, self.discountMissingIdentifierMap);
}
}

- (void)testSKPaymentDiscountFromMapMissingKeyIdentifier {
if (@available(iOS 12.2, *)) {
NSArray *invalidValues = @[ [NSNull null], @(1), @"" ];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,12 @@ + (NSDictionary *)getMapFromSKProductDiscount:(SKProductDiscount *)discount {
@"subscriptionPeriod" :
[FIAObjectTranslator getMapFromSKProductSubscriptionPeriod:discount.subscriptionPeriod]
?: [NSNull null],
@"paymentMode" : @(discount.paymentMode)
@"paymentMode" : @(discount.paymentMode),
}];
if (@available(iOS 12.2, *)) {
[map setObject:discount.identifier ?: [NSNull null] forKey:@"identifier"];
[map setObject:@(discount.type) forKey:@"type"];
}

// TODO(cyanglaz): NSLocale is a complex object, want to see the actual need of getting this
// expanded to a map. Matching android to only get the currencySymbol for now.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,27 @@ class _SerializedEnums {
late SKSubscriptionPeriodUnit unit;
late SKProductDiscountPaymentMode discountPaymentMode;
}

/// Serializer for [SKProductDiscountType].
///
/// Use these in `@JsonSerializable()` classes by annotating them with
/// `@SKProductDiscountTypeConverter()`.
class SKProductDiscountTypeConverter
implements JsonConverter<SKProductDiscountType, int?> {
/// Default const constructor.
const SKProductDiscountTypeConverter();

@override
SKProductDiscountType fromJson(int? json) {
if (json == null) {
return SKProductDiscountType.introductory;
}
return $enumDecode<SKProductDiscountType, dynamic>(
_$SKProductDiscountTypeEnumMap.cast<SKProductDiscountType, dynamic>(),
json);
}

@override
int toJson(SKProductDiscountType object) =>
_$SKProductDiscountTypeEnumMap[object]!;
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,24 @@ enum SKProductDiscountPaymentMode {
unspecified,
}

/// Dart wrapper around StoreKit's [SKProductDiscountType]
/// (https://developer.apple.com/documentation/storekit/skproductdiscounttype?language=objc)
///
/// This is used as a property in the [SKProductDiscountWrapper].
/// The values of the enum options are matching the [SKProductDiscountType]'s
/// values.
///
/// Values representing the types of discount offers an app can present.
enum SKProductDiscountType {
/// A constant indicating the discount type is an introductory offer.
@JsonValue(0)
introductory,

/// A constant indicating the discount type is a promotional offer.
@JsonValue(1)
subscription,
}

/// Dart wrapper around StoreKit's [SKProductDiscount](https://developer.apple.com/documentation/storekit/skproductdiscount?language=objc).
///
/// It is used as a property in [SKProductWrapper].
Expand All @@ -182,7 +200,9 @@ class SKProductDiscountWrapper {
required this.priceLocale,
required this.numberOfPeriods,
required this.paymentMode,
required this.subscriptionPeriod});
required this.subscriptionPeriod,
required this.identifier,
required this.type});

/// Constructing an instance from a map from the Objective-C layer.
///
Expand Down Expand Up @@ -214,6 +234,16 @@ class SKProductDiscountWrapper {
/// and their units and duration do not have to be matched.
final SKProductSubscriptionPeriodWrapper subscriptionPeriod;

/// A string used to uniquely identify a discount offer for a product.
///
/// You set up offers and their identifiers in App Store Connect.
@JsonKey(defaultValue: null)
final String? identifier;

/// Values representing the types of discount offers an app can present.
@SKProductDiscountTypeConverter()
final SKProductDiscountType type;

@override
bool operator ==(Object other) {
if (identical(other, this)) {
Expand All @@ -227,12 +257,14 @@ class SKProductDiscountWrapper {
other.priceLocale == priceLocale &&
other.numberOfPeriods == numberOfPeriods &&
other.paymentMode == paymentMode &&
other.subscriptionPeriod == subscriptionPeriod;
other.subscriptionPeriod == subscriptionPeriod &&
other.identifier == identifier &&
other.type == type;
}

@override
int get hashCode => Object.hash(
price, priceLocale, numberOfPeriods, paymentMode, subscriptionPeriod);
int get hashCode => Object.hash(price, priceLocale, numberOfPeriods,
paymentMode, subscriptionPeriod, identifier, type);
}

/// Dart wrapper around StoreKit's [SKProduct](https://developer.apple.com/documentation/storekit/skproduct?language=objc).
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: in_app_purchase_storekit
description: An implementation for the iOS platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework.
repository: https://github.com/flutter/plugins/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.1+1
version: 0.3.2

environment:
sdk: ">=2.14.0 <3.0.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ void main() {
expect(wrapper, equals(dummyDiscount));
});

test(
'SKProductDiscountWrapper missing identifier and type should have '
'property values consistent with map', () {
final SKProductDiscountWrapper wrapper =
SKProductDiscountWrapper.fromJson(
buildDiscountMapMissingIdentifierAndType(
dummyDiscountMissingIdentifierAndType));
expect(wrapper, equals(dummyDiscountMissingIdentifierAndType));
});

test(
'SKProductDiscountWrapper should have properties to be default if map is empty',
() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,19 @@ final SKProductDiscountWrapper dummyDiscount = SKProductDiscountWrapper(
numberOfPeriods: 1,
paymentMode: SKProductDiscountPaymentMode.payUpFront,
subscriptionPeriod: dummySubscription,
identifier: 'id',
type: SKProductDiscountType.subscription,
);

final SKProductDiscountWrapper dummyDiscountMissingIdentifierAndType =
SKProductDiscountWrapper(
price: '1.0',
priceLocale: dollarLocale,
numberOfPeriods: 1,
paymentMode: SKProductDiscountPaymentMode.payUpFront,
subscriptionPeriod: dummySubscription,
identifier: null,
type: SKProductDiscountType.introductory,
);

final SKProductWrapper dummyProductWrapper = SKProductWrapper(
Expand Down Expand Up @@ -106,6 +119,21 @@ Map<String, dynamic> buildDiscountMap(SKProductDiscountWrapper discount) {
SKProductDiscountPaymentMode.values.indexOf(discount.paymentMode),
'subscriptionPeriod':
buildSubscriptionPeriodMap(discount.subscriptionPeriod),
'identifier': discount.identifier,
'type': SKProductDiscountType.values.indexOf(discount.type)
};
}

Map<String, dynamic> buildDiscountMapMissingIdentifierAndType(
SKProductDiscountWrapper discount) {
return <String, dynamic>{
'price': discount.price,
'priceLocale': buildLocaleMap(discount.priceLocale),
'numberOfPeriods': discount.numberOfPeriods,
'paymentMode':
SKProductDiscountPaymentMode.values.indexOf(discount.paymentMode),
'subscriptionPeriod':
buildSubscriptionPeriodMap(discount.subscriptionPeriod)
};
}

Expand Down

0 comments on commit 6c9738e

Please sign in to comment.