Skip to content

Commit

Permalink
Create separate SPM library to enable custom entitlement computation (
Browse files Browse the repository at this point in the history
#2440)

Fixes SDK-3084

![screenshot_2023-04-25_at_10 02
44_480](https://user-images.githubusercontent.com/685609/234360670-bd2628a1-0684-490d-a9eb-33846a60f72a.png)

## Changes:

- Added new `ENABLE_CUSTOM_ENTITLEMENT_COMPUTATION` compile time flag
- Hidden `configureInCustomEntitlementsModeWithApiKey` behind
`ENABLE_CUSTOM_ENTITLEMENT_COMPUTATION`
- Added new `RevenueCat_CustomEntitlementComputation` SPM target and
library
- Added CircleCI job for `swift build --target
RevenueCat_CustomEntitlementComputation`
- Added new `SPMCustomEntitlementComputationInstallation` using the new
`configureInCustomEntitlementsComputationMode`
- Added CircleCI job for `SPMCustomEntitlementComputationInstallation`
- Added `FrameworkDisambiguation.swift` to avoid using `RevenueCat.`
disambiguation for files that
- Changed API testers to only use the new methods if
`ENABLE_CUSTOM_ENTITLEMENT_COMPUTATION` is defined
- Updated `DangerousSettings` to not expose
`customEntitlementComputation` publicly. The only way to use that now
with `public` API is through
`Purchases.configureInCustomEntitlementsModeWithApiKey`
- Updated sample app to use new library
  • Loading branch information
NachoSoto authored Apr 25, 2023
1 parent 618ff12 commit 64c8638
Show file tree
Hide file tree
Showing 30 changed files with 998 additions and 55 deletions.
44 changes: 44 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,24 @@ commands:
working_directory: << parameters.directory >>
when: always

scan:
parameters:
directory:
type: string
steps:
- run:
name: Replace API key
command: bundle exec fastlane replace_api_key_integration_tests
- run:
name: Run tests
working_directory: << parameters.directory >>
command: bundle exec fastlane scan
- store_test_results:
path: fastlane/test_output
- store_artifacts:
path: fastlane/test_output/report.html
destination: test_report.html

scan-and-archive:
parameters:
directory:
Expand Down Expand Up @@ -269,6 +287,15 @@ jobs:
command: swift build -c release --target RevenueCat
no_output_timeout: 30m

spm-custom-entitlement-computation-build:
<<: *base-job
steps:
- checkout
- run:
name: SPM Custom Entitlement Computation Build
command: swift build --target RevenueCat_CustomEntitlementComputation
no_output_timeout: 30m

spm-receipt-parser:
<<: *base-job
steps:
Expand Down Expand Up @@ -551,6 +578,17 @@ jobs:
- scan-and-archive-all-platforms:
directory: Tests/InstallationTests/SPMInstallation/

installation-tests-custom-entitlement-computation-swift-package-manager:
<<: *base-job
steps:
- checkout
- trust-github-key
- update-spm-installation-commit
- install-dependencies:
directory: Tests/InstallationTests/SPMCustomEntitlementComputationInstallation/
- scan:
directory: Tests/InstallationTests/SPMCustomEntitlementComputationInstallation/

installation-tests-receipt-parser:
<<: *base-job
steps:
Expand Down Expand Up @@ -732,6 +770,8 @@ workflows:
xcode_version: '14.3.0'
- spm-release-build:
xcode_version: '14.3.0'
- spm-custom-entitlement-computation-build:
xcode_version: '14.3.0'
- spm-receipt-parser:
xcode_version: '14.3.0'
- run-test-ios-16:
Expand Down Expand Up @@ -775,6 +815,9 @@ workflows:
- installation-tests-swift-package-manager:
xcode_version: '14.3.0'
<<: *release-branches
- installation-tests-custom-entitlement-computation-swift-package-manager:
xcode_version: '14.3.0'
<<: *release-branches
- installation-tests-receipt-parser:
xcode_version: '14.1.0'
<<: *release-branches
Expand All @@ -790,6 +833,7 @@ workflows:
- release-checks
- installation-tests-cocoapods
- installation-tests-swift-package-manager
- installation-tests-custom-entitlement-computation-swift-package-manager
- installation-tests-carthage
- installation-tests-xcode-direct-integration
- installation-tests-receipt-parser
Expand Down
1 change: 1 addition & 0 deletions CustomEntitlementComputation
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
2D276F5A29F1C85800F51382 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2D276F5929F1C85800F51382 /* Assets.xcassets */; };
2D276F5D29F1C85800F51382 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2D276F5C29F1C85800F51382 /* Preview Assets.xcassets */; };
2DB0898929F30ABD00A4DAE7 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DB0898829F30ABD00A4DAE7 /* Constants.swift */; };
2DB0899329F32C9F00A4DAE7 /* RevenueCat in Frameworks */ = {isa = PBXBuildFile; productRef = 2DB0899229F32C9F00A4DAE7 /* RevenueCat */; };
5714383029F86B4400CC2D44 /* RevenueCat_CustomEntitlementComputation in Frameworks */ = {isa = PBXBuildFile; productRef = 5714382F29F86B4400CC2D44 /* RevenueCat_CustomEntitlementComputation */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand All @@ -30,7 +30,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
2DB0899329F32C9F00A4DAE7 /* RevenueCat in Frameworks */,
5714383029F86B4400CC2D44 /* RevenueCat_CustomEntitlementComputation in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -99,7 +99,7 @@
);
name = testCustomEntitlementsComputation;
packageProductDependencies = (
2DB0899229F32C9F00A4DAE7 /* RevenueCat */,
5714382F29F86B4400CC2D44 /* RevenueCat_CustomEntitlementComputation */,
);
productName = testCustomEntitlementsComputation;
productReference = 2D276F5229F1C85700F51382 /* testCustomEntitlementsComputation.app */;
Expand Down Expand Up @@ -362,9 +362,9 @@
/* End XCConfigurationList section */

