Skip to content

Commit 6c9738e

Browse files
author
Chris Yang
authored
[in_app_purchase_storekit] Add identifier, type fields to SKProductDiscountWrapper (flutter#6205)
1 parent 64ce54d commit 6c9738e

File tree

11 files changed

+148
-10
lines changed

11 files changed

+148
-10
lines changed

packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.3.2
2+
3+
* 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.
4+
15
## 0.3.1+1
26

37
* Fixes avoid_redundant_argument_values lint warnings and minor typos.

packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/Stubs.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ - (instancetype)initWithMap:(NSDictionary *)map {
3131
[[SKProductSubscriptionPeriodStub alloc] initWithMap:map[@"subscriptionPeriod"]];
3232
[self setValue:subscriptionPeriodSub forKey:@"subscriptionPeriod"];
3333
[self setValue:map[@"paymentMode"] ?: @(0) forKey:@"paymentMode"];
34+
if (@available(iOS 12.2, *)) {
35+
[self setValue:map[@"identifier"] ?: [NSNull null] forKey:@"identifier"];
36+
[self setValue:map[@"type"] ?: @(0) forKey:@"type"];
37+
}
3438
}
3539
return self;
3640
}

packages/in_app_purchase/in_app_purchase_storekit/example/ios/RunnerTests/TranslatorTests.m

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
@interface TranslatorTest : XCTestCase
1111

1212
@property(strong, nonatomic) NSDictionary *periodMap;
13-
@property(strong, nonatomic) NSDictionary *discountMap;
13+
@property(strong, nonatomic) NSMutableDictionary *discountMap;
14+
@property(strong, nonatomic) NSMutableDictionary *discountMissingIdentifierMap;
1415
@property(strong, nonatomic) NSMutableDictionary *productMap;
1516
@property(strong, nonatomic) NSDictionary *productResponseMap;
1617
@property(strong, nonatomic) NSDictionary *paymentMap;
@@ -27,13 +28,27 @@ @implementation TranslatorTest
2728

