Skip to content

Commit

Permalink
Migrate RCPurchaseOwnershipType (#594)
Browse files Browse the repository at this point in the history
* Add APITester target

- Added an `APITester` target that relies on the project's public API
- Added build phase run step that builds the `APITester` after the `Purchases` framework
- If the `APITester` target fails, that means we changed the public API

* Update project.pbxproj

Circular dependencies... let's just do a new circle CI step for the target.

* Update dependency

Forgot to update this dependency 🤦‍♂️

* Migrate the logger

Resolves #559

* Fix build

* Don't try and verify bitcode for the APITester project

* Add a swift file

Because apparently this is a thing.

* Update TODOs and messaging to make intentions more clear

* Add APITester target

- Added an `APITester` target that relies on the project's public API
- Added build phase run step that builds the `APITester` after the `Purchases` framework
- If the `APITester` target fails, that means we changed the public API

* Update project.pbxproj

Circular dependencies... let's just do a new circle CI step for the target.

* Initial stuff

* Rebased

* address comments that aren't being addressed in another PR
  • Loading branch information
taquitos authored Jun 25, 2021
1 parent 49acaa2 commit a6564db
Show file tree
Hide file tree
Showing 10 changed files with 148 additions and 104 deletions.
1 change: 1 addition & 0 deletions PublicSDKAPITester/RevenueCatAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface RevenueCatAPI<RCPurchasesDelegate> : NSObject

+ (void)allTheThings;
+ (void)enums;

@end

Expand Down
28 changes: 25 additions & 3 deletions PublicSDKAPITester/RevenueCatAPI.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
// Created by Joshua Liebowitz on 6/18/21.
//

#import <Purchases/RCEntitlementInfo.h>
#import <Purchases/RCPurchaserInfo.h>
#import <Purchases/RCPurchases.h>
#import "RevenueCatAPI.h"
#import "Purchases.h"
#import "RCPurchases.h"

@import StoreKit;
@import PurchasesCoreSwift;
@import StoreKit;

@implementation RevenueCatAPI

Expand Down Expand Up @@ -118,4 +119,25 @@ + (void)allTheThings {
[p.delegate purchases:p shouldPurchasePromoProduct:skp defermentBlock:^(RCPurchaseCompletedBlock makeDeferredPurchase) {}];
}

+ (void)enums {
RCPeriodType t = RCNormal;
t = RCIntro;
t = RCTrial;

RCPurchaseOwnershipType o = RCPurchaseOwnershipTypePurchased;
o = RCPurchaseOwnershipTypeFamilyShared;
o = RCPurchaseOwnershipTypeUnknown;

RCStore rs = RCAppStore;
rs = RCMacAppStore;
rs = RCPlayStore;
rs = RCStripe;
rs = RCPromotional;
rs = RCUnknownStore;

RCPeriodType pr = RCIntro;
pr = RCTrial;
pr = RCNormal;
}

@end
54 changes: 31 additions & 23 deletions Purchases.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
2D5033262406E4E8009CAE61 /* RCSpecialSubscriberAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D5033232406E4E8009CAE61 /* RCSpecialSubscriberAttributes.h */; settings = {ATTRIBUTES = (Private, ); }; };
2D50332A2406EA61009CAE61 /* RCSubscriberAttributesManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D5033272406EA61009CAE61 /* RCSubscriberAttributesManager.h */; settings = {ATTRIBUTES = (Private, ); }; };
2D50332B2406EA61009CAE61 /* RCSubscriberAttributesManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D5033282406EA61009CAE61 /* RCSubscriberAttributesManager.m */; };
2D540CD72601509000A7475D /* RCPurchaseOwnershipType.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D540CD62601509000A7475D /* RCPurchaseOwnershipType.h */; settings = {ATTRIBUTES = (Public, ); }; };
2D8DB34B24072AAE00BE3D31 /* SubscriberAttributeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D8DB34A24072AAE00BE3D31 /* SubscriberAttributeTests.swift */; };
2D8E9D482523747D00AFEE11 /* NSDictionaryExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DD269162522A20A006AC4BC /* NSDictionaryExtensionsTests.swift */; };
2D8E9D54252374A600AFEE11 /* NSDictionary+RCExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D8E9D52252374A600AFEE11 /* NSDictionary+RCExtensions.h */; settings = {ATTRIBUTES = (Private, ); }; };
Expand Down Expand Up @@ -133,7 +132,6 @@
37E351F90612047842AFF1A6 /* ProductInfoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E350E57B0A393455A72B40 /* ProductInfoTests.swift */; };
37E352039F075299CD4CF6B0 /* RCHTTPRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 37E35B76B22E9086A5BABECB /* RCHTTPRequest.h */; settings = {ATTRIBUTES = (Private, ); }; };
37E352279076F9A5A48D1460 /* RCAttributionTypeFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = 37E3580957BA17D5C2EEF8A8 /* RCAttributionTypeFactory.m */; };
37E352351CF73E8F4C23D522 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E3564466002C9162AC7C5E /* Package.swift */; };
37E3524628A1D7568679FEE2 /* SusbcriberAttributesManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E3567E972B9B04FE079ABA /* SusbcriberAttributesManagerTests.swift */; };
37E3524CB70618E6C5F3DB49 /* MockPurchasesDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E35838A7FD36982EE14100 /* MockPurchasesDelegate.swift */; };
37E352897F7CB3A122F9739F /* PurchasesSubscriberAttributesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E3508E52201122137D4B4A /* PurchasesSubscriberAttributesTests.swift */; };
Expand Down Expand Up @@ -244,6 +242,8 @@
B387F47A2683FDEA0028701F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B387F46D2683FDAC0028701F /* main.m */; };
B387F47B2683FDEA0028701F /* RevenueCatAPI.h in Sources */ = {isa = PBXBuildFile; fileRef = B387F46C2683FDAC0028701F /* RevenueCatAPI.h */; };
B387F47C2683FDEA0028701F /* RevenueCatAPI.m in Sources */ = {isa = PBXBuildFile; fileRef = B387F46E2683FDAC0028701F /* RevenueCatAPI.m */; };
B3D3C4722685784800CB3C21 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3D3C4712685784800CB3C21 /* Package.swift */; };
B387F480268400EE0028701F /* Purchases.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 352629FE1F7C4B9100C04F2C /* Purchases.framework */; };
B3D3C47026856AFF00CB3C21 /* APITester.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3D3C46F26856AFF00CB3C21 /* APITester.swift */; };
B3D5CFBD267282630056FA67 /* Nimble in Frameworks */ = {isa = PBXBuildFile; productRef = B3D5CFBC267282630056FA67 /* Nimble */; };
B3D5CFC0267282760056FA67 /* OHHTTPStubsSwift in Frameworks */ = {isa = PBXBuildFile; productRef = B3D5CFBF267282760056FA67 /* OHHTTPStubsSwift */; };
Expand All @@ -252,6 +252,8 @@
B3D5CFC62672827D0056FA67 /* OHHTTPStubs in Frameworks */ = {isa = PBXBuildFile; productRef = B3D5CFC52672827D0056FA67 /* OHHTTPStubs */; };
B3D5CFC82672827D0056FA67 /* OHHTTPStubsSwift in Frameworks */ = {isa = PBXBuildFile; productRef = B3D5CFC72672827D0056FA67 /* OHHTTPStubsSwift */; };
B3D5CFCA267282860056FA67 /* Nimble in Frameworks */ = {isa = PBXBuildFile; productRef = B3D5CFC9267282860056FA67 /* Nimble */; };
B3DDB55926854865008CCF23 /* PurchaseOwnershipType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3DDB55826854865008CCF23 /* PurchaseOwnershipType.swift */; };
B3DDB55C26854A1E008CCF23 /* EntitlementInfoEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3DDB55B26854A1E008CCF23 /* EntitlementInfoEnums.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -327,7 +329,6 @@
2D5033232406E4E8009CAE61 /* RCSpecialSubscriberAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RCSpecialSubscriberAttributes.h; path = Purchases/SubscriberAttributes/RCSpecialSubscriberAttributes.h; sourceTree = SOURCE_ROOT; };
2D5033272406EA61009CAE61 /* RCSubscriberAttributesManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RCSubscriberAttributesManager.h; path = Purchases/SubscriberAttributes/RCSubscriberAttributesManager.h; sourceTree = SOURCE_ROOT; };
2D5033282406EA61009CAE61 /* RCSubscriberAttributesManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RCSubscriberAttributesManager.m; path = Purchases/SubscriberAttributes/RCSubscriberAttributesManager.m; sourceTree = SOURCE_ROOT; };
2D540CD62601509000A7475D /* RCPurchaseOwnershipType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCPurchaseOwnershipType.h; sourceTree = "<group>"; };
2D5BB46A24C8E8ED00E27537 /* ReceiptParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReceiptParser.swift; sourceTree = "<group>"; };
2D604CA224E5BF37004821DC /* RCTransaction.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTransaction.h; sourceTree = "<group>"; };
2D8DB34A24072AAE00BE3D31 /* SubscriberAttributeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriberAttributeTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -572,6 +573,9 @@
B387F46E2683FDAC0028701F /* RevenueCatAPI.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RevenueCatAPI.m; sourceTree = "<group>"; };
B387F4732683FDDB0028701F /* APITester */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = APITester; sourceTree = BUILT_PRODUCTS_DIR; };
B387F4752683FDDB0028701F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
B3D3C4712685784800CB3C21 /* Package.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
B3DDB55826854865008CCF23 /* PurchaseOwnershipType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PurchaseOwnershipType.swift; sourceTree = "<group>"; };
B3DDB55B26854A1E008CCF23 /* EntitlementInfoEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntitlementInfoEnums.swift; sourceTree = "<group>"; };
B3D3C46E26856AFE00CB3C21 /* APITester-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "APITester-Bridging-Header.h"; sourceTree = "<group>"; };
B3D3C46F26856AFF00CB3C21 /* APITester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APITester.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
Expand Down Expand Up @@ -637,6 +641,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
B387F486268405F10028701F /* Purchases.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -704,18 +709,10 @@
path = SubscriberAttributes;
sourceTree = "<group>";
};
2D638627267D2E3A0057E024 /* Public */ = {
isa = PBXGroup;
children = (
37E3564466002C9162AC7C5E /* Package.swift */,
);
path = Public;
sourceTree = "<group>";
};
2DC5621724EC63420031F69B /* PurchasesCoreSwift */ = {
isa = PBXGroup;
children = (
2D638627267D2E3A0057E024 /* Public */,
B3DDB55726854850008CCF23 /* Public */,
2D11F5DE250FF63E005A70E8 /* Logging */,
2DDA3E4524DB0B4500EDFE5B /* Misc */,
354235D524C11138008C84EE /* Purchasing */,
Expand All @@ -724,7 +721,6 @@
2DC5621824EC63430031F69B /* PurchasesCoreSwift.h */,
2DC5621924EC63430031F69B /* Info.plist */,
2D97458E24BDFCEF006245E9 /* IntroEligibilityCalculator.swift */,
37E353219039E44092EED299 /* Caching */,
);
path = PurchasesCoreSwift;
sourceTree = "<group>";
Expand All @@ -746,6 +742,7 @@
2DCB85BF2406EC3F003C1260 /* Recovered References */ = {
isa = PBXGroup;
children = (
37E3564466002C9162AC7C5E /* Package.swift */,
);
name = "Recovered References";
sourceTree = "<group>";
Expand Down Expand Up @@ -896,7 +893,6 @@
35B54E4B22EA6FD3005918B1 /* RCEntitlementInfos.m */,
2D604CA224E5BF37004821DC /* RCTransaction.h */,
37E357903E469CF42667760C /* RCAttributionNetwork.h */,
2D540CD62601509000A7475D /* RCPurchaseOwnershipType.h */,
2D4C02E326697E490038F877 /* RCLogLevel.h */,
);
path = Public;
Expand Down Expand Up @@ -1093,13 +1089,6 @@
path = Attribution;
sourceTree = "<group>";
};
37E353219039E44092EED299 /* Caching */ = {
isa = PBXGroup;
children = (
);
path = Caching;
sourceTree = "<group>";
};
37E353592BA71F362DD61153 /* FoundationExtensions */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -1331,6 +1320,24 @@
path = APITester;
sourceTree = "<group>";
};
B3DDB55726854850008CCF23 /* Public */ = {
isa = PBXGroup;
children = (
B3DDB55A26854A0A008CCF23 /* ReOrgLater */,
B3D3C4712685784800CB3C21 /* Package.swift */,
B3DDB55826854865008CCF23 /* PurchaseOwnershipType.swift */,
);
path = Public;
sourceTree = "<group>";
};
B3DDB55A26854A0A008CCF23 /* ReOrgLater */ = {
isa = PBXGroup;
children = (
B3DDB55B26854A1E008CCF23 /* EntitlementInfoEnums.swift */,
);
path = ReOrgLater;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXHeadersBuildPhase section */
Expand Down Expand Up @@ -1388,7 +1395,6 @@
2DC5623824EC7DF70031F69B /* RCTransaction.h in Headers */,
37E354AFE06A9723230E47B8 /* RCInMemoryCachedObject+Protected.h in Headers */,
37E35C7C3BADD6D1BBAE7129 /* RCSubscriberAttribute+Protected.h in Headers */,
2D540CD72601509000A7475D /* RCPurchaseOwnershipType.h in Headers */,
2DAF814E25B24243002C621E /* RCIdentityManager+Protected.h in Headers */,
37E358D3F3C7C0388FF5C2BD /* RCOfferingsFactory.h in Headers */,
37E35DB1E991055497D18044 /* RCPromotionalOffer.h in Headers */,
Expand Down Expand Up @@ -1711,14 +1717,17 @@
2DC5623024EC63730031F69B /* OperationDispatcher.swift in Sources */,
9A65E03625918B0500DE00B0 /* ConfigureStrings.swift in Sources */,
2D11F5E1250FF886005A70E8 /* AttributionStrings.swift in Sources */,
B3DDB55C26854A1E008CCF23 /* EntitlementInfoEnums.swift in Sources */,
2DDF41B524F6F387005BC22D /* AppleReceiptBuilder.swift in Sources */,
2DDF41A324F6F331005BC22D /* ReceiptParser.swift in Sources */,
2DDF41BA24F6F392005BC22D /* ISO3601DateFormatter.swift in Sources */,
B3D3C4722685784800CB3C21 /* Package.swift in Sources */,
2DDF41AE24F6F37C005BC22D /* InAppPurchase.swift in Sources */,
2DC19195255F36D10039389A /* Logger.swift in Sources */,
2DDF419F24F6F331005BC22D /* ReceiptParsingError.swift in Sources */,
2DDF419D24F6F331005BC22D /* IntroEligibilityCalculator.swift in Sources */,
9A65E0802591977900DE00B0 /* ReceiptStrings.swift in Sources */,
B3DDB55926854865008CCF23 /* PurchaseOwnershipType.swift in Sources */,
2DC5623224EC63730031F69B /* TransactionsFactory.swift in Sources */,
2DDF41B424F6F387005BC22D /* ASN1ContainerBuilder.swift in Sources */,
2DDF41AC24F6F37C005BC22D /* ASN1Container.swift in Sources */,
Expand All @@ -1735,7 +1744,6 @@
2DDF41B324F6F387005BC22D /* InAppPurchaseBuilder.swift in Sources */,
9A65DFDE258AD60A00DE00B0 /* LogIntent.swift in Sources */,
37E35C8515C5E2D01B0AF5C1 /* Strings.swift in Sources */,
37E352351CF73E8F4C23D522 /* Package.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
40 changes: 6 additions & 34 deletions Purchases/Public/RCEntitlementInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,9 @@
//

#import <Foundation/Foundation.h>
#import "RCPurchaseOwnershipType.h"

NS_ASSUME_NONNULL_BEGIN

/**
Enum of supported stores
*/
typedef NS_ENUM(NSInteger, RCStore) {
/// For entitlements granted via Apple App Store.
RCAppStore = 0,
/// For entitlements granted via Apple Mac App Store.
RCMacAppStore,
/// For entitlements granted via Google Play Store.
RCPlayStore,
/// For entitlements granted via Stripe.
RCStripe,
/// For entitlements granted via a promo in RevenueCat.
RCPromotional,
/// For entitlements granted via an unknown store.
RCUnknownStore,
} NS_SWIFT_NAME(Purchases.Store);

/**
Enum of supported period types for an entitlement.
*/
typedef NS_ENUM(NSInteger, RCPeriodType) {
/// If the entitlement is not under an introductory or trial period.
RCNormal = 0,
/// If the entitlement is under a introductory price period.
RCIntro,
/// If the entitlement is under a trial period.
RCTrial,
} NS_SWIFT_NAME(Purchases.PeriodType);

/**
The EntitlementInfo object gives you access to all of the information about the status of a user entitlement.
*/
Expand Down Expand Up @@ -65,7 +34,7 @@ NS_SWIFT_NAME(Purchases.EntitlementInfo)
The last period type this entitlement was in
Either: RCNormal, RCIntro, RCTrial
*/
@property (readonly) RCPeriodType periodType;
@property (readonly) enum RCPeriodType periodType;

/**
The latest purchase or renewal date for the entitlement.
Expand All @@ -87,7 +56,7 @@ NS_SWIFT_NAME(Purchases.EntitlementInfo)
The store where this entitlement was unlocked from
Either: RCAppStore, RCMacAppStore, RCPlayStore, RCStripe, RCPromotional, RCUnknownStore
*/
@property (readonly) RCStore store;
@property (readonly) enum RCStore store;

/**
The product identifier that unlocked this entitlement
Expand Down Expand Up @@ -120,8 +89,11 @@ NS_SWIFT_NAME(Purchases.EntitlementInfo)
or shared to them by a family member. This can be useful for onboarding users who have had
an entitlement shared with them, but might not be entirely aware of the benefits they now have.
*/
@property (readonly) RCPurchaseOwnershipType ownershipType;
@property (readonly) enum RCPurchaseOwnershipType ownershipType;

@end

NS_ASSUME_NONNULL_END



2 changes: 2 additions & 0 deletions Purchases/Public/RCEntitlementInfo.m
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// Copyright © 2019 RevenueCat. All rights reserved.
//

@import PurchasesCoreSwift;

#import "RCEntitlementInfo.h"
#import "RCEntitlementInfo+Protected.h"

Expand Down
22 changes: 0 additions & 22 deletions Purchases/Public/RCPurchaseOwnershipType.h

This file was deleted.

22 changes: 22 additions & 0 deletions PurchasesCoreSwift/Public/PurchaseOwnershipType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// PurchaseOwnershipType.swift
// PurchasesCoreSwift
//
// Created by Joshua Liebowitz on 6/24/21.
// Copyright © 2021 Purchases. All rights reserved.
//

import Foundation

@objc(RCPurchaseOwnershipType) public enum PurchaseOwnershipType: Int {
/**
The purchase was made directly by this user.
*/
case purchased = 0
/**
The purchase has been shared to this user by a family member.
*/
case familyShared = 1

case unknown = 2
}
39 changes: 39 additions & 0 deletions PurchasesCoreSwift/Public/ReOrgLater/EntitlementInfoEnums.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// EntitlementInfoEnums.swift
// PurchasesCoreSwift
//
// Created by Joshua Liebowitz on 6/24/21.
// Copyright © 2021 Purchases. All rights reserved.
//

import Foundation

/**
Enum of supported stores
*/
@objc(RCStore) public enum Store: Int {
/// For entitlements granted via Apple App Store.
@objc(RCAppStore) case appStore = 0
/// For entitlements granted via Apple Mac App Store.
@objc(RCMacAppStore) case macAppStore = 1
/// For entitlements granted via Google Play Store.
@objc(RCPlayStore) case playStore = 2
/// For entitlements granted via Stripe.
@objc(RCStripe) case stripe = 3
/// For entitlements granted via a promo in RevenueCat.
@objc(RCPromotional) case promotional = 4
/// For entitlements granted via an unknown store.
@objc(RCUnknownStore) case unknownStore = 5
}

/**
Enum of supported period types for an entitlement.
*/
@objc(RCPeriodType) public enum PeriodType: Int {
/// If the entitlement is not under an introductory or trial period.
@objc(RCNormal) case normal = 0
/// If the entitlement is under a introductory price period.
@objc(RCIntro) case intro = 1
/// If the entitlement is under a trial period.
@objc(RCTrial) case trial = 2
}
Loading

0 comments on commit a6564db

Please sign in to comment.