Update baseline swiftinterface files for pallares/hashable-configuration-via-storage#6812
Merged
Conversation
ajpallares
approved these changes
May 18, 2026
c40c1fd
into
pallares/hashable-configuration-via-storage
5 checks passed
ajpallares
added a commit
that referenced
this pull request
May 22, 2026
…6811) * Expand DangerousSettings API tester coverage Adds explicit property-read and constructor coverage for `DangerousSettings` in both the Swift and Obj-C API testers so future refactors of the type (e.g. moving stored properties into an internal value-type backing) are guaranteed to remain source- and ABI-compatible for SDK consumers. Swift (SwiftAPITester/DangerousSettingsAPI.swift): - Exercise `DangerousSettings()` and `DangerousSettings(autoSyncPurchases:)` in addition to the existing `DangerousSettings(uiPreviewMode:)` case. - Read all three publicly exposed properties (`autoSyncPurchases`, `customEntitlementComputation`, `uiPreviewMode`). Obj-C (ObjcAPITester/RCDangerousSettingsAPI.{h,m}): - New file mirroring the `RCConfigurationAPI` pattern (one `+ (void)checkAPI` per public type), wired into `main.m` and the `AllAPITests.xcodeproj` project file. - Covers `-init`, `-initWithAutoSyncPurchases:`, and reads of the two Obj-C-exposed properties (`autoSyncPurchases`, `customEntitlementComputation`). `uiPreviewMode` is intentionally omitted since it is `@_spi(Internal)` and not bridged to Obj-C. These additions are landed first so we can verify the existing public surface compiles on `main`, before any refactor of `DangerousSettings` internals is layered on top. Co-authored-by: Cursor <cursoragent@cursor.com> * Back Configuration, DangerousSettings, PlatformInfo with Hashable Storage struct Moves the comparable stored properties of `Configuration`, `DangerousSettings`, and `Purchases.PlatformInfo` into internal `Storage` value types that automatically synthesize `Hashable`. The class-level `isEqual(_:)` and `hash` overrides delegate to the storage, so adding a new field only requires touching `Storage`—no parallel updates to equality boilerplate. Notes: - Public `let` properties on `DangerousSettings` are now `var { get }` computed from `storage`. This is callsite-compatible for both Swift and Obj-C consumers, but `.swiftinterface` baselines under `api/` will need to be refreshed via the existing `update_swiftinterface_baselines` Fastlane lane. - `UserDefaults?` is intentionally part of `Configuration.Storage`'s equality via `NSObject` reference identity. This dedups the common case (both `nil`, both `.standard`, or the same cached suite) while still treating a different `UserDefaults` instance as a real configuration change. - `internalSettings` on `DangerousSettings` is excluded from equality; it's an internal/debug mechanism containing non-`Hashable` closures and has no observable effect on the public configuration. - `Signing.ResponseVerificationMode` is left untouched globally; a small case-only `ResponseVerificationModeKey` proxy inside `Configuration` is used in `Storage`, since the associated `PublicKey` is always the hardcoded value loaded by `Signing.loadPublicKey()`. Foundation for an upcoming change that deduplicates `Purchases.configure(...)` calls with identical configurations, mirroring purchases-android's behavior. Co-authored-by: Cursor <cursoragent@cursor.com> * Dedupe `Purchases.configure(with:)` calls with equal `Configuration` Mirrors `purchases-android`'s behavior of returning the existing `Purchases` instance when `configure` is invoked again with a `Configuration` equal to the one used previously. Implementation: - Store the `Configuration` used to create each `Purchases` instance on the instance itself (`currentConfiguration`). - Add a `dedupingAgainst:` parameter to `setDefaultInstance`. When the current singleton's `currentConfiguration` equals the supplied one, the existing instance is returned and `purchases` is not invoked, avoiding an unnecessary `Purchases` allocation. A dedicated `ConfigureStrings` case logs that the existing instance is being reused. - Route the public `Purchases.configure(with:)` directly through the new `setDefaultInstance(_:dedupingAgainst:)` and drop the legacy internal `configure(withAPIKey:...)` helper, which had no remaining callers. All other `configure(...)` overloads continue to funnel through `configure(with:)`, so every configure path now benefits from deduplication. Tests: - Add equality unit tests for `Configuration`, `DangerousSettings`, and `Purchases.PlatformInfo` (the three types now backed by `Hashable` storage structs), including `UserDefaults` reference-identity coverage for `Configuration`. - Add dedup behavior tests covering repeated `Purchases.configure(with:)` with equal/equivalent `Configuration`s, with same/different `UserDefaults` references, plus a direct check of the equality short-circuit used by `setDefaultInstance(_:dedupingAgainst:)`. Co-authored-by: Cursor <cursoragent@cursor.com> * Add new Misc unit tests to RevenueCat.xcodeproj `Tests/UnitTests/Misc/DangerousSettingsTests.swift` and `Tests/UnitTests/Misc/PlatformInfoTests.swift` were picked up by the Tuist-generated `UnitTests` target via globbing, but the hand-maintained `RevenueCat.xcodeproj` enumerates each file explicitly, so the Danger project-sync check flagged them as missing. Wire them into the `Misc` group, file references, and the `UnitTests` sources build phase, mirroring how `ClockTests.swift` is registered. Co-authored-by: Cursor <cursoragent@cursor.com> * Match `purchases-android`'s dedup log message verbatim The new info log is what developers will actually see when they configure twice with the same `Configuration`. Talking about "returning the existing instance" is misleading because the SDK does not surface a return value to callers that hit the dedup path at runtime (the public `configure(with:)` is `@discardableResult`). Align with `purchases-android`'s `INSTANCE_ALREADY_EXISTS_WITH_SAME_CONFIG`: - Rename `purchase_instance_already_set_with_same_config` to `instance_already_exists_with_same_config`. - Use Android's exact wording: "Purchases instance already set with the same configuration. Ignoring duplicate call." Co-authored-by: Cursor <cursoragent@cursor.com> * Drop redundant `Storage` rationale doc comments The doc paragraphs only narrated the obvious mechanic ("a `Hashable` struct lets the synthesized conformances drive `isEqual`/`hash`"), which the code itself already conveys. Keep the non-obvious bit: the comment explaining why `internalSettings` is excluded from `DangerousSettings`'s storage. Co-authored-by: Cursor <cursoragent@cursor.com> * Drop `purchases-android` reference from setDefaultInstance doc The doc comment for `setDefaultInstance(_:dedupingAgainst:)` already describes the behavior on its own terms; pointing at another SDK as the canonical reference adds noise. Co-authored-by: Cursor <cursoragent@cursor.com> * Clarify dedup doc: only test paths leave configuration `nil` There is no production "legacy" caller of `setDefaultInstance(_:dedupingAgainst:)` — every public configure overload routes through `configure(with: Configuration)`. The only callers that pass `nil` are `BasePurchasesTests` and `PurchasesSubscriberAttributesTests`, which build a `Purchases` directly with mocks. Tighten the doc on both `setDefaultInstance` and `currentConfiguration` to reflect that. Co-authored-by: Cursor <cursoragent@cursor.com> * Require `currentConfiguration` to be passed explicitly Drop the `= nil` default on `currentConfiguration` on both `Purchases` initializers and pass `nil` explicitly from the two test helpers that build `Purchases` directly with mocks. This way, future changes that add a new code path producing a `Purchases` instance will be forced to decide whether that instance participates in `configure(with:)` deduplication instead of silently opting out. Co-authored-by: Cursor <cursoragent@cursor.com> * Drop redundant `Storage` rationale doc on `Configuration` Matches the cleanup already done on `DangerousSettings` and `PlatformInfo`: the "Hashable struct synthesizes equality" paragraph just narrates the obvious mechanic. Keep the `UserDefaults` reference-identity note since that documents a non-obvious decision. Co-authored-by: Cursor <cursoragent@cursor.com> * Store `EntitlementVerificationMode` directly on `Configuration.Storage` `Configuration.Storage` previously held a `ResponseVerificationModeKey` proxy enum because `Signing.ResponseVerificationMode` is not `Hashable` (its associated `PublicKey` is a CryptoKit type without `Hashable` conformance) and the developer-facing `EntitlementVerificationMode` could not be used as a destination in a `Signing.ResponseVerificationMode` mapping switch (`.enforced` is `@available(*, unavailable)`). The proxy is unnecessary if we keep the developer-facing setting on both the `Builder` and `Configuration`, and only expand it into a `Signing.ResponseVerificationMode` (which carries the hardcoded `PublicKey`) on demand: - `Builder.responseVerificationMode: Signing.ResponseVerificationMode` becomes `Builder.entitlementVerificationMode: EntitlementVerificationMode`, defaulting to `.informational` (preserving the previous runtime default of `Signing.ResponseVerificationMode.default`). - `Configuration.Storage.entitlementVerificationMode` replaces `responseVerificationMode: ResponseVerificationModeKey`, so `Storage` keeps free synthesized `Hashable` conformance without a proxy. - `Configuration.responseVerificationMode` becomes a `lazy var` that calls `Signing.verificationMode(with:)` (and thus `Signing.loadPublicKey()`) on first access. The only production reader is `Purchases.configure(with:)`, so the lazy resolution happens at the same point the eager resolution used to. Drops the proxy enum and its rationale doc comment. Co-authored-by: Cursor <cursoragent@cursor.com> * Drop `responseVerificationMode` lazy resolution doc The doc explains an internal mechanic (when the lazy var resolves) that the reader can see from the `lazy` keyword itself. Co-authored-by: Cursor <cursoragent@cursor.com> * Update baseline swiftinterface files (#6812) * Merge the same-configuration dedup unit tests The instance-identity check and the log-message check exercised the same `configure → configure` setup. Collapse them into one test so we only build the dedup scenario once and the assertion list reads in one pass. Co-authored-by: Cursor <cursoragent@cursor.com> * Restore internal `configure(withAPIKey:...)` helper for backend integration tests `BaseBackendIntegrationTests.configurePurchases()` calls the internal long-form `Purchases.configure(withAPIKey:..., responseVerificationMode: Signing.ResponseVerificationMode, ...)` helper to opt into strict signing verification without going through `Configuration` (whose public `EntitlementVerificationMode.enforced` case is marked `@available(*, unavailable)`). The previous dedup commit inadvertently removed this helper while consolidating `configure(with:)` paths, breaking compilation of all five `backend-integration-tests-*` CI jobs. Restore the helper and forward `currentConfiguration: nil` so this path opts out of dedup, mirroring the behavior of the other test-only call sites that construct `Purchases` directly with mocks. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by @ajpallares for pallares/hashable-configuration-via-storage
Note
Medium Risk
Updates the public Swift/ObjC interface for core configuration-related types, including equality/hash exposure and property mutability, which can affect how SDK objects behave in collections and comparisons. While mostly baseline/interface regeneration, it touches widely-used public API surfaces across all Apple platforms.
Overview
Refreshes the generated
.swiftinterfacebaselines across iOS/macOS/tvOS/watchOS/visionOS (and simulators).Notable API surface changes include
DangerousSettingsswitchingautoSyncPurchasesandcustomEntitlementComputationfrom storedletto getter-onlyvar, and adding Objective-CisEqual(_:)/hashoverrides (guarded bycompiler(>=5.3) && $NonescapableTypes) forDangerousSettings,Purchases.PlatformInfo, andConfiguration.Reviewed by Cursor Bugbot for commit ded1c2c. Bugbot is set up for automated code reviews on this repo. Configure here.