Skip to content

Commit

Permalink
Merge branch 'release/3.6.0' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
aboedo committed Sep 14, 2020
2 parents 2315b08 + 340f57b commit 46dac33
Show file tree
Hide file tree
Showing 26 changed files with 344 additions and 119 deletions.
4 changes: 2 additions & 2 deletions .jazzy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ objc: true
sdk: iphonesimulator
module: Purchases
umbrella_header: Purchases/Public/Purchases.h
module_version: 3.6.0-beta-2
module_version: 3.6.0
github_url: https://github.com/revenuecat/purchases-ios
github_file_prefix: https://github.com/revenuecat/purchases-ios/tree/3.6.0-beta-2
github_file_prefix: https://github.com/revenuecat/purchases-ios/tree/3.6.0
output: docs
# Leaving this commented out. We used to specify this before, but now it's working without it
# xcodebuild_arguments: [--objc,Purchases/Public/Purchases.h,--,-x,objective-c,-isysroot,$(xcrun --show-sdk-path),-I,$(pwd)]
8 changes: 5 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## 3.6.0-beta-2
## 3.6.0
- Fixed a race condition with purchase completed callbacks
https://github.com/RevenueCat/purchases-ios/pull/313
- Made RCTransaction public to fix compiling issues on Swift Package Manager
Expand All @@ -13,8 +13,6 @@
https://github.com/RevenueCat/purchases-ios/pull/320
- Added a local receipt parser, updated intro eligibility calculation to perform on device first
https://github.com/RevenueCat/purchases-ios/pull/302

## 3.6.0-beta-1
- Fix crash when productIdentifier or payment is nil.
https://github.com/RevenueCat/purchases-ios/pull/297
- Fixes ask-to-buy flow and will now send an error indicating there's a deferred payment.
Expand All @@ -25,6 +23,10 @@
https://github.com/RevenueCat/purchases-ios/pull/287
- New properties added to the PurchaserInfo to better manage non-subscriptions.
https://github.com/RevenueCat/purchases-ios/pull/281
- Bypass workaround in watchOS 7 that fixes watchOS 6.2 bug where devices report wrong `appStoreReceiptURL`
https://github.com/RevenueCat/purchases-ios/pull/330
- Fix bug where 404s in subscriber attributes POST would mark them as synced
https://github.com/RevenueCat/purchases-ios/pull/328

## 3.5.2
- Feature/defer cache updates if woken from push notification
Expand Down
8 changes: 4 additions & 4 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ GEM
json (>= 1.5.1)
atomos (0.1.3)
aws-eventstream (1.1.0)
aws-partitions (1.359.0)
aws-partitions (1.362.0)
aws-sdk-core (3.105.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.239.0)
Expand All @@ -23,7 +23,7 @@ GEM
aws-sdk-kms (1.37.0)
aws-sdk-core (~> 3, >= 3.99.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.79.0)
aws-sdk-s3 (1.79.1)
aws-sdk-core (~> 3, >= 3.104.3)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.1)
Expand Down Expand Up @@ -89,7 +89,7 @@ GEM
faraday_middleware (1.0.0)
faraday (~> 1.0)
fastimage (2.2.0)
fastlane (2.156.1)
fastlane (2.157.2)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.3, < 3.0.0)
aws-sdk-s3 (~> 1.0)
Expand Down Expand Up @@ -144,7 +144,7 @@ GEM
google-cloud-env (1.3.3)
faraday (>= 0.17.3, < 2.0)
google-cloud-errors (1.0.1)
google-cloud-storage (1.27.0)
google-cloud-storage (1.28.0)
addressable (~> 2.5)
digest-crc (~> 0.4)
google-api-client (~> 0.33)
Expand Down
4 changes: 2 additions & 2 deletions Purchases.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "Purchases"
s.version = "3.6.0-beta-2"
s.version = "3.6.0"
s.summary = "Subscription and in-app-purchase backend service."

s.description = <<-DESC
Expand All @@ -22,7 +22,7 @@ Pod::Spec.new do |s|
s.tvos.deployment_target = '9.0'

s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
s.dependency 'PurchasesCoreSwift'
s.dependency 'PurchasesCoreSwift', '3.6.0'

