### Motivation
Get more context with targeting information
### Description
- Parse targeting information into `Offerings`
- Add targeting context into `Offerings.current`
- Add targeting context into `getCurrentOffering(forPlacement: String)`
- Post targeting context in post receipts
---------
Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com>
### Motivation
New function to get `Offering` by a placement identifier
### Description
New `currentOffering(forPlacement: String): Offering?` method on
`Offerings`
- Added demo button for this in purchase tester app
```swift
// Example usage
if let offering = offerings.currentOffering(forPlacement: "onboarding") {
showPaywall(offering)
}
```
#### Logic flow
- If a placement identifier explicitly exists within the
`placements.offeringIdsByPlacement` dictionary, the method attempts to
fetch the corresponding `Offering` without resorting to a fallback
- Example: when there is a `nil` / null value for a placement, we don't
want to use the fallback
- If the placement identifier does not exist, the method then attempts
to fetch a fallback offering defined by `placements.fallbackOfferingId`
#### New placements object in offerings response
```js
{
// other offerings stuff
"placements": {
"fallback_offering_id": "offering_a", // can be null
"offering_ids_by_placement": {
"onboarding_start": null,
"onboarding_end": "offering_b",
}
}
}
```
---------
Co-authored-by: Guido Torres <guido@revenuecat.com>
Co-authored-by: Josh Holtz <me@joshholtz.com>
Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com>
I've moved `PaywallViewMode` from `RevenueCatUI` to `RevenueCat`. This
uses the same approach as #2645. This method is `public` so it can be
used from `RevenueCatUI`, but the documentation stays its purpose.
The behavior is covered with integration tests and a snapshot test.
Depends on https://github.com/RevenueCat/khepri/pull/6364.
This paves the way to add a second set of paths with a separate server
URL to be able to shed requests to offerings and product entitlement
mapping to a separate server.
### Changes:
- New `HTTPRequestPath` with the same properties as `HTTPRequest.Path` +
a new `serverHostURL`
- Moved proxy logic from `SystemInfo`/`HTTPClient` to `HTTPRequestPath`
- Simplified `HTTPRequest` implementation
### Changes:
- `HTTPClient` has a new `X-Post-Params-Hash` request header
- Replaced `HTTPRequest.Method.post`'s `Encodable` value with a more
specific `HTTPRequestBody` (like `HTTPResponseBody`)
- `HTTPRequestBody` allows overriding `contentForSignature` (it defaults
to `[]`) which then `Signing` uses for the hash
- Fixed `LogInOperation` not passing the `VerificationResult` to the
result `CustomerInfo`. This is captured in unit and integration tests
now.
- Add support for signing `POST` requests in `Path.logIn` and
`Path.postReceiptData`
Depends on https://github.com/RevenueCat/khepri/pull/6246
Follow up to #2540.
This was necessary to ensure that we can implement #2533 without
reference cycles.
This was the hierarchy before this change:

The red arrows are what this PR removes.
## Changes:
- `TransactionPoster` no longer has references to `IdentityManager` and
`Attribution`
- `markSyncedIfNeeded` is no longer in `TransactionPoster`, moved back
to `PurchasesOrchestrator`
- Changed `TransactionPoster`'s callback to return `Result<CustomerInfo,
BackendError>`, providing greater flexibility for users to be able to
read the specific errors.
- Extracted `PurchasedTransactionData` to be reduce the number of
parameters passed around. This removed the need for 2
`swiftlint:disable:next function_parameter_count`. This is the majority
of this diff. It was helpful to combine in this PR because I needed to
add another parameter to the flow: `unsyncedAttributes`, since
`TransactionPoster` could no longer read these directly.
This was necessary for a follow-up refactor to #2540. The main change is
in `Backend`, the rest are test changes that are required now that the
closure is `@Sendable`.
This will be logged when doing so:
```
ERROR: 😿❗❗ Error computing offline CustomerInfo. Will return original error.
Creation error: Unable to compute offline CustomerInfo when purchasing consumable products.
```
### Changes:
- Added unit tests for `PostReceiptDataOperation`
- Added integration test for this new scenario
- `PostReceiptDataOperation` won't use `OfflineCustomerInfoCreator` when
purchasing consumable products
### Changes:
- Added `Purchases.configureInCustomEntitlementsComputationMode`
- Added `Purchases.switchUser(to:)`
- Disabled parts of the SDK in this new mode
- New example app for testing `customEntitlementsComputation` mode
---------
Co-authored-by: NachoSoto <ignaciosoto90@gmail.com>
See also #2397.
Xcode 14.3 introduced the following error:
> Heterogeneous collection literal could only be inferred to '[String :
Any]'; add explicit type annotation if this is intentional
### Changes:
- Added explicit types to fix this new error
- Removed unnecessary `@preconcurrency` imports
- Added new `CustomerInfo.emptyInfo` to reduce duplication of same fake
`CustomerInfo` creation
Fixes [CSDK-629].
Depends on #2274 and #2267.
Entitlement verification is accomplished by preventing MiTM attacks
between the SDK and the RevenueCat server.
With verification enabled, the SDK ensures that the response created by
the server was not modified by a third-party, and the entitlements
received are exactly what was sent.
Entitlements are only verified if enabled using
`Configuration.Builder.with(entitlementVerificationMode:)`, which is
disabled by default.
### Changes:
- Added new `VerificationResult`, which replaces
`HTTPResponseVerificationResult`
- Renamed `Configuration.EntitlementVerificationLevel` to
`Configuration.EntitlementVerificationMode`
- `Configuration.with(entitlementVerificationMode:)` is now `public`
- Added `CustomerInfo.entitlementVerification`
- Added `EntitlementInfos.verification` and
`EntitlementInfo.verification`
- Added tests to cover serialization of these new values
- `CustomerInfoResponseHandler` copies the `ValidationResult` to
`CustomerInfo` (all operations that fetch `CustomerInfo` use this)
- Renamed "validation" to "verification" everywhere for consistency
- Finalized documentation
### TODO:
- [x] Figure out whether to use a separate type from
`HTTPResponseValidationResult` or rename that one
- [x] Figure out final API type and property names
- [x] Add tests for `CustomerInfo.copy(with:)`
- [x] Ensure validation result is encoded and decoded (for caching)
- [x] Add missing snapshots
[CSDK-629]:
https://revenuecats.atlassian.net/browse/CSDK-629?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
Example:
```diff
- var result: Result<Offerings, OfferingsManager.Error>?
- offeringsManager.offerings(appUserID: MockData.anyAppUserID) {
- result = $0
+ let result = waitUntilValue { completed in
+ self.offeringsManager.offerings(appUserID: MockData.anyAppUserID) {
+ completed($0)
+ }
}
- expect(result).toEventuallyNot(beNil())
```
This removes the need to _poll_ for when the value is returned, and
instead it gets detected immediately, potentially providing a
significant boost in test execution speed.
I've also used `Nimble`'s own `waitUntil` for cases where we don't need
to return a value.
#### `toEventually` usage:
- Before this PR: 353
- After: 229
There's still a lot of legitimate usage of `toEventually`, but I'm sure
I missed other places where this can also simplify the code. This is a
good start for now.
Fixes [CSDK-419].
This is an improvement over the fix in #1292. In there, we avoided
appending on an existing `CustomerInfoOperation` if there were any
`PostReceiptDataOperation`s in progress, because those might come back
with outdated entitlements.
This fix removes that cache key hack, and instead *reuses* the entire
response to that `PostReceiptDataOperation` by "stealing" that request's
cache key.
This is perfectly captured by the existing test
`BackendPostReceiptDataTests.testGetsUpdatedSubscriberInfoAfterPost`,
added in #1292. Amazingly enough, that test still passes with one minor
change: the entire process requires one fewer API call 🎉🐼
[CSDK-419]:
https://revenuecats.atlassian.net/browse/CSDK-419?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
* Create < iOS 13 and >= iOS 14 tests for attribute tests and receipt tests
Fixes [CF-457]
attConsentStatus is not available on < iOS 14, so we needed to create specific test cases for each range of platforms that support/don't support.
* Update BackendPostReceiptDataTests json for < iOS13 and >= iOS14
* Update BackendSubscriberAttributesTests json for < iOS13 and >= iOS14
🙃
* moved test suites to Tests folder
* fixed outdated path
* fixed more outdated paths
* moved TestsPlans to Tests folder
* update path to Constants.swift