2829
- (void)setUp {
2930
self.periodMap = @{@"numberOfUnits" : @(0), @"unit" : @(0)};
30-
self.discountMap = @{
31+
32+
self.discountMap = [[NSMutableDictionary alloc] initWithDictionary:@{
3133
@"price" : @"1",
3234
@"priceLocale" : [FIAObjectTranslator getMapFromNSLocale:NSLocale.systemLocale],
3335
@"numberOfPeriods" : @1,
3436
@"subscriptionPeriod" : self.periodMap,
35-
@"paymentMode" : @1
36-
};
37+
@"paymentMode" : @1,
38+
}];
39+
if (@available(iOS 12.2, *)) {
40+
self.discountMap[@"identifier"] = @"test offer id";
41+
self.discountMap[@"type"] = @(SKProductDiscountTypeIntroductory);
42+
}
43+
self.discountMissingIdentifierMap = [[NSMutableDictionary alloc] initWithDictionary:@{
44+
@"price" : @"1",
45+
@"priceLocale" : [FIAObjectTranslator getMapFromNSLocale:NSLocale.systemLocale],
46+
@"numberOfPeriods" : @1,
47+
@"subscriptionPeriod" : self.periodMap,
48+
@"paymentMode" : @1,
49+
@"identifier" : [NSNull null],
50+
@"type" : @0,
51+
}];
3752

3853
self.productMap = [[NSMutableDictionary alloc] initWithDictionary:@{
3954
@"price" : @"1",
@@ -274,6 +289,15 @@ - (void)testSKPaymentDiscountFromMapMissingIdentifier {
274289
}
275290
}
276291

292+
- (void)testGetMapFromSKProductDiscountMissingIdentifier {
293+
if (@available(iOS 12.2, *)) {
294+
SKProductDiscountStub *discount =
295+
[[SKProductDiscountStub alloc] initWithMap:self.discountMissingIdentifierMap];
296+
NSDictionary *map = [FIAObjectTranslator getMapFromSKProductDiscount:discount];
297+
XCTAssertEqualObjects(map, self.discountMissingIdentifierMap);
298+
}
299+
}
300+
277301
- (void)testSKPaymentDiscountFromMapMissingKeyIdentifier {
278302
if (@available(iOS 12.2, *)) {
279303
NSArray *invalidValues = @[ [NSNull null], @(1), @"" ];

packages/in_app_purchase/in_app_purchase_storekit/ios/Classes/FIAObjectTranslator.m

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,12 @@ + (NSDictionary *)getMapFromSKProductDiscount:(SKProductDiscount *)discount {
7474
@"subscriptionPeriod" :
7575
[FIAObjectTranslator getMapFromSKProductSubscriptionPeriod:discount.subscriptionPeriod]
7676
?: [NSNull null],
77-
@"paymentMode" : @(discount.paymentMode)
77+
@"paymentMode" : @(discount.paymentMode),
7878
}];
79+
if (@available(iOS 12.2, *)) {
80+
[map setObject:discount.identifier ?: [NSNull null] forKey:@"identifier"];
81+
[map setObject:@(discount.type) forKey:@"type"];
82+
}
7983

8084
// TODO(cyanglaz): NSLocale is a complex object, want to see the actual need of getting this
8185
// expanded to a map. Matching android to only get the currencySymbol for now.

packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/enum_converters.dart

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,27 @@ class _SerializedEnums {
117117
late SKSubscriptionPeriodUnit unit;
118118
late SKProductDiscountPaymentMode discountPaymentMode;
119119
}
120+
121+
/// Serializer for [SKProductDiscountType].
122+
///
123+
/// Use these in `@JsonSerializable()` classes by annotating them with
124+
/// `@SKProductDiscountTypeConverter()`.
125+
class SKProductDiscountTypeConverter
126+
implements JsonConverter<SKProductDiscountType, int?> {
127+
/// Default const constructor.
128+
const SKProductDiscountTypeConverter();
129+
130+
@override
131+
SKProductDiscountType fromJson(int? json) {
132+
if (json == null) {
133+
return SKProductDiscountType.introductory;
134+
}
135+
return $enumDecode<SKProductDiscountType, dynamic>(
136+
_$SKProductDiscountTypeEnumMap.cast<SKProductDiscountType, dynamic>(),
137+
json);
138+
}
139+
140+
@override
141+
int toJson(SKProductDiscountType object) =>
142+
_$SKProductDiscountTypeEnumMap[object]!;
143+
}

packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/enum_converters.g.dart

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.dart

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,24 @@ enum SKProductDiscountPaymentMode {
167167
unspecified,
168168
}
169169

170+
/// Dart wrapper around StoreKit's [SKProductDiscountType]
171+
/// (https://developer.apple.com/documentation/storekit/skproductdiscounttype?language=objc)
172+
///
173+
/// This is used as a property in the [SKProductDiscountWrapper].
174+
/// The values of the enum options are matching the [SKProductDiscountType]'s
175+
/// values.
176+
///
177+
/// Values representing the types of discount offers an app can present.
178+
enum SKProductDiscountType {
179+
/// A constant indicating the discount type is an introductory offer.
180+
@JsonValue(0)
181+
introductory,
182+
183+
/// A constant indicating the discount type is a promotional offer.
184+
@JsonValue(1)
185+
subscription,
186+
}
187+
170188
/// Dart wrapper around StoreKit's [SKProductDiscount](https://developer.apple.com/documentation/storekit/skproductdiscount?language=objc).
171189
///
172190
/// It is used as a property in [SKProductWrapper].
@@ -182,7 +200,9 @@ class SKProductDiscountWrapper {
182200
required this.priceLocale,
183201
required this.numberOfPeriods,
184202
required this.paymentMode,
185-
required this.subscriptionPeriod});
203+
required this.subscriptionPeriod,
204+
required this.identifier,
205+
required this.type});
186206

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