s.source_files = ['Purchases/**/*.{h,m}']
s.public_header_files = [
Expand Down
4 changes: 4 additions & 0 deletions Purchases.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@
37E35F20B49BCE1B6D76B084 /* RCStoreKitRequestFetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 37E35804C14F5E6CEAF3909C /* RCStoreKitRequestFetcher.h */; settings = {ATTRIBUTES = (Private, ); }; };
37E35F20FB949985BEEB4B58 /* MockRequestFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E35609E46E869675A466C1 /* MockRequestFetcher.swift */; };
37E35F549AEB655AB6DA83B3 /* MockSKDiscount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E35EABF6D7AFE367718784 /* MockSKDiscount.swift */; };
37E35F67255A87BD86B39D43 /* MockReceiptParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E3558F697A939D2BBD7FEC /* MockReceiptParser.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -374,6 +375,7 @@
37E35548F15DE7CFFCE3AA8A /* NSLocale+RCExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSLocale+RCExtensions.m"; sourceTree = "<group>"; };
37E3555B4BE0A4F7222E7B00 /* MockOfferingsFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockOfferingsFactory.swift; sourceTree = "<group>"; };
37E355744D64075AA91342DE /* MockInAppPurchaseBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockInAppPurchaseBuilder.swift; sourceTree = "<group>"; };
37E3558F697A939D2BBD7FEC /* MockReceiptParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockReceiptParser.swift; sourceTree = "<group>"; };
37E355AE6CB674484555D1AC /* NSDate+RCExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDate+RCExtensions.h"; sourceTree = "<group>"; };
37E355CBB3F3A31A32687B14 /* Transaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Transaction.swift; sourceTree = "<group>"; };
37E35609E46E869675A466C1 /* MockRequestFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockRequestFetcher.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -784,6 +786,7 @@
2DD7BA4C24C63A830066B4C2 /* MockSystemInfo.swift */,
37E35659EB530A5109AFAB50 /* MockOperationDispatcher.swift */,
2D4D6AF224F7172900B656BE /* MockProductsRequest.swift */,
37E3558F697A939D2BBD7FEC /* MockReceiptParser.swift */,
);
path = Mocks;
sourceTree = "<group>";
Expand Down Expand Up @@ -1428,6 +1431,7 @@
37E35F0387D0ADE014186924 /* ProductInfoExtractorTests.swift in Sources */,
37E351505CB4764821451E27 /* ProductInfoExtensions.swift in Sources */,
37E3599326581376E0142EEC /* SystemInfoTests.swift in Sources */,
37E35F67255A87BD86B39D43 /* MockReceiptParser.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
2 changes: 1 addition & 1 deletion Purchases/Misc/RCSystemInfo.m
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ + (BOOL)isSandbox {
}

+ (NSString *)frameworkVersion {
return @"3.6.0-beta-2";
return @"3.6.0";
}

+ (NSString *)systemVersion {
Expand Down
6 changes: 4 additions & 2 deletions Purchases/Networking/RCBackend.m
Original file line number Diff line number Diff line change
Expand Up @@ -479,8 +479,10 @@ - (void)handleSubscriberAttributesResultWithStatusCode:(NSInteger)statusCode
- (NSDictionary *)attributesUserInfoFromResponse:(NSDictionary *)response statusCode:(NSInteger)statusCode {
NSMutableDictionary *resultDict = [[NSMutableDictionary alloc] init];
BOOL isInternalServerError = statusCode >= RC_INTERNAL_SERVER_ERROR;
resultDict[RCSuccessfullySyncedKey] = @(!isInternalServerError);

BOOL isNotFoundError = statusCode == RC_NOT_FOUND_ERROR;
BOOL successfullySynced = !(isInternalServerError || isNotFoundError);
resultDict[RCSuccessfullySyncedKey] = @(successfullySynced);

BOOL hasAttributesResponseContainerKey = (response[RCAttributeErrorsResponseKey] != nil);
NSDictionary *attributesResponseDict = hasAttributesResponseContainerKey
? response[RCAttributeErrorsResponseKey]
Expand Down
1 change: 1 addition & 0 deletions Purchases/Networking/RCHTTPStatusCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ NS_ASSUME_NONNULL_BEGIN

typedef NS_ENUM(NSUInteger, RCHTTPStatusCodes) {
RC_REDIRECT = 300,
RC_NOT_FOUND_ERROR = 404,
RC_INTERNAL_SERVER_ERROR = 500,
RC_NETWORK_CONNECT_TIMEOUT_ERROR = 599
};
Expand Down
6 changes: 4 additions & 2 deletions Purchases/ProtectedExtensions/RCPurchases+Protected.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
RCSubscriberAttributesManager,
RCSystemInfo,
RCOperationDispatcher,
RCIntroEligibilityCalculator;
RCIntroEligibilityCalculator,
RCReceiptParser;

NS_ASSUME_NONNULL_BEGIN

Expand All @@ -40,7 +41,8 @@ NS_ASSUME_NONNULL_BEGIN
identityManager:(RCIdentityManager *)identityManager
subscriberAttributesManager:(RCSubscriberAttributesManager *)subscriberAttributesManager
operationDispatcher:(RCOperationDispatcher *)operationDispatcher
introEligibilityCalculator:(RCIntroEligibilityCalculator *)introEligibilityCalculator;
introEligibilityCalculator:(RCIntroEligibilityCalculator *)introEligibilityCalculator
receiptParser:(RCReceiptParser *)receiptParser;

+ (void)setDefaultInstance:(nullable RCPurchases *)instance;

Expand Down
19 changes: 16 additions & 3 deletions Purchases/Public/RCPurchases.m
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ @interface RCPurchases () <RCStoreKitWrapperDelegate> {
@property (nonatomic) RCDeviceCache *deviceCache;
@property (nonatomic) RCIdentityManager *identityManager;
@property (nonatomic) RCSystemInfo *systemInfo;
@property (nonatomic) RCOperationDispatcher *operationDispatcher;
@property (nonatomic) RCIntroEligibilityCalculator *introEligibilityCalculator;
@property (nonatomic) RCReceiptParser *receiptParser;

@end

Expand Down Expand Up @@ -225,6 +225,7 @@ - (instancetype)initWithAPIKey:(NSString *)APIKey
deviceCache:deviceCache];
RCOperationDispatcher *operationDispatcher = [[RCOperationDispatcher alloc] init];
RCIntroEligibilityCalculator *introCalculator = [[RCIntroEligibilityCalculator alloc] init];
RCReceiptParser *receiptParser = [[RCReceiptParser alloc] init];

return [self initWithAppUserID:appUserID
requestFetcher:fetcher
Expand All @@ -240,7 +241,8 @@ - (instancetype)initWithAPIKey:(NSString *)APIKey
identityManager:identityManager
subscriberAttributesManager:subscriberAttributesManager
operationDispatcher:operationDispatcher
introEligibilityCalculator:introCalculator];
introEligibilityCalculator:introCalculator
receiptParser:receiptParser];
}

- (instancetype)initWithAppUserID:(nullable NSString *)appUserID
Expand All @@ -257,7 +259,8 @@ - (instancetype)initWithAppUserID:(nullable NSString *)appUserID
identityManager:(RCIdentityManager *)identityManager
subscriberAttributesManager:(RCSubscriberAttributesManager *)subscriberAttributesManager
operationDispatcher:(RCOperationDispatcher *)operationDispatcher
introEligibilityCalculator:(RCIntroEligibilityCalculator *)introEligibilityCalculator {
introEligibilityCalculator:(RCIntroEligibilityCalculator *)introEligibilityCalculator
receiptParser:(RCReceiptParser *)receiptParser {
if (self = [super init]) {
RCDebugLog(@"Debug logging enabled.");
RCDebugLog(@"SDK Version - %@", self.class.frameworkVersion);
Expand All @@ -283,6 +286,7 @@ - (instancetype)initWithAppUserID:(nullable NSString *)appUserID
self.subscriberAttributesManager = subscriberAttributesManager;
self.operationDispatcher = operationDispatcher;
self.introEligibilityCalculator = introEligibilityCalculator;
self.receiptParser = receiptParser;

RCReceivePurchaserInfoBlock callDelegate = ^void(RCPurchaserInfo *info, NSError *error) {
if (info) {
Expand Down Expand Up @@ -625,6 +629,15 @@ - (void)restoreTransactionsWithCompletionBlock:(nullable RCReceivePurchaserInfoB
CALL_IF_SET_ON_MAIN_THREAD(completion, nil, [RCPurchasesErrorUtils missingReceiptFileError]);
return;
}

RCPurchaserInfo * _Nullable cachedPurchaserInfo = [self readPurchaserInfoFromCache];
BOOL hasOriginalPurchaseDate = cachedPurchaserInfo != nil && cachedPurchaserInfo.originalPurchaseDate != nil;
BOOL receiptHasTransactions = [self.receiptParser receiptHasTransactionsWithReceiptData:data];
if (!receiptHasTransactions && hasOriginalPurchaseDate) {
CALL_IF_SET_ON_MAIN_THREAD(completion, cachedPurchaserInfo, nil);
return;
}

RCSubscriberAttributeDict subscriberAttributes = self.unsyncedAttributesByKey;
[self.backend postReceiptData:data
appUserID:self.appUserID
Expand Down
4 changes: 3 additions & 1 deletion Purchases/Purchasing/RCReceiptFetcher.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ - (NSData *)receiptData {
// correct receipt.
// This has been filed as radar FB7699277. More info in https://github.com/RevenueCat/purchases-ios/issues/207.

if (RCSystemInfo.isSandbox) {
NSOperatingSystemVersion minimumOSVersionWithoutBug = { .majorVersion = 7, .minorVersion = 0, .patchVersion = 0 };
BOOL isBelowMinimumOSVersionWithoutBug = ![NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:minimumOSVersionWithoutBug];
if (isBelowMinimumOSVersionWithoutBug && RCSystemInfo.isSandbox) {
NSString *receiptURLFolder = [[receiptURL absoluteString] stringByDeletingLastPathComponent];
NSURL *productionReceiptURL = [NSURL URLWithString:[receiptURLFolder stringByAppendingPathComponent:@"receipt"]];
receiptURL = productionReceiptURL;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#import "RCPurchases.h"
#import "RCSubscriberAttribute.h"

@class RCSubscriberAttribute, RCSubscriberAttributesManager;
@class RCSubscriberAttribute, RCSubscriberAttributesManager, RCOperationDispatcher;

NS_ASSUME_NONNULL_BEGIN

Expand All @@ -31,6 +31,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface RCPurchases ()

@property (nonatomic) RCSubscriberAttributesManager *subscriberAttributesManager;
@property (nonatomic) RCOperationDispatcher *operationDispatcher;

@end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#import "RCCrossPlatformSupport.h"
#import "RCLogUtils.h"
#import "NSError+RCExtensions.h"
@import PurchasesCoreSwift;

NS_ASSUME_NONNULL_BEGIN

Expand Down Expand Up @@ -86,7 +87,9 @@ - (void)subscribeToAppBackgroundedNotifications {
}

- (void)syncSubscriberAttributesIfNeeded {
[self.subscriberAttributesManager syncAttributesForAllUsersWithCurrentAppUserID:self.appUserID];
[self.operationDispatcher dispatchOnWorkerThread:^{
[self.subscriberAttributesManager syncAttributesForAllUsersWithCurrentAppUserID:self.appUserID];
}];
}

@end
Expand Down
2 changes: 1 addition & 1 deletion PurchasesCoreSwift.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "PurchasesCoreSwift"
s.version = "3.6.0-beta-2"
s.version = "3.6.0"
s.summary = "Swift portion of RevenueCat's Subscription and in-app-purchase backend service."

s.description = <<-DESC
Expand Down
29 changes: 22 additions & 7 deletions PurchasesCoreSwift/LocalReceiptParsing/ReceiptParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,32 @@

import Foundation

class ReceiptParser {
private let objectIdentifierParser: ASN1ObjectIdentifierBuilder
@objc(RCReceiptParser) public class ReceiptParser: NSObject {
private let objectIdentifierBuilder: ASN1ObjectIdentifierBuilder
private let containerBuilder: ASN1ContainerBuilder
private let receiptBuilder: AppleReceiptBuilder

init(objectIdentifierParser: ASN1ObjectIdentifierBuilder = ASN1ObjectIdentifierBuilder(),
containerBuilder: ASN1ContainerBuilder = ASN1ContainerBuilder(),
receiptBuilder: AppleReceiptBuilder = AppleReceiptBuilder()) {
self.objectIdentifierParser = objectIdentifierParser
@objc public convenience override init() {
self.init(objectIdentifierBuilder: ASN1ObjectIdentifierBuilder(),
containerBuilder: ASN1ContainerBuilder(),
receiptBuilder: AppleReceiptBuilder())
}

init(objectIdentifierBuilder: ASN1ObjectIdentifierBuilder,
containerBuilder: ASN1ContainerBuilder,
receiptBuilder: AppleReceiptBuilder) {
self.objectIdentifierBuilder = objectIdentifierBuilder
self.containerBuilder = containerBuilder
self.receiptBuilder = receiptBuilder
super.init()
}

@objc public func receiptHasTransactions(receiptData: Data) -> Bool {
if let receipt = try? parse(from: receiptData) {
return receipt.inAppPurchases.count > 0
}
// if the receipt can't be parsed, conservatively return true
return true
}

func parse(from receiptData: Data) throws -> AppleReceipt {
Expand All @@ -40,7 +55,7 @@ private extension ReceiptParser {
if container.encodingType == .constructed {
for (index, internalContainer) in container.internalContainers.enumerated() {
if internalContainer.containerIdentifier == .objectIdentifier {
let objectIdentifier = objectIdentifierParser.build(fromPayload: internalContainer.internalPayload)
let objectIdentifier = objectIdentifierBuilder.build(fromPayload: internalContainer.internalPayload)
if objectIdentifier == objectId && index < container.internalContainers.count - 1 {
// the container that holds the data comes right after the one with the object identifier
return container.internalContainers[index + 1]
Expand Down
Loading

0 comments on commit 46dac33

Please sign in to comment.