Skip to content

Commit

Permalink
Hotfix Release 3.5.2 (#299)
Browse files Browse the repository at this point in the history
* Feature/defer cache updates if woken from push notification (#288)

* Added logic to skip updating cache on setup if app is not active

* inverted checks from is app active to !is app backgrounded

* made the app backgrounded method an instance method to make tests easier. Added tests to check that caches aren't updated when the SDK is set up with the app running in the background.

* updated value of isApplicationBackgrounded for macOS, since the value for NSApplication.sharedApplication.isActive is nil for whatever reason.

* Version 3.5.2

Co-authored-by: aboedo <[email protected]>
  • Loading branch information
vegaro and aboedo authored Jul 28, 2020
1 parent c44d4e1 commit bc0e76d
Show file tree
Hide file tree
Showing 12 changed files with 125 additions and 21 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.5.1
module_version: 3.5.2
github_url: https://github.com/revenuecat/purchases-ios
github_file_prefix: https://github.com/revenuecat/purchases-ios/tree/3.5.1
github_file_prefix: https://github.com/revenuecat/purchases-ios/tree/3.5.2
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)]
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 3.5.2
- Feature/defer cache updates if woken from push notification
https://github.com/RevenueCat/purchases-ios/pull/288

## 3.5.1
- Removes all references to ASIdentifierManager and advertisingIdentifier. This should help with some Kids apps being rejected
https://github.com/RevenueCat/purchases-ios/pull/286
Expand Down
2 changes: 1 addition & 1 deletion 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.5.1"
s.version = "3.5.2"
s.summary = "Subscription and in-app-purchase backend service."

