Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release/3.6.0 #335

Merged
merged 8 commits into from
Sep 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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