### 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>
This was already approved and merged into `5.0-dev` but bringing this
into `4.x` (see #3690)
### Motivation
Allow for storing of other presented offering context on a `Package`
### Description
- Replaced `offeringIdentifier: String` on `Package` with new
`presentedOfferingContext: PresentedOfferingContext`
- Currently it only has a `offeringIdentifier: String`
- Eventually this will add new optional properties for targeting and
placements
### Changes:
- `InternalAPI` is now `Sendable` to deal with new compilation error
- `PaywallEventStoreType` is now `Sendable`
- Extracted `SK2BeginRefundRequestHelperType` to make
`SK2BeginRefundRequestHelper` `Sendable`
- Updated `MockSK2BeginRefundRequestHelper` to be thread-safe and
`Sendable`
- Fixed compilation error in `DebugContentViews`
- Fixed compilation error in `DebugViewModel`
- Fixed compilation error in `StoreMessagesHelper`
### Description
This removes the extra APIs that were added to support showing store
messages in ObjC. We will add these in PHC. If there is a demand for it
we can always add it later.
---------
Co-authored-by: NachoSoto <NachoSoto@users.noreply.github.com>
### Description
StoreKit displays different types of in-app messages on app launch, but
it provides an API to disable that and show these messages at a
different time. This PR wraps those APIs to make them easier to use
through our SDK.
#### New APIs
- Add new configuration parameter to defer StoreKit's automatic in-app
messages
- Add new method in `Purchases` to display a list of specific message
types
- New `StoreKitMessageType` enum mapping to the types used by StoreKit
---------
Co-authored-by: NachoSoto <NachoSoto@users.noreply.github.com>
Fixes https://github.com/RevenueCat/purchases-capacitor/issues/100 and
https://app.circleci.com/pipelines/github/RevenueCat/purchases-ios/14093/workflows/b3f8d001-6f39-40db-a5c5-00fc37d0211e/jobs/105838
```
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 ??? 0x0 ???
1 libswiftCore.dylib 0x18f9b8cf8 swift_getSingletonMetadata + 956
2 RevenueCat 0x107f42de4 type metadata accessor for DebugViewModel + 64
3 libobjc.A.dylib 0x18017d938 realizeClassMaybeSwiftMaybeRelock(objc_class*, mutex_tt<false>&, bool) + 116
4 libobjc.A.dylib 0x180184eb0 realizeAllClasses() + 168
5 libobjc.A.dylib 0x180187b3c objc_copyClassList + 36
6 XCTestCore 0x10532fcb0 +[XCTestCase(RuntimeUtilities) _allSubclasses] + 36
7 XCTestCore 0x10532fdb8 +[XCTestCase(RuntimeUtilities) allSubclasses] + 16
```
Both `XCTest` (to load tests at runtime) and `Capacitor` use
`objc_getClassList`. That's broken before iOS 16 and will try to load
the metadata for `DebugViewModel` even though it's not `@available`.
A similar issue has been reported before:
https://openradar.appspot.com/radar?id=4970535809187840 /
https://github.com/apple/swift/issues/58099
This adds a layer of indirection so loading `DebugViewModel` at runtime
doesn't require the metadata for `NavigationPath`.
### Context:
`#if os(iOS)` no longer evaluates to `true` when compiling for
`visionOS`
Which means we need `|| os(xrOS)`.
But to support older Xcode we need `#if swift(>=5.9) && os(xrOS)`.
But, fun fact, short-circuiting doesn’t work in `Xcode 13`.
And we still need that to run iOS <15 tests (`FB12223404` /
`FB12805891`)
💩
This works around that by defining our own `VISION_OS` flag.
### Changes:
- Added `VISION_OS` compile time flag to determine SDK
- Replaced `#if os(xrOS)` with `#if VISION_OS`
- Added flag to `Package.swift` (thanks @neonichu for helping with this)
Signature verification is not working due to changing signature (see
#2744), but the debug screen shouldn't show an error if verification
isn't enabled.
### Changes:
- Exposed `internal`-only `Purchases.cachePresentedOfferingIdentifier`
- Changed `SwiftUI` extensions to `static` methods: this is required to
be able to call an instance method on them
- Added integration test to verify this offering identifier is used
Technically this is an API change, but these APIs are implicitly beta
because they're only available in Xcode 15 beta.
Not sure why I didn't notice this when compiling through Xcode, but just
noticed this on CI in #2591.
Fixes:
```
❌ /Users/distiller/purchases-ios/Sources/Support/PaywallExtensions.swift:106:27: 'some' return types are only available in application extensions for iOS 13.0.0 or newer
private var products: some Sequence<StoreProduct> {
```
### Description
This adds overloads, but there's still a few things pending for the SDK
to fully support these:
- Set `presentedOfferingIdentifier` when displaying StoreKit2 paywalls:
SDK-3176
- Fix `preventPurchasePopupCallFromTriggeringCacheRefresh`: SDK-3172
- Always initialize `StoreKit2TransactionListener` even on SK1 mode:
SDK-3170
### Example:
```swift
struct App: View {
var offering: Offering
var body: some View {
Content()
.sheet(isPresented: self.$showingSubscriptionSheet) {
SubscriptionStoreView(offering: self.offering)
}
}
}
```
Fixes SDK-3084

## 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
Starting April 2023 it's no longer possible to submit apps with Xcode
13.x. This technically doesn't drop _support_, but we might won't be
testing on the oldest supported version of Xcode 13, since it's not
necessary.
### Changes:
_See
[`UpdatingRequirements.md`](https://github.com/RevenueCat/purchases-ios/blob/f8e07f07e8e842c25755f3dbca3a3f35961258a0/Contributing/UpdatingRequirements.md)_
- Removed `CircleCI` Xcode 13.2.1 job (_this saves ~11 minutes of build
time on every commit_)
- Updated `README`
- Updated message in `SwiftVersionCheck.swift`
Note that a lot of CI tests still run on Xcode 13.x for now because
installing old simulators on Xcode 14.x was failing. We'll need to fix
this when `CircleCI` drops support for `Xcode 13.x`.
Once we do that, we can refactor a lot of code like
```swift
#if swift(>=5.7)
extension CustomerInfo: Sendable {}
#else
// `@unchecked` because:
// - `Date` is not `Sendable` until Swift 5.7
// - `URL` is not `Sendable` until Swift 5.7
extension CustomerInfo: @unchecked Sendable {}
#endif
```
Fixes [CSDK-632].
### Changes:
- Created `Signing` to encapsulate new signing logic and types
- Added abstractions like `Signing.Certificate` and `Signing.PublicKey`
over `Security` C types
- Created `Configuration.EntitlementVerificationLevel` and
`Signing.ResponseVerificationLevel` (the logic for the name difference
is that publicly this is used to validate entitlements, but the internal
implementation works for any response).
- Created `Signing.loadPublicKey`
- Added tests for loading public key embedded in SDK
- Added `internal` (for now)
`Configuration.with(entitlementVerificationLevel:)`
- Added `SystemInfo.responseVerificationLevel` with the loaded key, if
enabled
### For a future PR:
- Make the `Configuration.with(entitlementVerificationLevel:)` `public`
- Make `Configuration.EntitlementVerificationLevel` `public`
- Add those 2 to `APITesters`
- Improve documentation by adding a link to the docs
- Verify certificate issuer in test
[CSDK-632]:
https://revenuecats.atlassian.net/browse/CSDK-632?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
This will make it easier to figure out that the version is no longer
compatible.
Aditionally, I've added `UpdatingRequirements.md` so we have a single
place to reference when changing these requirements.
Finishes [CSDK-451].
This new small public API allows users to quickly figure out if
everything in the SDK is correctly configured in a simple way:
```swift
let diagnostics = PurchasesDiagnostics.default
do {
try await diagnostics.testSDKHealth()
} catch {
print(error)
}
```
The specific underlying errors will provide information about what
failed.
We can continue growing this to check for more specific things, but for
now it does 4 things:
- Verify API connectivity: networking issues, firewalling, etc.
- Verify API key is correct
- Verify `Offerings` are configured correctly
- Verify that all products in `Offerings` are configured correctly and
found in `StoreKit`
This new API is covered by:
- API testers
- Unit tests
- Integration tests (both on `SK1` and `SK2`)
_I've taken the "shortcut" of making this `async` only (while still
compatible with Objective-C), which means it's not compatible with iOS
12.x. But that made the implementation a lot simpler, which I think is a
useful tradeoff._
### Depends on:
- #1970
- #1971
- #1973
- #1974
- #1975
- #1976
[CSDK-451]:
https://revenuecats.atlassian.net/browse/CSDK-451?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
Created `Async` with some shared methods that make this conversion
simpler in a type-safe way.
Helps turn this:
```swift
_ = Task<Void, Never> {
do {
completion(.success(try await f()))
} catch {
completion(.failure(ErrorUtils.error())
}
}
```
Into this:
```swift
Async.call(with: completion) {
try await f()
}
```