s.description = <<-DESC
Expand Down
4 changes: 4 additions & 0 deletions Purchases.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
2D8DB34B24072AAE00BE3D31 /* SubscriberAttributeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D8DB34A24072AAE00BE3D31 /* SubscriberAttributeTests.swift */; };
2DD448FF24088473002F5694 /* RCPurchases+SubscriberAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD448FD24088473002F5694 /* RCPurchases+SubscriberAttributes.h */; settings = {ATTRIBUTES = (Private, ); }; };
2DD4490024088473002F5694 /* RCPurchases+SubscriberAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = 2DD448FE24088473002F5694 /* RCPurchases+SubscriberAttributes.m */; };
2DD7BA4D24C63A830066B4C2 /* MockSystemInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7BA4C24C63A830066B4C2 /* MockSystemInfo.swift */; };
2DEB9767247DB46900A92099 /* RCISOPeriodFormatter.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DEB9766247DB46900A92099 /* RCISOPeriodFormatter.h */; settings = {ATTRIBUTES = (Private, ); }; };
2DEB976B247DB85400A92099 /* SKProductSubscriptionDurationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DEB976A247DB85400A92099 /* SKProductSubscriptionDurationExtensions.swift */; };
350FBDE91F7EEF070065833D /* RCPurchases.h in Headers */ = {isa = PBXBuildFile; fileRef = 350FBDE71F7EEF070065833D /* RCPurchases.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -178,6 +179,7 @@
2D8DB34A24072AAE00BE3D31 /* SubscriberAttributeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriberAttributeTests.swift; sourceTree = "<group>"; };
2DD448FD24088473002F5694 /* RCPurchases+SubscriberAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "RCPurchases+SubscriberAttributes.h"; path = "Purchases/SubscriberAttributes/RCPurchases+SubscriberAttributes.h"; sourceTree = SOURCE_ROOT; };
2DD448FE24088473002F5694 /* RCPurchases+SubscriberAttributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "RCPurchases+SubscriberAttributes.m"; path = "Purchases/SubscriberAttributes/RCPurchases+SubscriberAttributes.m"; sourceTree = SOURCE_ROOT; };
2DD7BA4C24C63A830066B4C2 /* MockSystemInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockSystemInfo.swift; sourceTree = "<group>"; };
2DEB9766247DB46900A92099 /* RCISOPeriodFormatter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCISOPeriodFormatter.h; sourceTree = "<group>"; };
2DEB976A247DB85400A92099 /* SKProductSubscriptionDurationExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SKProductSubscriptionDurationExtensions.swift; sourceTree = "<group>"; };
350A1B84226E3E8700CCA10F /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
Expand Down Expand Up @@ -480,6 +482,7 @@
37E351D48260D9DC8B1EE360 /* MockSubscriberAttributesManager.swift */,
2DEB976A247DB85400A92099 /* SKProductSubscriptionDurationExtensions.swift */,
37E35EABF6D7AFE367718784 /* MockSKDiscount.swift */,
2DD7BA4C24C63A830066B4C2 /* MockSystemInfo.swift */,
);
path = Mocks;
sourceTree = "<group>";
Expand Down Expand Up @@ -902,6 +905,7 @@
37E35EBDFC5CD3068E1792A3 /* MockNotificationCenter.swift in Sources */,
37E354E0A9A371481540B2B0 /* MockAttributionFetcher.swift in Sources */,
37E35EDC57C486AC2D66B4B8 /* MockOfferingsFactory.swift in Sources */,
2DD7BA4D24C63A830066B4C2 /* MockSystemInfo.swift in Sources */,
37E35EB7B35C86140B96C58B /* MockUserManager.swift in Sources */,
37E357E33F0E20D92EE6372E /* MockSKProduct.swift in Sources */,
37E3524CB70618E6C5F3DB49 /* MockPurchasesDelegate.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion Purchases/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>3.5.1</string>
<string>3.5.2</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
Expand Down
13 changes: 12 additions & 1 deletion Purchases/Misc/RCCrossPlatformSupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@
#define APP_WILL_RESIGN_ACTIVE_NOTIFICATION_NAME NSExtensionHostWillResignActiveNotification
#endif

#if TARGET_OS_IPHONE
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_MACCATALYST
#import <UIKit/UIKit.h>
#elif TARGET_OS_OSX
#import <AppKit/AppKit.h>
#elif TARGET_OS_WATCH
#import <UIKit/UIKit.h>
#import <WatchKit/WatchKit.h>
#endif

#if TARGET_OS_MACCATALYST
Expand Down Expand Up @@ -58,3 +61,11 @@
#else
#define PURCHASES_INITIATED_FROM_APP_STORE_AVAILABLE 0
#endif

#if TARGET_OS_IOS || TARGET_OS_TV
#define IS_APPLICATION_BACKGROUNDED UIApplication.sharedApplication.applicationState == UIApplicationStateBackground
#elif TARGET_OS_OSX
#define IS_APPLICATION_BACKGROUNDED NO
#elif TARGET_OS_WATCH
#define IS_APPLICATION_BACKGROUNDED WKExtension.sharedExtension.applicationState == WKApplicationStateBackground
#endif
3 changes: 3 additions & 0 deletions Purchases/Misc/RCSystemInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ NS_ASSUME_NONNULL_BEGIN
@property(nonatomic, copy, readonly) NSString *platformFlavor;
@property(nonatomic, copy, readonly) NSString *platformFlavorVersion;


- (BOOL)isApplicationBackgrounded;

+ (BOOL)isSandbox;
+ (NSString *)frameworkVersion;
+ (NSString *)systemVersion;
Expand Down
7 changes: 6 additions & 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.5.1";
return @"3.5.2";
}

+ (NSString *)systemVersion {
Expand Down Expand Up @@ -75,13 +75,18 @@ + (NSURL *)serverHostURL {
+ (nullable NSURL *)proxyURL {
return proxyURL;
}

+ (void)setProxyURL:(nullable NSURL *)newProxyURL {
proxyURL = newProxyURL;
if (newProxyURL) {
RCLog(@"Purchases is being configured using a proxy for RevenueCat with URL: %@", newProxyURL);
}
}

- (BOOL)isApplicationBackgrounded {
return IS_APPLICATION_BACKGROUNDED;
}

@end


Expand Down
25 changes: 19 additions & 6 deletions Purchases/Public/RCPurchases.m
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,12 @@ - (instancetype)initWithAppUserID:(nullable NSString *)appUserID
};

[self.identityManager configureWithAppUserID:appUserID];
[self updateAllCachesWithCompletionBlock:callDelegate];
if (!self.systemInfo.isApplicationBackgrounded) {
[self updateAllCachesWithCompletionBlock:callDelegate];
} else {
[self sendCachedPurchaserInfoIfAvailable];
}

[self configureSubscriberAttributesManager];

self.storeKitWrapper.delegate = self;
Expand Down Expand Up @@ -335,11 +340,8 @@ - (void)setDelegate:(id<RCPurchasesDelegate>)delegate
{
_delegate = delegate;
RCDebugLog(@"Delegate set");

RCPurchaserInfo *infoFromCache = [self readPurchaserInfoFromCache];
if (infoFromCache) {
[self sendUpdatedPurchaserInfoToDelegateIfChanged:infoFromCache];
}

[self sendCachedPurchaserInfoIfAvailable];
}

#pragma mark - Public Methods
Expand Down Expand Up @@ -738,6 +740,17 @@ - (void)setPushToken:(nullable NSData *)pushToken {

- (void)applicationDidBecomeActive:(__unused NSNotification *)notif
{
[self updateAllCachesIfNeeded];
}

- (void)sendCachedPurchaserInfoIfAvailable {
RCPurchaserInfo *infoFromCache = [self readPurchaserInfoFromCache];
if (infoFromCache) {
[self sendUpdatedPurchaserInfoToDelegateIfChanged:infoFromCache];
}
}

- (void)updateAllCachesIfNeeded {
RCDebugLog(@"applicationDidBecomeActive");
if ([self.deviceCache isPurchaserInfoCacheStale]) {
RCDebugLog(@"PurchaserInfo cache is stale, updating caches");
Expand Down
2 changes: 1 addition & 1 deletion PurchasesTests/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>3.5.1</string>
<string>3.5.2</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
Expand Down
17 changes: 17 additions & 0 deletions PurchasesTests/Mocks/MockSystemInfo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// MockSystemInfo.swift
// PurchasesTests
//
// Created by Andrés Boedo on 7/20/20.
// Copyright © 2020 Purchases. All rights reserved.
//

import Foundation

class MockSystemInfo: RCSystemInfo {
var stubbedIsApplicationBackgrounded: Bool?

override func isApplicationBackgrounded() -> Bool {
return stubbedIsApplicationBackgrounded ?? super.isApplicationBackgrounded()
}
}
63 changes: 55 additions & 8 deletions PurchasesTests/Purchasing/PurchasesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -179,16 +179,16 @@ class PurchasesTests: XCTestCase {
let deviceCache = MockDeviceCache()
let subscriberAttributesManager = MockSubscriberAttributesManager()
let identityManager = MockUserManager(mockAppUserID: "app_user");

let systemInfo = MockSystemInfo(platformFlavor: nil, platformFlavorVersion: nil, finishTransactions: true)

let purchasesDelegate = MockPurchasesDelegate()

var purchases: Purchases!

func setupPurchases(automaticCollection: Bool = false) {
Purchases.automaticAppleSearchAdsAttributionCollection = automaticCollection
self.identityManager.mockIsAnonymous = false
let systemInfo = RCSystemInfo(platformFlavor: nil, platformFlavorVersion: nil, finishTransactions: true)


purchases = Purchases(appUserID: identityManager.currentAppUserID,
requestFetcher: requestFetcher,
receiptFetcher: receiptFetcher,
Expand All @@ -209,8 +209,7 @@ class PurchasesTests: XCTestCase {
func setupAnonPurchases() {
Purchases.automaticAppleSearchAdsAttributionCollection = false
self.identityManager.mockIsAnonymous = true
let systemInfo = RCSystemInfo(platformFlavor: nil, platformFlavorVersion: nil, finishTransactions: true)


purchases = Purchases(appUserID: nil,
requestFetcher: requestFetcher,
receiptFetcher: receiptFetcher,
Expand Down Expand Up @@ -259,10 +258,46 @@ class PurchasesTests: XCTestCase {
expect(self.purchasesDelegate.purchaserInfoReceivedCount).toEventually(equal(1))
}

func testFirstInitializationCallDelegateForAnon() {
setupAnonPurchases()
func testFirstInitializationFromForegroundDelegateForAnonIfNothingCached() {
systemInfo.stubbedIsApplicationBackgrounded = false
setupPurchases()
expect(self.purchasesDelegate.purchaserInfoReceivedCount).toEventually(equal(1))
}

func testFirstInitializationFromBackgroundDoesntCallDelegateForAnonIfNothingCached() {
systemInfo.stubbedIsApplicationBackgrounded = true
setupPurchases()
expect(self.purchasesDelegate.purchaserInfoReceivedCount).toEventually(equal(0))
}

func testFirstInitializationFromBackgroundDoesntCallDelegateForAnonIfInfoCached() {
systemInfo.stubbedIsApplicationBackgrounded = true
let info = Purchases.PurchaserInfo(data: [
"subscriber": [
"subscriptions": [:],
"other_purchases": [:]
]]);

let jsonObject = info!.jsonObject()

let object = try! JSONSerialization.data(withJSONObject: jsonObject, options: []);
self.deviceCache.cachedPurchaserInfo[identityManager.currentAppUserID] = object

setupPurchases()
expect(self.purchasesDelegate.purchaserInfoReceivedCount).toEventually(equal(1))
}

func testFirstInitializationFromBackgroundDoesntUpdatePurchaserInfoCache() {
systemInfo.stubbedIsApplicationBackgrounded = true
setupPurchases()
expect(self.backend.getSubscriberCallCount).toEventually(equal(0))
}

func testFirstInitializationFromForegroundUpdatesPurchaserInfoCache() {
systemInfo.stubbedIsApplicationBackgrounded = false
setupPurchases()
expect(self.backend.getSubscriberCallCount).toEventually(equal(1))
}

func testDelegateIsCalledForRandomPurchaseSuccess() {
setupPurchases()
Expand Down Expand Up @@ -606,7 +641,7 @@ class PurchasesTests: XCTestCase {
}
}

func testFetchesProductInfoIfNotCached() {
func testFetchesProductInfoIfNotCachedAndAppActive() {
setupPurchases()
let product = MockSKProduct(mockProductIdentifier: "com.product.id1")

Expand Down Expand Up @@ -1258,6 +1293,18 @@ class PurchasesTests: XCTestCase {
expect(offerings!["base"]!.monthly?.product).toNot(beNil())
}

func testFirstInitializationGetsOfferingsIfAppActive() {
systemInfo.stubbedIsApplicationBackgrounded = false
setupPurchases()
expect(self.backend.gotOfferings).toEventually(equal(1))
}

func testFirstInitializationDoesntProductInfoFromOfferingsIfAppBackgrounded() {
systemInfo.stubbedIsApplicationBackgrounded = true
setupPurchases()
expect(self.backend.gotOfferings).toEventually(equal(0))
}

func testProductInfoIsCachedForOfferings() {
setupPurchases()
expect(self.backend.gotOfferings).toEventually(equal(1))
Expand Down

0 comments on commit bc0e76d

Please sign in to comment.