237+
/// A string used to uniquely identify a discount offer for a product.
238+
///
239+
/// You set up offers and their identifiers in App Store Connect.
240+
@JsonKey(defaultValue: null)
241+
final String? identifier;
242+
243+
/// Values representing the types of discount offers an app can present.
244+
@SKProductDiscountTypeConverter()
245+
final SKProductDiscountType type;
246+
217247
@override
218248
bool operator ==(Object other) {
219249
if (identical(other, this)) {
@@ -227,12 +257,14 @@ class SKProductDiscountWrapper {
227257
other.priceLocale == priceLocale &&
228258
other.numberOfPeriods == numberOfPeriods &&
229259
other.paymentMode == paymentMode &&
230-
other.subscriptionPeriod == subscriptionPeriod;
260+
other.subscriptionPeriod == subscriptionPeriod &&
261+
other.identifier == identifier &&
262+
other.type == type;
231263
}
232264

233265
@override
234-
int get hashCode => Object.hash(
235-
price, priceLocale, numberOfPeriods, paymentMode, subscriptionPeriod);
266+
int get hashCode => Object.hash(price, priceLocale, numberOfPeriods,
267+
paymentMode, subscriptionPeriod, identifier, type);
236268
}
237269

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

packages/in_app_purchase/in_app_purchase_storekit/lib/src/store_kit_wrappers/sk_product_wrapper.g.dart

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/in_app_purchase/in_app_purchase_storekit/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: in_app_purchase_storekit
22
description: An implementation for the iOS platform of the Flutter `in_app_purchase` plugin. This uses the StoreKit Framework.
33
repository: https://github.com/flutter/plugins/tree/main/packages/in_app_purchase/in_app_purchase_storekit
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22
5-
version: 0.3.1+1
5+
version: 0.3.2
66

77
environment:
88
sdk: ">=2.14.0 <3.0.0"

packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_product_test.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,16 @@ void main() {
3838
expect(wrapper, equals(dummyDiscount));
3939
});
4040

41+
test(
42+
'SKProductDiscountWrapper missing identifier and type should have '
43+
'property values consistent with map', () {
44+
final SKProductDiscountWrapper wrapper =
45+
SKProductDiscountWrapper.fromJson(
46+
buildDiscountMapMissingIdentifierAndType(
47+
dummyDiscountMissingIdentifierAndType));
48+
expect(wrapper, equals(dummyDiscountMissingIdentifierAndType));
49+
});
50+
4151
test(
4252
'SKProductDiscountWrapper should have properties to be default if map is empty',
4353
() {

packages/in_app_purchase/in_app_purchase_storekit/test/store_kit_wrappers/sk_test_stub_objects.dart

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,19 @@ final SKProductDiscountWrapper dummyDiscount = SKProductDiscountWrapper(
5858
numberOfPeriods: 1,
5959
paymentMode: SKProductDiscountPaymentMode.payUpFront,
6060
subscriptionPeriod: dummySubscription,
61+
identifier: 'id',
62+
type: SKProductDiscountType.subscription,
63+
);
64+
65+
final SKProductDiscountWrapper dummyDiscountMissingIdentifierAndType =
66+
SKProductDiscountWrapper(
67+
price: '1.0',
68+
priceLocale: dollarLocale,
69+
numberOfPeriods: 1,
70+
paymentMode: SKProductDiscountPaymentMode.payUpFront,
71+
subscriptionPeriod: dummySubscription,
72+
identifier: null,
73+
type: SKProductDiscountType.introductory,
6174
);
6275

6376
final SKProductWrapper dummyProductWrapper = SKProductWrapper(
@@ -106,6 +119,21 @@ Map<String, dynamic> buildDiscountMap(SKProductDiscountWrapper discount) {
106119
SKProductDiscountPaymentMode.values.indexOf(discount.paymentMode),
107120
'subscriptionPeriod':
108121
buildSubscriptionPeriodMap(discount.subscriptionPeriod),
122+
'identifier': discount.identifier,
123+
'type': SKProductDiscountType.values.indexOf(discount.type)
124+
};
125+
}
126+
127+
Map<String, dynamic> buildDiscountMapMissingIdentifierAndType(
128+
SKProductDiscountWrapper discount) {
129+
return <String, dynamic>{
130+
'price': discount.price,
131+
'priceLocale': buildLocaleMap(discount.priceLocale),
132+
'numberOfPeriods': discount.numberOfPeriods,
133+
'paymentMode':
134+
SKProductDiscountPaymentMode.values.indexOf(discount.paymentMode),
135+
'subscriptionPeriod':
136+
buildSubscriptionPeriodMap(discount.subscriptionPeriod)
109137
};
110138
}
111139

0 commit comments

Comments
 (0)