/* Begin XCSwiftPackageProductDependency section */
2DB0899229F32C9F00A4DAE7 /* RevenueCat */ = {
5714382F29F86B4400CC2D44 /* RevenueCat_CustomEntitlementComputation */ = {
isa = XCSwiftPackageProductDependency;
productName = RevenueCat;
productName = RevenueCat_CustomEntitlementComputation;
};
/* End XCSwiftPackageProductDependency section */
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//

import SwiftUI
import RevenueCat
import RevenueCat_CustomEntitlementComputation

struct CustomerInfoStreamFires: Identifiable {
let customerInfo: CustomerInfo
Expand Down
6 changes: 6 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ let package = Package(
products: [
.library(name: "RevenueCat",
targets: ["RevenueCat"]),
.library(name: "RevenueCat_CustomEntitlementComputation",
targets: ["RevenueCat_CustomEntitlementComputation"]),
.library(name: "ReceiptParser",
targets: ["ReceiptParser"])
],
Expand All @@ -35,6 +37,10 @@ let package = Package(
.target(name: "RevenueCat",
path: "Sources",
exclude: ["Info.plist", "LocalReceiptParsing/ReceiptParser-only-files"]),
.target(name: "RevenueCat_CustomEntitlementComputation",
path: "CustomEntitlementComputation",
exclude: ["Info.plist", "LocalReceiptParsing/ReceiptParser-only-files"],
swiftSettings: [.define("ENABLE_CUSTOM_ENTITLEMENT_COMPUTATION")]),
.target(name: "ReceiptParser",
path: "LocalReceiptParsing"),
.testTarget(name: "ReceiptParserTests", dependencies: ["ReceiptParser", "Nimble"])
Expand Down
4 changes: 4 additions & 0 deletions RevenueCat.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@
57BB071628D282A4007F5DF0 /* CachingProductsManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57BB071528D282A4007F5DF0 /* CachingProductsManagerTests.swift */; };
57BD50AA27692B7500211D6D /* StoreKitError+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57BD50A927692B7500211D6D /* StoreKitError+Extensions.swift */; };
57BF87592967880C00424254 /* MockCachingTrialOrIntroPriceEligibilityChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57BF87582967880C00424254 /* MockCachingTrialOrIntroPriceEligibilityChecker.swift */; };
57C0BB6929F840BD00827807 /* FrameworkDisambiguation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57C0BB6829F840BD00827807 /* FrameworkDisambiguation.swift */; };
57C2931528BFEF4F0054EDFC /* PurchasesError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57C2931428BFEF4F0054EDFC /* PurchasesError.swift */; };
57C2932A28BFF89D0054EDFC /* ErrorMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57E4A52028BD8F610095C793 /* ErrorMatcher.swift */; };
57C381B72791E593009E3940 /* StoreKit2TransactionListenerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57C381B62791E593009E3940 /* StoreKit2TransactionListenerTests.swift */; };
Expand Down Expand Up @@ -1018,6 +1019,7 @@
57BB071528D282A4007F5DF0 /* CachingProductsManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CachingProductsManagerTests.swift; sourceTree = "<group>"; };
57BD50A927692B7500211D6D /* StoreKitError+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StoreKitError+Extensions.swift"; sourceTree = "<group>"; };
57BF87582967880C00424254 /* MockCachingTrialOrIntroPriceEligibilityChecker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockCachingTrialOrIntroPriceEligibilityChecker.swift; sourceTree = "<group>"; };
57C0BB6829F840BD00827807 /* FrameworkDisambiguation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FrameworkDisambiguation.swift; sourceTree = "<group>"; };
57C2931428BFEF4F0054EDFC /* PurchasesError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PurchasesError.swift; sourceTree = "<group>"; };
57C381B62791E593009E3940 /* StoreKit2TransactionListenerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreKit2TransactionListenerTests.swift; sourceTree = "<group>"; };
57C381D92796153D009E3940 /* SK1StoreProductDiscount.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SK1StoreProductDiscount.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1905,6 +1907,7 @@
35E840C5270FB47C00899AE2 /* ManageSubscriptionsHelper.swift */,
578C5F2B28DB82DD00A56F02 /* PurchasesDiagnostics.swift */,
57F2C60B29784C11009EE527 /* SwiftVersionCheck.swift */,
57C0BB6829F840BD00827807 /* FrameworkDisambiguation.swift */,
);
path = Support;
sourceTree = "<group>";
Expand Down Expand Up @@ -3000,6 +3003,7 @@
B35042C426CDB79A00905B95 /* Purchases.swift in Sources */,
2D22BF6526F3CB31001AE2F9 /* FatalErrorUtil.swift in Sources */,
2D1015DE275A57FC0086173F /* SubscriptionPeriod.swift in Sources */,
57C0BB6929F840BD00827807 /* FrameworkDisambiguation.swift in Sources */,
B3B5FBBF269E081E00104A0C /* InMemoryCachedObject.swift in Sources */,
579B67F428C5326A0094F7E8 /* PaymentQueueWrapper.swift in Sources */,
B3DDB55926854865008CCF23 /* PurchaseOwnershipType.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ Most features require configuring the SDK before using it.
- ``Purchases/setOnesignalID(_:)``

### Advanced Configuration
- ``Purchases/configureInCustomEntitlementsComputationMode(apiKey:appUserID:)``
- ``Purchases/finishTransactions``
- ``Purchases/invalidateCustomerInfoCache()``
- ``Purchases/forceUniversalAppStore``
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,6 @@ Or browse our iOS sample apps:
- ``Purchases/configure(with:)-6oipy``
- ``PurchasesDiagnostics``

### Advanced Configuration
- ``Purchases/configureInCustomEntitlementsComputationMode(apiKey:appUserID:)``

### Displaying Products
- ``Offerings``
- ``Offering``
Expand Down
16 changes: 8 additions & 8 deletions Sources/Logging/Logger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ struct Logger {
static var logHandler: VerboseLogHandler = Self.defaultLogHandler

static let defaultLogHandler: VerboseLogHandler = { level, message, file, functionName, line in
RevenueCat.defaultLogHandler(
framework: Self.frameworkDescription,
verbose: Logger.verbose,
level: level,
message: message,
file: file,
function: functionName,
line: line
RCDefaultLogHandler(
Self.frameworkDescription,
Logger.verbose,
level,
message,
file,
functionName,
line
)
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/Misc/Concurrency/Purchases+async.swift
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ extension Purchases {
forProductDiscount: discount,
product: product
)
} catch RevenueCat.ErrorCode.ineligibleError {
} catch RCErrorCode.ineligibleError {
return nil
} catch {
Logger.error(
Expand Down
10 changes: 9 additions & 1 deletion Sources/Misc/DangerousSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,15 @@ import Foundation
* If this is disabled, RevenueCat won't observe the StoreKit queue, and it will not sync any purchase
* automatically.
*/
@objc public convenience init(autoSyncPurchases: Bool = true, customEntitlementComputation: Bool = false) {
@objc public convenience init(autoSyncPurchases: Bool = true) {
self.init(autoSyncPurchases: autoSyncPurchases,
customEntitlementComputation: false)

}

/// - Note: this is `internal` only so the only `public` way to enable `customEntitlementComputation`
/// is through ``Purchases/configureInCustomEntitlementsComputationMode(apiKey:appUserID:)``.
@objc internal convenience init(autoSyncPurchases: Bool = true, customEntitlementComputation: Bool) {
self.init(autoSyncPurchases: autoSyncPurchases,
customEntitlementComputation: customEntitlementComputation,
internalSettings: .default)
Expand Down
72 changes: 41 additions & 31 deletions Sources/Purchasing/Purchases/Purchases.swift
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,8 @@ public extension Purchases {
return try await logOutAsync()
}

#if ENABLE_CUSTOM_ENTITLEMENT_COMPUTATION

///
/// Updates the current appUserID to a new one, without associating the two.
/// - Important: This method is **only available** in Custom Entitlements Computation mode.
Expand All @@ -657,6 +659,8 @@ public extension Purchases {
self.identityManager.switchUser(to: newAppUserID)
}

#endif

@objc func getOfferings(completion: @escaping (Offerings?, PublicError?) -> Void) {
self.getOfferings(fetchPolicy: .default, completion: completion)
}
Expand Down Expand Up @@ -990,6 +994,8 @@ public extension Purchases {
return Self.configure(with: builder.build())
}

#if !ENABLE_CUSTOM_ENTITLEMENT_COMPUTATION

/**
* Configures an instance of the Purchases SDK with a specified API key.
*
Expand Down Expand Up @@ -1032,6 +1038,40 @@ public extension Purchases {
Self.configure(withAPIKey: apiKey, appUserID: appUserID, observerMode: false)
}

/**
* Configures an instance of the Purchases SDK with a custom `UserDefaults`.
*
* Use this constructor if you want to
* sync status across a shared container, such as between a host app and an extension. The instance of the
* Purchases SDK will be set as a singleton.
* You should access the singleton instance using ``Purchases/shared``
*
* - Parameter apiKey: The API Key generated for your app from https://app.revenuecat.com/
*
* - Parameter appUserID: The unique app user id for this user. This user id will allow users to share their
* purchases and subscriptions across devices. Pass `nil` or an empty string if you want ``Purchases``
* to generate this for you.
*
* - Parameter observerMode: Set this to `true` if you have your own IAP implementation and want to use only
* RevenueCat's backend. Default is `false`.
*
* - Returns: An instantiated ``Purchases`` object that has been set as a singleton.
*/
@objc(configureWithAPIKey:appUserID:observerMode:)
@discardableResult static func configure(withAPIKey apiKey: String,
appUserID: String?,
observerMode: Bool) -> Purchases {
Self.configure(
with: Configuration
.builder(withAPIKey: apiKey)
.with(appUserID: appUserID)
.with(observerMode: observerMode)
.build()
)
}

#else

/**
* Configures an instance of the Purchases SDK with a specified API key and
* app user ID in Custom Entitlements Computation mode.
Expand Down Expand Up @@ -1075,37 +1115,7 @@ public extension Purchases {
.build())
}

/**
* Configures an instance of the Purchases SDK with a custom `UserDefaults`.
*
* Use this constructor if you want to
* sync status across a shared container, such as between a host app and an extension. The instance of the
* Purchases SDK will be set as a singleton.
* You should access the singleton instance using ``Purchases/shared``
*
* - Parameter apiKey: The API Key generated for your app from https://app.revenuecat.com/
*
* - Parameter appUserID: The unique app user id for this user. This user id will allow users to share their
* purchases and subscriptions across devices. Pass `nil` or an empty string if you want ``Purchases``
* to generate this for you.
*
* - Parameter observerMode: Set this to `true` if you have your own IAP implementation and want to use only
* RevenueCat's backend. Default is `false`.
*
* - Returns: An instantiated ``Purchases`` object that has been set as a singleton.
*/
@objc(configureWithAPIKey:appUserID:observerMode:)
@discardableResult static func configure(withAPIKey apiKey: String,
appUserID: String?,
observerMode: Bool) -> Purchases {
Self.configure(
with: Configuration
.builder(withAPIKey: apiKey)
.with(appUserID: appUserID)
.with(observerMode: observerMode)
.build()
)
}
#endif

// swiftlint:disable:next function_parameter_count
@discardableResult internal static func configure(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ private extension SK2BeginRefundRequestHelper {
* to our `RefundRequestStatus` type and adding more descriptive error messages where needed.
*/
func mapSk2Result(from sk2Result: Result<StoreKit.Transaction.RefundRequestStatus, Error>) throws ->
RevenueCat.RefundRequestStatus {
RCRefundRequestStatus {
switch sk2Result {
case .success(let sk2Status):
guard let rcStatus = RefundRequestStatus.from(sk2RefundRequestStatus: sk2Status) else {
Expand Down
26 changes: 26 additions & 0 deletions Sources/Support/FrameworkDisambiguation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// Copyright RevenueCat Inc. All Rights Reserved.
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/MIT
//
// FrameworkDisambiguation.swift
//
// Created by Nacho Soto on 4/25/23.w

/**
Purpose: this file is needed because several parts of the SDK need to explicitily reference a type or value
from the `RevenueCat` target. However, we expose 2 variants of the framework from SPM:
`RevenueCat` and `RevenueCat_CustomEntitlementComputation` (see `Package.swift`).
Because of that, we can't simply do `RevenueCat.ErrorCode` for example, since the other variant
would need `RevenueCat_CustomEntitlementComputation.ErrorCode`.

To handle that, this exposes those types explicitly so they work regardless of the name of the framework.
*/

typealias RCRefundRequestStatus = RefundRequestStatus
typealias RCErrorCode = ErrorCode
let RCDefaultLogHandler = defaultLogHandler
Loading

0 comments on commit 64c8638

Please sign in to comment.