From 2efb7abfab8de052f3ec32a87be2c52ba4c2ba46 Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Thu, 15 Jul 2021 15:46:14 -0300 Subject: [PATCH 01/14] updated version numbers and changelog --- CHANGELOG.md | 26 +++++++++++++++++++ PurchasesHybridCommon.podspec | 4 +-- android/build.gradle | 4 +-- android/gradle.properties | 2 +- fastlane/README.md | 2 +- ios/PurchasesHybridCommon/Info.plist | 2 +- ios/PurchasesHybridCommon/Podfile | 2 +- .../PurchasesHybridCommonTests/Info.plist | 2 +- 8 files changed, 35 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ac826f4..1012f5fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,29 @@ +### 1.8.0 + +#### Identity V3 + +##### New methods +- Introduces `logIn`, a new way of identifying users, which also returns whether a new user has been registered in the system. +`logIn` uses a new backend endpoint. +- Introduces `logOut`, a replacement for `reset`. + +##### Deprecations +- deprecates `createAlias` in favor of `logIn` +- deprecates `identify` in favor of `logIn` +- deprecates `reset` in favor of `logOut` +- deprecates `allowSharingStoreAccount` in favor of dashboard-side configuration + +#### Dependency updates +- Bumps `purchases-ios` to `3.12.0` ([Changelog here](/~https://github.com/RevenueCat/purchases-ios/releases/3.12.0)) +- Bumps `purchases-android` to `4.3.0` ([Changelog here](/~https://github.com/RevenueCat/purchases-android/releases/4.3.0)) + /~https://github.com/RevenueCat/purchases-hybrid-common/pull/84 + +#### Bug Fixes +- Added `readableErrorCode` to `UserInfo` when creating `ErrorContainer`, so all errors have `readableErrorCode` + /~https://github.com/RevenueCat/purchases-hybrid-common/pull/82 +- Made `underlyingErrorMessage` an empty string if it's missing in iOS + /~https://github.com/RevenueCat/purchases-hybrid-common/pull/71 + ### 1.7.1 - Fixed dependency specificiation in Podspec to purchases-ios@3.11.1 diff --git a/PurchasesHybridCommon.podspec b/PurchasesHybridCommon.podspec index 21755038..04cdb20a 100644 --- a/PurchasesHybridCommon.podspec +++ b/PurchasesHybridCommon.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "PurchasesHybridCommon" - s.version = "1.7.1" + s.version = "1.8.0" s.summary = "Common files for hybrid SDKs for RevenueCat's Subscription and in-app-purchase backend service." s.description = <<-DESC @@ -15,7 +15,7 @@ Pod::Spec.new do |s| s.framework = 'StoreKit' - s.dependency 'Purchases', '3.11.1' + s.dependency 'Purchases', '3.12.0' s.swift_version = '5.0' s.ios.deployment_target = '9.0' diff --git a/android/build.gradle b/android/build.gradle index 19ccc6d0..0e58b39f 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -8,7 +8,7 @@ buildscript { ext.assertj_version = '3.13.2' ext.mockk_version = '1.10.0' ext.gradle_maven_publish = '0.11.1' - ext.purchases_version = '4.2.1' + ext.purchases_version = '4.3.0' repositories { google() @@ -37,7 +37,7 @@ android { minSdkVersion 14 targetSdkVersion 29 versionCode 1 - versionName "1.7.1" + versionName "1.8.0" consumerProguardFiles 'consumer-rules.pro' } diff --git a/android/gradle.properties b/android/gradle.properties index 4ba4a259..67546eb5 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -22,7 +22,7 @@ kotlin.code.style=official # Maven GROUP=com.revenuecat.purchases -VERSION_NAME=1.7.1 +VERSION_NAME=1.8.0 POM_NAME=purchases-hybrid-common POM_PACKAGING=aar POM_ARTIFACT_ID=purchases-hybrid-common diff --git a/fastlane/README.md b/fastlane/README.md index edd384cb..083e1e0c 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -41,6 +41,6 @@ Increment build number ---- -This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. +This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run. More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). diff --git a/ios/PurchasesHybridCommon/Info.plist b/ios/PurchasesHybridCommon/Info.plist index 90c35c08..b1b9e872 100644 --- a/ios/PurchasesHybridCommon/Info.plist +++ b/ios/PurchasesHybridCommon/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 1.7.1 + 1.8.0 CFBundleVersion $(CURRENT_PROJECT_VERSION) diff --git a/ios/PurchasesHybridCommon/Podfile b/ios/PurchasesHybridCommon/Podfile index 0935aa7f..7f21cfb6 100644 --- a/ios/PurchasesHybridCommon/Podfile +++ b/ios/PurchasesHybridCommon/Podfile @@ -6,7 +6,7 @@ target 'PurchasesHybridCommon' do use_frameworks! # Pods for PurchasesHybridCommon - pod 'Purchases', '3.11.1' + pod 'Purchases', '3.12.0' target 'PurchasesHybridCommonTests' do # Pods for testing diff --git a/ios/PurchasesHybridCommon/PurchasesHybridCommonTests/Info.plist b/ios/PurchasesHybridCommon/PurchasesHybridCommonTests/Info.plist index 5d7e5afd..5b7fd1c5 100644 --- a/ios/PurchasesHybridCommon/PurchasesHybridCommonTests/Info.plist +++ b/ios/PurchasesHybridCommon/PurchasesHybridCommonTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 1.7.1 + 1.8.0 CFBundleVersion 1 From c657f2f5efd12e60f456fe2ee6799a23328d7032 Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Thu, 15 Jul 2021 15:46:33 -0300 Subject: [PATCH 02/14] update fastlane --- Gemfile.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 991a8b8f..4bf066df 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,8 +7,8 @@ GEM artifactory (3.0.15) atomos (0.1.3) aws-eventstream (1.1.1) - aws-partitions (1.474.0) - aws-sdk-core (3.116.0) + aws-partitions (1.478.0) + aws-sdk-core (3.117.0) aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.239.0) aws-sigv4 (~> 1.1) @@ -20,7 +20,7 @@ GEM aws-sdk-core (~> 3, >= 3.112.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.1) - aws-sigv4 (1.2.3) + aws-sigv4 (1.2.4) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) claide (1.0.3) @@ -36,7 +36,7 @@ GEM dotenv (2.7.6) emoji_regex (3.2.2) excon (0.84.0) - faraday (1.5.0) + faraday (1.5.1) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -54,7 +54,7 @@ GEM faraday-excon (1.1.0) faraday-httpclient (1.0.1) faraday-net_http (1.0.1) - faraday-net_http_persistent (1.1.0) + faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) faraday_middleware (1.0.0) faraday (~> 1.0) @@ -122,7 +122,7 @@ GEM google-cloud-env (1.5.0) faraday (>= 0.17.3, < 2.0) google-cloud-errors (1.1.0) - google-cloud-storage (1.34.0) + google-cloud-storage (1.34.1) addressable (~> 2.5) digest-crc (~> 0.4) google-apis-iamcredentials_v1 (~> 0.1) @@ -154,7 +154,7 @@ GEM os (1.1.1) plist (3.6.0) public_suffix (4.0.6) - rake (13.0.4) + rake (13.0.6) representable (3.1.1) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) From 674c68c38003d92d47982b526e9b436eb8d80b7a Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Thu, 15 Jul 2021 15:47:21 -0300 Subject: [PATCH 03/14] pod install --- ios/PurchasesHybridCommon/Podfile.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ios/PurchasesHybridCommon/Podfile.lock b/ios/PurchasesHybridCommon/Podfile.lock index ebcc8e2b..996e139e 100644 --- a/ios/PurchasesHybridCommon/Podfile.lock +++ b/ios/PurchasesHybridCommon/Podfile.lock @@ -1,13 +1,13 @@ PODS: - Nimble (9.0.1) - - Purchases (3.11.1): - - PurchasesCoreSwift (= 3.11.1) - - PurchasesCoreSwift (3.11.1) + - Purchases (3.12.0): + - PurchasesCoreSwift (= 3.12.0) + - PurchasesCoreSwift (3.12.0) - Quick (3.1.2) DEPENDENCIES: - Nimble - - Purchases (= 3.11.1) + - Purchases (= 3.12.0) - Quick SPEC REPOS: @@ -19,10 +19,10 @@ SPEC REPOS: SPEC CHECKSUMS: Nimble: 7bed62ffabd6dbfe05f5925cbc43722533248990 - Purchases: 6351f9ff6bd514e5ec5aa0f989ea181effa94bf5 - PurchasesCoreSwift: ee857e4c21e6254b09d7e303a756fcf2b9164408 + Purchases: 1707e34353a006f57aa9250ca1e2dfa1b48ecafb + PurchasesCoreSwift: 6672e412019b8d3b987c3ee04b07eda117125c39 Quick: 60f0ea3b8e0cfc0df3259a5c06a238ad8b3c46e0 -PODFILE CHECKSUM: 570bc3c74d41dccf8632df281ba90dfa9da60b6e +PODFILE CHECKSUM: 08aea1984bc62b0cbde1ffc5e11d597e6f04151a COCOAPODS: 1.10.1 From d38b8474af52ab6cabdbfc5de3e0f022a18da6ed Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Thu, 15 Jul 2021 15:51:03 -0300 Subject: [PATCH 04/14] re-added identity v3 for android --- .../purchases/hybridcommon/common.kt | 23 +++ .../purchases/hybridcommon/CommonKtTests.kt | 169 ++++++++++++++++++ 2 files changed, 192 insertions(+) diff --git a/android/src/main/java/com/revenuecat/purchases/hybridcommon/common.kt b/android/src/main/java/com/revenuecat/purchases/hybridcommon/common.kt index 306d115e..26c8ad03 100644 --- a/android/src/main/java/com/revenuecat/purchases/hybridcommon/common.kt +++ b/android/src/main/java/com/revenuecat/purchases/hybridcommon/common.kt @@ -20,6 +20,8 @@ import com.revenuecat.purchases.identifyWith import com.revenuecat.purchases.purchasePackageWith import com.revenuecat.purchases.purchaseProductWith import com.revenuecat.purchases.resetWith +import com.revenuecat.purchases.logInWith +import com.revenuecat.purchases.logOutWith import com.revenuecat.purchases.restorePurchasesWith import com.revenuecat.purchases.common.PlatformInfo import com.revenuecat.purchases.interfaces.Callback @@ -182,6 +184,27 @@ fun restoreTransactions( } } +fun logIn( + appUserID: String, + onResult: OnResult +) { + Purchases.sharedInstance.logInWith(appUserID, + onError = { onResult.onError(it.map()) }, + onSuccess = { purchaserInfo, created -> + val resultMap: Map = mapOf( + "purchaserInfo" to purchaserInfo.map(), + "created" to created + ) + onResult.onReceived(resultMap) + }) +} + +fun logOut(onResult: OnResult) { + Purchases.sharedInstance.logOutWith(onError = { onResult.onError(it.map()) }) { + onResult.onReceived(it.map()) + } +} + fun reset( onResult: OnResult ) { diff --git a/android/src/test/java/com/revenuecat/purchases/hybridcommon/CommonKtTests.kt b/android/src/test/java/com/revenuecat/purchases/hybridcommon/CommonKtTests.kt index f14bdcdc..2e97eeb6 100644 --- a/android/src/test/java/com/revenuecat/purchases/hybridcommon/CommonKtTests.kt +++ b/android/src/test/java/com/revenuecat/purchases/hybridcommon/CommonKtTests.kt @@ -7,6 +7,7 @@ import com.revenuecat.purchases.BillingFeature import com.revenuecat.purchases.Purchases import com.revenuecat.purchases.common.PlatformInfo import com.revenuecat.purchases.interfaces.Callback +import com.revenuecat.purchases.interfaces.LogInCallback import io.mockk.Runs import io.mockk.every import io.mockk.just @@ -187,4 +188,172 @@ internal class CommonKtTests { onResultAny.onError(any()) } } + + @Test + fun `calling logIn correctly passes call to Purchases`() { + val appUserID = "appUserID" + + configure( + context = mockContext, + apiKey = "api_key", + appUserID = "appUserID", + observerMode = true, + platformInfo = PlatformInfo("flavor", "version") + ) + + every { mockPurchases.logIn(appUserID, any()) } just runs + + logIn(appUserID = appUserID, onResult = object : OnResult { + override fun onReceived(map: Map?) {} + override fun onError(errorContainer: ErrorContainer) {} + }) + + verify(exactly = 1) { mockPurchases.logIn(appUserID, any()) } + } + + @Test + fun `calling logIn correctly calls onReceived`() { + val appUserID = "appUserID" + + configure( + context = mockContext, + apiKey = "api_key", + appUserID = "appUserID", + observerMode = true, + platformInfo = PlatformInfo("flavor", "version") + ) + + val mockInfo = mockk(relaxed = true) + val mockCreated = Random.nextBoolean() + + val logInCallback = slot() + every { + mockPurchases.logIn( + newAppUserID = appUserID, + capture(logInCallback) + ) + } just runs + + val onResult = mockk(relaxed = true) + + logIn(appUserID = appUserID, onResult = onResult) + logInCallback.captured.onReceived(mockInfo, mockCreated) + + val mockInfoMap = mockInfo.map() + + verify(exactly = 1) { + onResult.onReceived(mapOf( + "created" to mockCreated, + "purchaserInfo" to mockInfoMap + )) + } + } + + @Test + fun `calling logIn with error calls onError`() { + val appUserID = "appUserID" + + configure( + context = mockContext, + apiKey = "api_key", + appUserID = "appUserID", + observerMode = true, + platformInfo = PlatformInfo("flavor", "version") + ) + + val mockError = mockk(relaxed = true) + + val logInCallback = slot() + every { + mockPurchases.logIn( + newAppUserID = appUserID, + capture(logInCallback) + ) + } just runs + + val onResult = mockk() + every { onResult.onReceived(any()) } just runs + every { onResult.onError(any()) } just runs + + logIn(appUserID = appUserID, onResult = onResult) + logInCallback.captured.onError(mockError) + + val mockErrorMap = mockError.map() + verify(exactly = 1) { + onResult.onError(mockErrorMap) + } + } + + @Test + fun `calling logOut correctly passes call to Purchases`() { + configure( + context = mockContext, + apiKey = "api_key", + appUserID = "appUserID", + observerMode = true, + platformInfo = PlatformInfo("flavor", "version") + ) + + every { mockPurchases.logOut(any()) } just runs + + logOut(onResult = object : OnResult { + override fun onReceived(map: Map?) {} + override fun onError(errorContainer: ErrorContainer) {} + }) + + verify(exactly = 1) { mockPurchases.logOut(any()) } + } + + @Test + fun `calling logOut correctly calls onReceived`() { + configure( + context = mockContext, + apiKey = "api_key", + appUserID = "appUserID", + observerMode = true, + platformInfo = PlatformInfo("flavor", "version") + ) + + val mockInfo = mockk(relaxed = true) + val receivePurchaserInfoListener = slot() + + every { mockPurchases.logOut(capture(receivePurchaserInfoListener)) } just runs + val onResult = mockk() + every { onResult.onReceived(any()) } just runs + every { onResult.onError(any()) } just runs + + logOut(onResult) + + receivePurchaserInfoListener.captured.onReceived(mockInfo) + + val mockInfoMap = mockInfo.map() + verify(exactly = 1) { onResult.onReceived(mockInfoMap) } + } + + @Test + fun `calling logOut with error calls onError`() { + configure( + context = mockContext, + apiKey = "api_key", + appUserID = "appUserID", + observerMode = true, + platformInfo = PlatformInfo("flavor", "version") + ) + + val mockError = mockk(relaxed = true) + val receivePurchaserInfoListener = slot() + + every { mockPurchases.logOut(capture(receivePurchaserInfoListener)) } just runs + val onResult = mockk() + every { onResult.onReceived(any()) } just runs + every { onResult.onError(any()) } just runs + + logOut(onResult) + + receivePurchaserInfoListener.captured.onError(mockError) + + val mockErrorMap = mockError.map() + verify(exactly = 1) { onResult.onError(mockErrorMap) } + } + } From 6f07ffebcfae74a4e9c3abb0da1c304f911a52cd Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Thu, 15 Jul 2021 15:59:32 -0300 Subject: [PATCH 05/14] re-added identity v3 for iOS --- .../RCCommonFunctionality.h | 16 ++- .../RCCommonFunctionality.m | 32 +++++ .../Mocks/MockPurchases.swift | 34 +++++ .../PurchasesHybridCommonTests.swift | 131 ++++++++++++++++++ 4 files changed, 209 insertions(+), 4 deletions(-) diff --git a/ios/PurchasesHybridCommon/PurchasesHybridCommon/RCCommonFunctionality.h b/ios/PurchasesHybridCommon/PurchasesHybridCommon/RCCommonFunctionality.h index 3ccc6c63..e5e719a7 100644 --- a/ios/PurchasesHybridCommon/PurchasesHybridCommon/RCCommonFunctionality.h +++ b/ios/PurchasesHybridCommon/PurchasesHybridCommon/RCCommonFunctionality.h @@ -20,7 +20,8 @@ typedef void (^RCHybridResponseBlock)(NSDictionary * _Nullable, RCErrorContainer + (void)configure; -+ (void)setAllowSharingStoreAccount:(BOOL)allowSharingStoreAccount; ++ (void)setAllowSharingStoreAccount:(BOOL)allowSharingStoreAccount +__attribute((deprecated("Configure behavior through the RevenueCat dashboard instead."))); + (void)addAttributionData:(NSDictionary *)data network:(NSInteger)network @@ -35,12 +36,19 @@ __attribute((deprecated("Use the set functions instead."))); + (NSString *)appUserID; -+ (void)createAlias:(nullable NSString *)newAppUserId completionBlock:(RCHybridResponseBlock)completion; ++ (void)logInWithAppUserID:(NSString *)appUserId completionBlock:(RCHybridResponseBlock)completion; + ++ (void)logOutWithCompletionBlock:(RCHybridResponseBlock)completion; + ++ (void)createAlias:(nullable NSString *)newAppUserId completionBlock:(RCHybridResponseBlock)completion +__attribute((deprecated("Use logIn instead."))); + (void)identify:(NSString *)appUserId - completionBlock:(RCHybridResponseBlock)completion; + completionBlock:(RCHybridResponseBlock)completion +__attribute((deprecated("Use logIn instead."))); -+ (void)resetWithCompletionBlock:(RCHybridResponseBlock)completion; ++ (void)resetWithCompletionBlock:(RCHybridResponseBlock)completion +__attribute((deprecated("Use logOut instead."))); + (void)setDebugLogsEnabled:(BOOL)enabled; diff --git a/ios/PurchasesHybridCommon/PurchasesHybridCommon/RCCommonFunctionality.m b/ios/PurchasesHybridCommon/PurchasesHybridCommon/RCCommonFunctionality.m index 27a44f09..329272b6 100644 --- a/ios/PurchasesHybridCommon/PurchasesHybridCommon/RCCommonFunctionality.m +++ b/ios/PurchasesHybridCommon/PurchasesHybridCommon/RCCommonFunctionality.m @@ -79,20 +79,52 @@ + (NSString *)appUserID { return RCPurchases.sharedPurchases.appUserID; } + ++ (void)logInWithAppUserID:(NSString *)appUserId completionBlock:(RCHybridResponseBlock)completion { + NSAssert(RCPurchases.sharedPurchases, @"You must call setup first."); + [RCPurchases.sharedPurchases logIn:appUserId + completionBlock:^(RCPurchaserInfo * _Nullable purchaserInfo, + BOOL created, + NSError * _Nullable error) { + if (error) { + completion(nil, [self payloadForError:error withExtraPayload:@{}]); + } else { + completion(@{ + @"purchaserInfo": purchaserInfo.dictionary, + @"created": @(created) + }, nil); + } + }]; +} + ++ (void)logOutWithCompletionBlock:(RCHybridResponseBlock)completion { + NSAssert(RCPurchases.sharedPurchases, @"You must call setup first."); + [RCPurchases.sharedPurchases logOutWithCompletionBlock:[self getPurchaserInfoCompletionBlock:completion]]; +} + + (void)createAlias:(nullable NSString *)newAppUserID completionBlock:(RCHybridResponseBlock)completion { NSAssert(RCPurchases.sharedPurchases, @"You must call setup first."); + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" [RCPurchases.sharedPurchases createAlias:newAppUserID completionBlock:[self getPurchaserInfoCompletionBlock:completion]]; + #pragma GCC diagnostic pop } + (void)identify:(NSString *)appUserID completionBlock:(RCHybridResponseBlock)completion { NSAssert(RCPurchases.sharedPurchases, @"You must call setup first."); + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" [RCPurchases.sharedPurchases identify:appUserID completionBlock:[self getPurchaserInfoCompletionBlock:completion]]; + #pragma GCC diagnostic pop } + (void)resetWithCompletionBlock:(RCHybridResponseBlock)completion { NSAssert(RCPurchases.sharedPurchases, @"You must call setup first."); + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" [RCPurchases.sharedPurchases resetWithCompletionBlock:[self getPurchaserInfoCompletionBlock:completion]]; + #pragma GCC diagnostic pop } + (void)setDebugLogsEnabled:(BOOL)enabled { diff --git a/ios/PurchasesHybridCommon/PurchasesHybridCommonTests/Mocks/MockPurchases.swift b/ios/PurchasesHybridCommon/PurchasesHybridCommonTests/Mocks/MockPurchases.swift index 54d0bac3..8b8d208e 100644 --- a/ios/PurchasesHybridCommon/PurchasesHybridCommonTests/Mocks/MockPurchases.swift +++ b/ios/PurchasesHybridCommon/PurchasesHybridCommonTests/Mocks/MockPurchases.swift @@ -23,6 +23,40 @@ class MockPurchases: Purchases { invokedIsAnonymousGetterCount += 1 return stubbedIsAnonymous } + + var invokedLogInError = false + var invokedLogInCount = 0 + var invokedLogInParameters: (appUserID: String, Void)? + var invokedLogInParametersList = [(appUserID: String, Void)]() + var stubbedLogInCompletionResult: (Purchases.PurchaserInfo?, Bool, Error?)? + + override func logIn(_ appUserID: String, + _ completion: @escaping (Purchases.PurchaserInfo?, Bool, Error?) -> ()) { + invokedLogInError = true + invokedLogInCount += 1 + invokedLogInParameters = (appUserID, ()) + invokedLogInParametersList.append((appUserID, ())) + if let result = stubbedLogInCompletionResult { + completion(result.0, result.1, result.2) + } + } + + var invokedLogOut = false + var invokedLogOutCount = 0 + var invokedLogOutParameters: (completion: Purchases.ReceivePurchaserInfoBlock?, Void)? + var invokedLogOutParametersList = [(completion: Purchases.ReceivePurchaserInfoBlock?, Void)]() + var stubbedLogOutCompletionResult: (Purchases.PurchaserInfo?, Error?)? + + override func logOut(_ completion: Purchases.ReceivePurchaserInfoBlock?) { + invokedLogOut = true + invokedLogOutCount += 1 + invokedLogOutParameters = (completion, ()) + invokedLogOutParametersList.append((completion, ())) + if let completion = completion, let result = stubbedLogOutCompletionResult { + completion(result.0, result.1) + } + } + var invokedCreateAlias = false var invokedCreateAliasCount = 0 var invokedCreateAliasParameters: (alias: String, completion: Purchases.ReceivePurchaserInfoBlock?)? diff --git a/ios/PurchasesHybridCommon/PurchasesHybridCommonTests/PurchasesHybridCommonTests.swift b/ios/PurchasesHybridCommon/PurchasesHybridCommonTests/PurchasesHybridCommonTests.swift index 42afc379..489666d6 100644 --- a/ios/PurchasesHybridCommon/PurchasesHybridCommonTests/PurchasesHybridCommonTests.swift +++ b/ios/PurchasesHybridCommon/PurchasesHybridCommonTests/PurchasesHybridCommonTests.swift @@ -34,5 +34,136 @@ class PurchasesHybridCommonTests: QuickSpec { expect { RCCommonFunctionality.proxyURLString = "not a valid url" }.to(raiseException()) } } + + context("logIn") { + it("passes the call correctly to Purchases") { + let mockPurchases = MockPurchases() + let mockPurchaserInfo = PartialMockPurchaserInfo() + let mockCreated = Bool.random() + mockPurchases.stubbedLogInCompletionResult = (mockPurchaserInfo, mockCreated, nil) + + Purchases.setDefaultInstance(mockPurchases) + + let appUserID = "appUserID" + RCCommonFunctionality.logIn(withAppUserID: appUserID) { _, _ in } + + expect { mockPurchases.invokedLogInCount } == 1 + expect { mockPurchases.invokedLogInParameters?.appUserID } == appUserID + } + + it("returns purchaserInfo and created if successful") { + let mockPurchases = MockPurchases() + let mockPurchaserInfo = PartialMockPurchaserInfo() + let mockCreated = Bool.random() + mockPurchases.stubbedLogInCompletionResult = (mockPurchaserInfo, mockCreated, nil) + + Purchases.setDefaultInstance(mockPurchases) + var receivedResultDict: NSDictionary? + var receivedError: RCErrorContainer? + + let appUserID = "appUserID" + RCCommonFunctionality.logIn(withAppUserID: appUserID) { resultDict, error in + receivedResultDict = resultDict! as NSDictionary + receivedError = error + } + + let expectedResult: NSDictionary = [ + "purchaserInfo": mockPurchaserInfo.dictionary() as NSDictionary, + "created": mockCreated + ] + + expect { receivedResultDict } == expectedResult + expect { receivedError }.to(beNil()) + } + + it("returns error if not successful") { + let mockPurchases = MockPurchases() + let mockError = NSError(domain: "revenuecat", code: 123) + + mockPurchases.stubbedLogInCompletionResult = (nil, false, mockError) + + Purchases.setDefaultInstance(mockPurchases) + var receivedResultDict: NSDictionary? + var receivedError: RCErrorContainer? + + let appUserID = "appUserID" + RCCommonFunctionality.logIn(withAppUserID: appUserID) { resultDict, error in + receivedResultDict = resultDict as NSDictionary? + receivedError = error + } + + let expectedErrorDict: NSDictionary = [ + "code": mockError.code, + "message": mockError.localizedDescription + ] + + expect { receivedResultDict }.to(beNil()) + expect { receivedError }.toNot(beNil()) + expect { receivedError!.error as NSError } == mockError + expect { receivedError!.code } == mockError.code + expect { receivedError!.message } == mockError.localizedDescription + expect { receivedError!.info as NSDictionary } == expectedErrorDict + } + } + + context("logOut") { + it("passes the call correctly to Purchases") { + let mockPurchases = MockPurchases() + let mockPurchaserInfo = PartialMockPurchaserInfo() + mockPurchases.stubbedLogOutCompletionResult = (mockPurchaserInfo, nil) + + Purchases.setDefaultInstance(mockPurchases) + + RCCommonFunctionality.logOut { _, _ in } + + expect { mockPurchases.invokedLogOutCount } == 1 + } + + it("returns purchaserInfo if successful") { + let mockPurchases = MockPurchases() + let mockPurchaserInfo = PartialMockPurchaserInfo() + mockPurchases.stubbedLogOutCompletionResult = (mockPurchaserInfo, nil) + + Purchases.setDefaultInstance(mockPurchases) + var receivedResultDict: NSDictionary? + var receivedError: RCErrorContainer? + + RCCommonFunctionality.logOut { resultDict, error in + receivedResultDict = resultDict! as NSDictionary + receivedError = error + } + expect { receivedResultDict } == mockPurchaserInfo.dictionary() as NSDictionary + expect { receivedError }.to(beNil()) + } + + it("returns error if not successful") { + let mockPurchases = MockPurchases() + let mockError = NSError(domain: "revenuecat", code: 123) + + mockPurchases.stubbedLogOutCompletionResult = (nil, mockError) + + Purchases.setDefaultInstance(mockPurchases) + var receivedResultDict: NSDictionary? + var receivedError: RCErrorContainer? + + RCCommonFunctionality.logOut { resultDict, error in + receivedResultDict = resultDict as NSDictionary? + receivedError = error + } + + let expectedErrorDict: NSDictionary = [ + "code": mockError.code, + "message": mockError.localizedDescription + ] + + expect { receivedResultDict }.to(beNil()) + expect { receivedError }.toNot(beNil()) + expect { receivedError!.error as NSError } == mockError + expect { receivedError!.code } == mockError.code + expect { receivedError!.message } == mockError.localizedDescription + expect { receivedError!.info as NSDictionary } == expectedErrorDict + } + } + } } From 05f18902797f05f6594e5ac492c5500d5e1f4c99 Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Thu, 15 Jul 2021 16:43:29 -0300 Subject: [PATCH 06/14] updated `sku` -> `skus` in purchase completed function --- .../main/java/com/revenuecat/purchases/hybridcommon/common.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/java/com/revenuecat/purchases/hybridcommon/common.kt b/android/src/main/java/com/revenuecat/purchases/hybridcommon/common.kt index 26c8ad03..8704bdc2 100644 --- a/android/src/main/java/com/revenuecat/purchases/hybridcommon/common.kt +++ b/android/src/main/java/com/revenuecat/purchases/hybridcommon/common.kt @@ -328,7 +328,7 @@ private fun getPurchaseCompletedFunction(onResult: OnResult): (Purchase?, Purcha return { purchase, purchaserInfo -> onResult.onReceived( mapOf( - "productIdentifier" to purchase?.sku, + "productIdentifier" to purchase?.skus, "purchaserInfo" to purchaserInfo.map() ) ) From 90a50b3afa03d8a1d239436b6a70c12de02c51bb Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Thu, 15 Jul 2021 16:51:19 -0300 Subject: [PATCH 07/14] updated call to set debug logs enabled to the latest syntax --- .../PurchasesHybridCommon/RCCommonFunctionality.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/PurchasesHybridCommon/PurchasesHybridCommon/RCCommonFunctionality.m b/ios/PurchasesHybridCommon/PurchasesHybridCommon/RCCommonFunctionality.m index 329272b6..8d86dfa3 100644 --- a/ios/PurchasesHybridCommon/PurchasesHybridCommon/RCCommonFunctionality.m +++ b/ios/PurchasesHybridCommon/PurchasesHybridCommon/RCCommonFunctionality.m @@ -128,7 +128,7 @@ + (void)resetWithCompletionBlock:(RCHybridResponseBlock)completion { } + (void)setDebugLogsEnabled:(BOOL)enabled { - RCPurchases.debugLogsEnabled = enabled; + RCPurchases.logLevel = enabled ? RCLogLevelDebug : RCLogLevelInfo; } + (void)setProxyURLString:(nullable NSString *)proxyURLString { From 0d1362cc60c34c3501660a2de96693204b978542 Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Thu, 15 Jul 2021 16:51:26 -0300 Subject: [PATCH 08/14] added a few missing imports --- .../com/revenuecat/purchases/hybridcommon/CommonKtTests.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/android/src/test/java/com/revenuecat/purchases/hybridcommon/CommonKtTests.kt b/android/src/test/java/com/revenuecat/purchases/hybridcommon/CommonKtTests.kt index 2e97eeb6..bb93e256 100644 --- a/android/src/test/java/com/revenuecat/purchases/hybridcommon/CommonKtTests.kt +++ b/android/src/test/java/com/revenuecat/purchases/hybridcommon/CommonKtTests.kt @@ -5,9 +5,13 @@ import android.app.Application import android.content.Context import com.revenuecat.purchases.BillingFeature import com.revenuecat.purchases.Purchases +import com.revenuecat.purchases.PurchaserInfo +import com.revenuecat.purchases.PurchasesError import com.revenuecat.purchases.common.PlatformInfo +import com.revenuecat.purchases.hybridcommon.mappers.map import com.revenuecat.purchases.interfaces.Callback import com.revenuecat.purchases.interfaces.LogInCallback +import com.revenuecat.purchases.interfaces.ReceivePurchaserInfoListener import io.mockk.Runs import io.mockk.every import io.mockk.just From e8dbd0567f872200181f939fe954168d96d34a33 Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Thu, 15 Jul 2021 16:55:28 -0300 Subject: [PATCH 09/14] updated reference to old method in RCCommonFunctionality --- .../PurchasesHybridCommon/RCCommonFunctionality.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ios/PurchasesHybridCommon/PurchasesHybridCommon/RCCommonFunctionality.m b/ios/PurchasesHybridCommon/PurchasesHybridCommon/RCCommonFunctionality.m index 8d86dfa3..235625ae 100644 --- a/ios/PurchasesHybridCommon/PurchasesHybridCommon/RCCommonFunctionality.m +++ b/ios/PurchasesHybridCommon/PurchasesHybridCommon/RCCommonFunctionality.m @@ -87,7 +87,9 @@ + (void)logInWithAppUserID:(NSString *)appUserId completionBlock:(RCHybridRespon BOOL created, NSError * _Nullable error) { if (error) { - completion(nil, [self payloadForError:error withExtraPayload:@{}]); + RCErrorContainer *errorContainer = [[RCErrorContainer alloc] initWithError:error + extraPayload:@{}]; + completion(nil, errorContainer); } else { completion(@{ @"purchaserInfo": purchaserInfo.dictionary, From 6b1fe47cf106e553a1f4e4a2cbdea5d6dd64b78f Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Thu, 15 Jul 2021 16:56:54 -0300 Subject: [PATCH 10/14] updated tests to include `underlyingErrorMessage` --- .../PurchasesHybridCommonTests.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ios/PurchasesHybridCommon/PurchasesHybridCommonTests/PurchasesHybridCommonTests.swift b/ios/PurchasesHybridCommon/PurchasesHybridCommonTests/PurchasesHybridCommonTests.swift index 489666d6..70f7615a 100644 --- a/ios/PurchasesHybridCommon/PurchasesHybridCommonTests/PurchasesHybridCommonTests.swift +++ b/ios/PurchasesHybridCommon/PurchasesHybridCommonTests/PurchasesHybridCommonTests.swift @@ -94,7 +94,9 @@ class PurchasesHybridCommonTests: QuickSpec { let expectedErrorDict: NSDictionary = [ "code": mockError.code, - "message": mockError.localizedDescription + "message": mockError.localizedDescription, + "underlyingErrorMessage": "" + ] expect { receivedResultDict }.to(beNil()) @@ -153,7 +155,8 @@ class PurchasesHybridCommonTests: QuickSpec { let expectedErrorDict: NSDictionary = [ "code": mockError.code, - "message": mockError.localizedDescription + "message": mockError.localizedDescription, + "underlyingErrorMessage": "" ] expect { receivedResultDict }.to(beNil()) From 410b541c0e8a500316fea3d62a4d2eb9cd48c582 Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Thu, 15 Jul 2021 17:02:30 -0300 Subject: [PATCH 11/14] updated xcode version for CI --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 20098712..a3f3c066 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -19,7 +19,7 @@ executors: ios-executor: working_directory: ~/ios macos: - xcode: 12.0 + xcode: 12.5.1 jobs: test-ios: From 87097a763d034a778545dcfc318a1857f674153e Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Thu, 15 Jul 2021 17:13:47 -0300 Subject: [PATCH 12/14] added deprecation notices for identity v2 methods --- .../revenuecat/purchases/hybridcommon/common.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/android/src/main/java/com/revenuecat/purchases/hybridcommon/common.kt b/android/src/main/java/com/revenuecat/purchases/hybridcommon/common.kt index 8704bdc2..189b4a3c 100644 --- a/android/src/main/java/com/revenuecat/purchases/hybridcommon/common.kt +++ b/android/src/main/java/com/revenuecat/purchases/hybridcommon/common.kt @@ -28,6 +28,10 @@ import com.revenuecat.purchases.interfaces.Callback import java.net.URL +@Deprecated( + "Replaced with configuration in the RevenueCat dashboard", + ReplaceWith("configure through the RevenueCat dashboard") +) fun setAllowSharingAppStoreAccount( allowSharingAppStoreAccount: Boolean ) { @@ -205,6 +209,10 @@ fun logOut(onResult: OnResult) { } } +@Deprecated( + "Use logOut instead", + ReplaceWith("CommonKt.logOut(newAppUserID, onResult)") +) fun reset( onResult: OnResult ) { @@ -213,6 +221,10 @@ fun reset( } } +@Deprecated( + "Use logIn instead", + ReplaceWith("CommonKt.logIn(newAppUserID, onResult)") +) fun identify( appUserID: String, onResult: OnResult @@ -222,6 +234,10 @@ fun identify( } } +@Deprecated( + "Use logIn instead", + ReplaceWith("CommonKt.logIn(newAppUserID, onResult)") +) fun createAlias( newAppUserID: String, onResult: OnResult From 9393fd3ddaf71250b99f9a070307bb03a5429e53 Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Mon, 19 Jul 2021 11:16:39 -0300 Subject: [PATCH 13/14] updated purchases-ios version to 3.12.1 --- CHANGELOG.md | 2 +- PurchasesHybridCommon.podspec | 2 +- ios/PurchasesHybridCommon/Podfile | 2 +- ios/PurchasesHybridCommon/Podfile.lock | 14 +++++++------- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1012f5fc..c256f3bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ - deprecates `allowSharingStoreAccount` in favor of dashboard-side configuration #### Dependency updates -- Bumps `purchases-ios` to `3.12.0` ([Changelog here](/~https://github.com/RevenueCat/purchases-ios/releases/3.12.0)) +- Bumps `purchases-ios` to `3.12.1` ([Changelog here](/~https://github.com/RevenueCat/purchases-ios/releases/3.12.1)) - Bumps `purchases-android` to `4.3.0` ([Changelog here](/~https://github.com/RevenueCat/purchases-android/releases/4.3.0)) /~https://github.com/RevenueCat/purchases-hybrid-common/pull/84 diff --git a/PurchasesHybridCommon.podspec b/PurchasesHybridCommon.podspec index 04cdb20a..dd4c3f91 100644 --- a/PurchasesHybridCommon.podspec +++ b/PurchasesHybridCommon.podspec @@ -15,7 +15,7 @@ Pod::Spec.new do |s| s.framework = 'StoreKit' - s.dependency 'Purchases', '3.12.0' + s.dependency 'Purchases', '3.12.1' s.swift_version = '5.0' s.ios.deployment_target = '9.0' diff --git a/ios/PurchasesHybridCommon/Podfile b/ios/PurchasesHybridCommon/Podfile index 7f21cfb6..041b3333 100644 --- a/ios/PurchasesHybridCommon/Podfile +++ b/ios/PurchasesHybridCommon/Podfile @@ -6,7 +6,7 @@ target 'PurchasesHybridCommon' do use_frameworks! # Pods for PurchasesHybridCommon - pod 'Purchases', '3.12.0' + pod 'Purchases', '3.12.1' target 'PurchasesHybridCommonTests' do # Pods for testing diff --git a/ios/PurchasesHybridCommon/Podfile.lock b/ios/PurchasesHybridCommon/Podfile.lock index 996e139e..180f0faa 100644 --- a/ios/PurchasesHybridCommon/Podfile.lock +++ b/ios/PurchasesHybridCommon/Podfile.lock @@ -1,13 +1,13 @@ PODS: - Nimble (9.0.1) - - Purchases (3.12.0): - - PurchasesCoreSwift (= 3.12.0) - - PurchasesCoreSwift (3.12.0) + - Purchases (3.12.1): + - PurchasesCoreSwift (= 3.12.1) + - PurchasesCoreSwift (3.12.1) - Quick (3.1.2) DEPENDENCIES: - Nimble - - Purchases (= 3.12.0) + - Purchases (= 3.12.1) - Quick SPEC REPOS: @@ -19,10 +19,10 @@ SPEC REPOS: SPEC CHECKSUMS: Nimble: 7bed62ffabd6dbfe05f5925cbc43722533248990 - Purchases: 1707e34353a006f57aa9250ca1e2dfa1b48ecafb - PurchasesCoreSwift: 6672e412019b8d3b987c3ee04b07eda117125c39 + Purchases: 2b5ce82613e3742e354a108444006d7d20bf9773 + PurchasesCoreSwift: 62208989a2768755da2d8162db5d94b9f91688f6 Quick: 60f0ea3b8e0cfc0df3259a5c06a238ad8b3c46e0 -PODFILE CHECKSUM: 08aea1984bc62b0cbde1ffc5e11d597e6f04151a +PODFILE CHECKSUM: d2227f20e900c5bed37ccd30aa2ca7dbf1d221e5 COCOAPODS: 1.10.1 From 17f9fc93d0779734696451c95f8723f286beddfa Mon Sep 17 00:00:00 2001 From: Andy Boedo Date: Mon, 19 Jul 2021 15:54:30 -0300 Subject: [PATCH 14/14] updated purchases-ios version to 3.12.2 --- CHANGELOG.md | 2 +- PurchasesHybridCommon.podspec | 2 +- ios/PurchasesHybridCommon/Podfile | 2 +- ios/PurchasesHybridCommon/Podfile.lock | 14 +++++++------- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c256f3bd..cc68934f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ - deprecates `allowSharingStoreAccount` in favor of dashboard-side configuration #### Dependency updates -- Bumps `purchases-ios` to `3.12.1` ([Changelog here](/~https://github.com/RevenueCat/purchases-ios/releases/3.12.1)) +- Bumps `purchases-ios` to `3.12.2` ([Changelog here](/~https://github.com/RevenueCat/purchases-ios/releases/3.12.2)) - Bumps `purchases-android` to `4.3.0` ([Changelog here](/~https://github.com/RevenueCat/purchases-android/releases/4.3.0)) /~https://github.com/RevenueCat/purchases-hybrid-common/pull/84 diff --git a/PurchasesHybridCommon.podspec b/PurchasesHybridCommon.podspec index dd4c3f91..185e66f7 100644 --- a/PurchasesHybridCommon.podspec +++ b/PurchasesHybridCommon.podspec @@ -15,7 +15,7 @@ Pod::Spec.new do |s| s.framework = 'StoreKit' - s.dependency 'Purchases', '3.12.1' + s.dependency 'Purchases', '3.12.2' s.swift_version = '5.0' s.ios.deployment_target = '9.0' diff --git a/ios/PurchasesHybridCommon/Podfile b/ios/PurchasesHybridCommon/Podfile index 041b3333..e7568fe2 100644 --- a/ios/PurchasesHybridCommon/Podfile +++ b/ios/PurchasesHybridCommon/Podfile @@ -6,7 +6,7 @@ target 'PurchasesHybridCommon' do use_frameworks! # Pods for PurchasesHybridCommon - pod 'Purchases', '3.12.1' + pod 'Purchases', '3.12.2' target 'PurchasesHybridCommonTests' do # Pods for testing diff --git a/ios/PurchasesHybridCommon/Podfile.lock b/ios/PurchasesHybridCommon/Podfile.lock index 180f0faa..d1384f21 100644 --- a/ios/PurchasesHybridCommon/Podfile.lock +++ b/ios/PurchasesHybridCommon/Podfile.lock @@ -1,13 +1,13 @@ PODS: - Nimble (9.0.1) - - Purchases (3.12.1): - - PurchasesCoreSwift (= 3.12.1) - - PurchasesCoreSwift (3.12.1) + - Purchases (3.12.2): + - PurchasesCoreSwift (= 3.12.2) + - PurchasesCoreSwift (3.12.2) - Quick (3.1.2) DEPENDENCIES: - Nimble - - Purchases (= 3.12.1) + - Purchases (= 3.12.2) - Quick SPEC REPOS: @@ -19,10 +19,10 @@ SPEC REPOS: SPEC CHECKSUMS: Nimble: 7bed62ffabd6dbfe05f5925cbc43722533248990 - Purchases: 2b5ce82613e3742e354a108444006d7d20bf9773 - PurchasesCoreSwift: 62208989a2768755da2d8162db5d94b9f91688f6 + Purchases: 3e20881892483ab6ca17cb86a89af4c6d1731562 + PurchasesCoreSwift: e614645840af49d465dee1918011f784365d79a6 Quick: 60f0ea3b8e0cfc0df3259a5c06a238ad8b3c46e0 -PODFILE CHECKSUM: d2227f20e900c5bed37ccd30aa2ca7dbf1d221e5 +PODFILE CHECKSUM: 7ef4e82b85b2be3e865d9a27a6c226c60ef4bc98 COCOAPODS: 1.10.1