Skip to content

Upgrade Purchasely SDK to 6.2.0 - #9

Merged
kherembourg merged 28 commits into
mainfrom
chore/sdk-v6-migration
Oct 8, 2026
Merged

kherembourg merged 28 commits into
mainfrom
chore/sdk-v6-migration

Conversation

@chouaibMo

@chouaibMo chouaibMo commented May 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Moves Shaker (Android + iOS) to the published Purchasely SDK 6.2.0, fixes the bugs found in a code review, and adds the 6.1 / 6.2 features a customer demo needs.

The branch no longer depends on local SDK builds: no mavenLocal(), no local SPM shim, no .worktrees/ios-develop symlink. A clean checkout builds on both platforms.

SDK 6.2.0

Android

  • io.purchasely:core / google-play 6.2.0 from Maven Central; mavenLocal() removed from both settings files.
  • compileSdk / targetSdk 36 (required by the 6.2.0 AAR metadata).
  • Removed the obsolete io.purchasely.ext.interceptAction import.

iOS

  • Remote Purchasely-iOS package, exactVersion: 6.2.0; ios/LocalPackages/ deleted.
  • onClose → onCloseRequested; presentation finishers keyed by ObjectIdentifier instead of the internal presentation.id.
  • ShakerTests builds in Swift 5 language mode: Xcode 16.4 (CI) rejects try await super.setUp() in Swift 6 mode. The app target stays on Swift 6 with complete strict concurrency.

Bug fixes

  • Consent after a cold start (Android + iOS): revoked consent purposes were applied only when Settings opened, so the SDK tracked again after each launch. They are now applied when the SDK start succeeds and after a restart.
  • Premium after login (Android): login with refresh = false showed a premium user as free. Premium is now refreshed after login and logout.
  • PENDING purchase in Observer mode (Android): it counted as a success. It now reports Error("Purchase pending").
  • Replayed purchase results (Android): PurchaseManager used replay = 1, so a new collector got an old result. Now replay = 0 with a suspending emit, so live collectors still get every result.
  • Deeplink on rotation (Android): the deeplink was handled again after a configuration change. Now only on first creation.

New demo features

  • Custom events (6.2 Purchasely.emit): recipe_viewed {recipe_id, spirit}, favorite_added, surprise_me on both platforms, cocktail_mixed on Android. They are the triggers for Console campaigns.
  • Web redemption (6.1): listener (Android) / delegate (iOS) registered at start; the SDK shows its own alert; premium is refreshed on success.

Test plan

  • Android: 12 selected unit tests, written first (RED: 8 assertion failures), then GREEN, incl. the 3 existing PurchaseManager delivery tests.
  • iOS: 4 selected unit tests, written first (RED: 6 failures), then GREEN.
  • ./gradlew :app:assembleDebug and xcodebuild build (iOS Simulator) pass.
  • CI "Build & Test Android" passes.
  • CI "Build & Test iOS" on the latest commit.
  • Device smoke: paywall display, Observer and Full purchase, restore, web redemption.
  • Campaign journey: recipe_viewed → campaign paywall, also after a restart with Campaigns consent revoked.

Known gap: no test drives the real app start for the consent fix (ShakerApp.onConfigured, AppViewModel start). A test needs Robolectric or a new seam; the helper and restart paths are tested.

Console prerequisites (Shaker app, not done by this PR)

The code uses items that are not configured in the Shaker Console yet:

  • Placements filters, favorites, recipe_detail.
  • Custom events recipe_viewed, favorite_added, surprise_me, cocktail_mixed.
  • One campaign triggered by recipe_viewed.

Follow-up proposals (not in this PR)

  • iOS promotional offers: move to the token-aware signPromotionalOffer(purchaseContextToken:) overload.
  • iOS: StoreKit 2 in Full mode (today .storeKit1).
  • iOS: wire Config.xcconfig with a checked-in defaults file, so the API key is not hard-coded.
  • Android: remove the fallback signing password from build.gradle.kts.
  • Apply stored consent also after an offline start.
  • "Why this screen" inspector (placement, audience, A/B test, campaign, flow ids) and a live event log.

🤖 Generated with Claude Code

Migrate the Android integration to the v6 public API per MIGRATION_V6.md:

- Replace the removed global setPaywallActionsInterceptor with per-action
  Purchasely.interceptAction<…> suspend handlers; bridge them to the existing
  Observer-mode billing flow with a pendingResult continuation that resolves
  to PLYInterceptResult.SUCCESS / NOT_HANDLED / FAILED based on the emitted
  TransactionResult.
- Switch to allowDeeplink, handleDeeplink, the single-arg start { error -> }
  callback, and PLYRunningMode.Observer (was PaywallObserver).
- Move from fetchPresentation/PLYPresentationProperties to the
  PLYPresentation { placementId(...) }.preload() DSL and map display() /
  buildView() callbacks to PLYPresentationOutcome (PLYPurchaseResult →
  DisplayResult).
- Re-anchor presentation imports under io.purchasely.ext.presentation.

Collateral: Purchasely 6.0.0 transitively pulls Google Play Billing 8.x;
update PurchaseManager.queryProductDetailsAsync to the new
QueryProductDetailsResult.productDetailsList shape.

Add mavenLocal() to dependency resolution (remove once 6.0.0 ships to
Maven Central).

Rewrite PurchaselyWrapperTest against the new suspend interceptors and
flow ordering. ./gradlew :app:testDebugUnitTest — 178/178 passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@chouaibMo chouaibMo changed the title chore(android): upgrade Purchasely SDK to 6.0.0 Upgrade Purchasely SDK to 6.0.0 May 18, 2026
chouaibMo and others added 2 commits May 18, 2026 18:43
AGP 9.2.1 requires Gradle ≥ 9.4.1 (MIGRATION_V6.md §7). The committed
wrapper was still 8.13, which broke CI even though local builds passed
because the dev machine had 9.4.1 cached.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ose plugin setup

- Switch Purchasely SDK init from Builder chain to the new `Purchasely { ... }` Kotlin DSL entrypoint (idiomatic v6).
- Drop redundant `kotlin.android` plugin alias: AGP 9.2.1 + `kotlin.plugin.compose` 2.2.10 already register the Kotlin extension (was causing "extension already registered" if re-applied).
- Add `mavenLocal()` to repositories for local SDK snapshot testing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@kherembourg
kherembourg marked this pull request as ready for review May 22, 2026 16:13
kherembourg and others added 2 commits May 27, 2026 12:17
Migrate the iOS sample to the v6 API surface:
- fluent init `Purchasely.apiKey(...).…start { error in }` (.storeKit1 on develop)
- `.paywallObserver` -> `.observer`, `readyToOpenDeeplink` -> `allowDeeplink`,
  `isDeeplinkHandled` -> `handleDeeplink`
- typed `interceptAction(.login/.navigate/.purchase/.restore)` returning PLYInterceptResult
- `fetchPresentation` -> `PLYPresentationBuilder.…preload`, dismissal -> PLYPresentationOutcome
- embedded paywall via `PLYPresentationViewController` + UIViewControllerRepresentable
- local Swift package workaround for the unreleased develop snapshot (no root Package.swift)
- docs/migration_v6.md recap

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@kherembourg

kherembourg commented May 28, 2026 •

Copy link
Copy Markdown
Contributor

CI note after Android v6 Presentation example update:

  • Android job currently fails before compiling app code because io.purchasely:core:6.0.0 and io.purchasely:google-play:6.0.0 are not published to Maven Central/local Maven on GitHub Actions. Local verification passed with the v6 artifacts available in Maven local: cd android && ./gradlew :app:assembleDebug --quiet.
  • iOS job currently fails in ios/LocalPackages/Purchasely/Shims/PLYLottieView.swift on missing PLYLogger; this PR update did not touch iOS per scope.

@kherembourg
kherembourg requested a review from Copilot May 28, 2026 21:54
@kherembourg

Copy link
Copy Markdown
Contributor

@greptileai review

@greptile-apps

greptile-apps Bot commented May 28, 2026 •

Copy link
Copy Markdown

Greptile Summary

Migrates both Android and iOS integrations from Purchasely SDK v5 to v6, replacing the removed setPaywallActionsInterceptor/processAction pattern with typed interceptAction suspend handlers, switching presentation APIs from fetchPresentation/PLYPresentationProperties to the builder DSL with preload(), and updating deeplink and running-mode APIs throughout.

  • Android: PurchaselyWrapper rewired around Purchasely.interceptAction<T> suspend lambdas bridged through awaitPendingResult; PurchaseManager.queryProductDetailsAsync updated for the Billing 8.x QueryProductDetailsResult shape; enum rename from PAYWALL_OBSERVER → OBSERVER with legacy storage migration.
  • iOS: Parallel changes to PurchaselyWrapper and EmbeddedScreenBanner; handleObserverAction simplified to direct async/await on @MainActor; loadPresentation adds defensive post-preload callback re-assignment.
  • mavenLocal() remains in settings.gradle.kts (tracked in the PR checklist for removal once 6.0.0 is on Maven Central).

Confidence Score: 4/5

Safe to merge with one fix — the iOS success payment screen may silently skip subscription refresh in some SDK builds.

The Android migration is thorough: interceptors, presentation loading, tests, and the enum rename are all consistent. The iOS side has a gap in showSuccessPaymentScreen — the defensive post-preload callback re-assignment applied carefully in loadPresentation is missing here, which means refreshAfterSuccessPayment won't be invoked when the SDK build doesn't propagate builder-seeded callbacks. Users who successfully purchase will see stale premium state until they manually restart the app.

ios/Shaker/Purchasely/PurchaselyWrapper.swift — specifically the showSuccessPaymentScreen method.

Important Files Changed

Filename Overview
ios/Shaker/Purchasely/PurchaselyWrapper.swift iOS v6 migration: switches to builder DSL, typed interceptors, and async/await observer flow. loadPresentation defensively re-assigns presentation callbacks after preload, but showSuccessPaymentScreen omits this, leaving refreshAfterSuccessPayment unreachable when builder-seeded callbacks don't fire.
android/app/src/main/java/com/purchasely/shaker/purchasely/PurchaselyWrapper.kt Core v6 migration: replaces setPaywallActionsInterceptor + processAction with typed interceptAction suspend handlers bridged through awaitPendingResult; updates SDK init, deeplink, and presentation APIs.
android/app/src/test/java/com/purchasely/shaker/purchasely/PurchaselyWrapperTest.kt Tests updated to match the new suspend-based interceptor API; async/await-style interceptJob pattern correctly resolves pending results and cleans up after each test.
android/app/src/main/java/com/purchasely/shaker/data/purchase/PurchaseManager.kt Updated queryProductDetailsAsync callback to accept the Billing 8.x QueryProductDetailsResult wrapper and extract productDetailsList from it; minimal and correct.
android/app/src/main/java/com/purchasely/shaker/data/PurchaselySdkMode.kt Renames PAYWALL_OBSERVER to OBSERVER and adds LEGACY_PAYWALL_OBSERVER migration constant; fromStorage correctly handles both old and new storage values.
ios/Shaker/Purchasely/EmbeddedScreenBanner.swift Replaces removed PresentationView property with a UIViewControllerRepresentable wrapper around PLYPresentationViewController; clean and correct.
android/settings.gradle.kts Adds mavenLocal() as a temporary dependency resolver for the pre-release Purchasely 6.0.0 SDK; must be removed before merging (explicitly tracked in PR checklist).

Sequence Diagram

sequenceDiagram
    participant SDK as Purchasely SDK v6
    participant IW as interceptAction lambda
    participant PW as PurchaselyWrapper
    participant PM as PurchaseManager
    participant App as App / ViewModel

    Note over SDK,App: Observer-mode purchase flow (v6)
    SDK->>IW: intercept(.purchase) [suspend]
    IW->>PW: handlePurchase(info, action)
    PW->>PW: awaitPendingResult
    PW->>PM: purchaseRequests.emit(PurchaseRequest)
    PM->>PM: Google Play Billing purchase
    PM->>PW: "transactionResult.emit(Success|Cancelled|Error)"
    PW->>PW: pendingResult.invoke(PLYInterceptResult)
    PW-->>IW: PLYInterceptResult (resume continuation)
    IW-->>SDK: return result

    Note over SDK,App: Presentation display flow (v6)
    App->>PW: loadPresentation(placementId)
    PW->>SDK: "PLYPresentation{}.preload()"
    SDK-->>PW: PLYPresentation
    PW->>PW: re-assign onClose/onDismissed defensively
    PW-->>App: FetchResult.Success(handle)
    App->>PW: display(handle, activity)
    SDK-->>PW: PLYPresentationOutcome
    PW->>PW: toDisplayResult()
    alt pendingSuccessfulPurchase
        PW->>PW: showSuccessPaymentScreen()
    end
    PW-->>App: DisplayResult
Loading

Fix All in Claude Code

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
ios/Shaker/Purchasely/PurchaselyWrapper.swift:351-376
**`showSuccessPaymentScreen` skips the defensive callback re-assignment done in `loadPresentation`**

`loadPresentation` explicitly re-assigns `presentation.onClose` and `presentation.onDismissed` on the loaded presentation object after `preload`, with the comment "builder-seeded callbacks are not fired by all develop snapshots." `showSuccessPaymentScreen` only attaches `onDismissed` on the builder; if the SDK snapshot in use doesn't propagate builder callbacks to the loaded presentation, the `refreshAfterSuccessPayment` call is never reached — subscriptions remain stale after the success screen is dismissed. The defensive re-assignment of `presentation.onDismissed` should be added inside the `preload` closure here, mirroring the pattern in `loadPresentation`.

Reviews (3): Last reviewed commit: "fix: block SDK default flow on Observer-..." | Re-trigger Greptile

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates Shaker’s Android + iOS Purchasely integrations to Purchasely SDK v6, updating running modes, deeplink handling, action interception, and presentation fetching/display APIs, plus adjusting Android Billing for the transitive Billing v8 upgrade.

Changes:

  • Android: upgrade Purchasely to 6.0.0, migrate to v6 Presentation APIs + typed interceptors, and adapt Billing queryProductDetailsAsync usage.
  • iOS: migrate Purchasely initialization + deeplink + presentation flows to v6 APIs, and introduce a local Swift package wrapper for Purchasely sources.
  • Tooling/docs: add temporary mavenLocal() resolution, update Gradle wrapper, and add a migration write-up.

Reviewed changes

Copilot reviewed 34 out of 34 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
settings.gradle.kts Adds mavenLocal() to resolve Purchasely v6 artifacts temporarily.
android/settings.gradle.kts Adds mavenLocal() for Android-only build resolution.
android/gradle/wrapper/gradle-wrapper.properties Updates Gradle wrapper distribution URL.
android/gradle/libs.versions.toml Bumps Purchasely + Billing versions and adjusts test dependency versions.
android/build.gradle.kts Updates plugin aliases applied across Android build.
android/app/build.gradle.kts Updates app module plugins and test dependencies; removes explicit Kotlin options.
android/app/src/main/java/com/purchasely/shaker/purchasely/PurchaselyWrapper.kt Migrates to v6 DSL init, typed interceptors, Presentation preload/display APIs, and updated callback types.
android/app/src/main/java/com/purchasely/shaker/purchasely/PresentationHandle.kt Re-anchors PLYPresentation import to v6 presentation package.
android/app/src/main/java/com/purchasely/shaker/data/RunningModeRepository.kt Updates observer mode mapping for v6 running mode naming.
android/app/src/main/java/com/purchasely/shaker/data/PurchaselySdkMode.kt Renames/migrates SDK mode storage/labels to “Observer/Presentation Observer”.
android/app/src/main/java/com/purchasely/shaker/data/purchase/PurchaseManager.kt Adapts to new Billing v8 QueryProductDetailsResult.productDetailsList.
android/app/src/main/java/com/purchasely/shaker/ui/screen/settings/SettingsViewModel.kt Renames paywall flows to presentation flows for onboarding placement.
android/app/src/main/java/com/purchasely/shaker/ui/screen/settings/SettingsScreen.kt Updates UI wiring to new presentation display flows and observer naming.
android/app/src/main/java/com/purchasely/shaker/ui/screen/home/HomeViewModel.kt Renames paywall triggers/callbacks to presentation equivalents.
android/app/src/main/java/com/purchasely/shaker/ui/screen/home/HomeScreen.kt Updates presentation collection + dismissal callback wiring.
android/app/src/main/java/com/purchasely/shaker/ui/screen/favorites/FavoritesViewModel.kt Renames paywall triggers/callbacks to presentation equivalents.
android/app/src/main/java/com/purchasely/shaker/ui/screen/favorites/FavoritesScreen.kt Updates presentation collection + unlock action wiring.
android/app/src/main/java/com/purchasely/shaker/ui/screen/detail/DetailViewModel.kt Renames paywall triggers/callbacks to presentation equivalents; updates comments.
android/app/src/main/java/com/purchasely/shaker/ui/screen/detail/DetailScreen.kt Updates presentation collection + unlock action wiring.
android/app/src/test/java/com/purchasely/shaker/purchasely/PurchaselyWrapperTest.kt Updates tests for v6 interceptor + presentation types and new suspend-based interception model.
android/app/src/test/java/com/purchasely/shaker/data/RunningModeRepositoryTest.kt Updates tests for v6 observer mode mapping.
android/app/src/test/java/com/purchasely/shaker/data/PurchaselySdkModeTest.kt Updates tests for v6 running mode mapping.
ios/Shaker/Screens/Settings/SettingsViewModel.swift Updates stored running mode from .paywallObserver to .observer.
ios/Shaker/Screens/Onboarding/OnboardingScreen.swift Avoids fetching onboarding presentation if onboarding is already completed.
ios/Shaker/Purchasely/PurchaselyWrapper.swift Migrates initialization, deeplinks, action interceptors, and presentation preload/display to v6 APIs.
ios/Shaker/Purchasely/EmbeddedScreenBanner.swift Replaces removed embedded view property with a UIViewControllerRepresentable wrapper.
ios/Shaker/Data/RunningModeRepository.swift Updates stored running mode mapping to .observer.
ios/Shaker/Data/PurchaselySDKMode.swift Maps app “paywall observer” selection to v6 .observer running mode.
ios/project.yml Switches Purchasely dependency to local package and adjusts test strict concurrency setting.
ios/LocalPackages/Purchasely/Package.swift Adds local SPM package wrapper definition for Purchasely sources.
ios/LocalPackages/Purchasely/Shims/Exports.swift Adds exported imports shim for Purchasely sources in SPM.
ios/LocalPackages/Purchasely/Shims/PLYLottieView.swift Adds Swift shim for PLYLottieView to avoid mixed ObjC/Swift target issues in SwiftPM.
docs/migration_v6.md Adds a repo-specific migration guide for Purchasely v6 (Android + iOS).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread settings.gradle.kts
Comment thread android/settings.gradle.kts Outdated
Comment thread android/app/build.gradle.kts
Comment thread android/build.gradle.kts
Comment thread android/app/src/main/java/com/purchasely/shaker/purchasely/PurchaselyWrapper.kt Outdated
Comment thread ios/Shaker/Purchasely/PurchaselyWrapper.swift
Comment thread ios/Shaker/Purchasely/PurchaselyWrapper.swift Outdated
Comment thread ios/Shaker/Purchasely/PurchaselyWrapper.swift Outdated
Comment thread ios/LocalPackages/Purchasely/Package.swift Outdated
Comment thread android/app/src/test/java/com/purchasely/shaker/data/RunningModeRepositoryTest.kt Outdated
Comment thread android/app/src/main/java/com/purchasely/shaker/data/PurchaselySdkMode.kt Outdated
Comment thread android/app/src/main/java/com/purchasely/shaker/data/RunningModeRepository.kt Outdated
Comment thread android/settings.gradle.kts Outdated
- PurchaselySdkModeTest/SettingsViewModelTest: replace removed PAYWALL_OBSERVER
  enum refs with OBSERVER and fix storageValue/label assertions (compile error)
- RunningModeRepositoryTest: assert persisted value is "observer" not "paywallObserver"
- PurchaselySdkMode: replace "pay" + "wallObserver" split with named
  LEGACY_PAYWALL_OBSERVER constant
- RunningModeRepository: drop dead LEGACY_OBSERVER branch (fromStorage handles it)
- PurchaselyWrapper (Android+iOS): return SUCCESS instead of NOT_HANDLED for
  Observer-mode cancellation/ignored actions so the SDK does not run its own flow
…ptors)

Return SUCCESS instead of NOT_HANDLED for Observer-mode purchase/restore
cancellations and ignored duplicate actions, so the SDK does not run its own
purchase/restore flow (the v6 equivalent of v5 processAction(false)).

Copy link
Copy Markdown
Contributor

Addressed the review findings across 97d74a9 (tests + data) and f801a15 (interceptors). Note: the Android suite isn't runnable in CI yet (io.purchasely:core:6.0.0 isn't on Maven Central), so the test fixes were verified by inspection against the actual enum shape.

# Finding Disposition
1 PurchaselySdkModeTest references removed PAYWALL_OBSERVER + stale storageValue/label assertions (compile error) Fixed 97d74a9 — all refs → OBSERVER, storageValue→"observer", label→"Presentation Observer". Also fixed a second stale ref the report missed: SettingsViewModelTest.kt:296 (same compile error).
2 RunningModeRepositoryTest asserts persisted "paywallObserver" but setter writes "observer" Fixed 97d74a9 — assertion → "observer", test renamed.
3 PurchaselySdkMode "pay" + "wallObserver" split literal Fixed 97d74a9 — named LEGACY_PAYWALL_OBSERVER = "paywallObserver" constant.
4 RunningModeRepository LEGACY_OBSERVER branch is dead code Fixed 97d74a9 — branch + constant removed; fromStorage() already maps "observer" and legacy "paywallObserver" to OBSERVER.
5 mavenLocal() must be removed before merging to main Kept (tracked) — 6.0.0 isn't on Maven Central yet, so removing it breaks the build now. Tracked in the PR checklist; inline threads left open as the pre-merge reminder.

Additional Copilot findings also addressed inline:

  • Observer-mode interceptor returns (Android + iOS): cancellation / ignored-duplicate now return SUCCESS/.success instead of NOT_HANDLED/.notHandled, so the SDK no longer runs its own purchase flow (v6 equivalent of v5 processAction(false), per docs/paywall-observer-reference.md). Fixed in f801a15.
  • Kotlin plugin removal: intentional — AGP 9.2.1 ships built-in Kotlin (verified :app:compileDebugUnitTestKotlin is registered). Not a break.
  • iOS forced StoreKit 1 and local SPM symlink: documented pre-release accommodations; left open for visibility, same as mavenLocal().

Generated by Claude Code

Copy link
Copy Markdown
Contributor

@greptileai review


Generated by Claude Code

@kherembourg

Copy link
Copy Markdown
Contributor

@greptileai review

kherembourg and others added 10 commits May 29, 2026 12:49
…SS on cancel)

Complements 97d74a9 (which only fixed PAYWALL_OBSERVER refs) and f801a15:
- Detail/Favorites/Home/Settings ViewModelTest: show*Paywall -> show*Presentation,
  onPaywallDismissed -> onPresentationDismissed (remaining compile errors)
- DetailViewModelTest: named args for loadPresentation (v6 inserts screenId at
  position 1, shifting contentId)
- PurchaselyWrapperTest: Cancelled now resolves with SUCCESS, matching the v6
  Observer-mode block of the SDK default flow introduced in f801a15

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… work

Opening the repo root in Android Studio uses the root Gradle build, which
had drifted from the canonical android/ build during the v6/AGP 9 migration:

- build.gradle.kts referenced libs.plugins.kotlin.android, an alias the
  version catalog no longer defines -> sync failed with
  "Unresolved reference 'android'".
- gradle.properties carried AGP-9 opt-out flags absent from
  android/gradle.properties, notably android.builtInKotlin=false, which
  disabled AGP's built-in Kotlin support. With no kotlin-android plugin
  applied, no compile*Kotlin tasks were created: assembleDebug produced an
  APK without compiling Kotlin and testDebugUnitTest reported NO-SOURCE.

Aligns both root files with the proven android/ configuration. Verified from
the root: sync (help) OK, compileDebugKotlin present, assembleDebug compiles
Kotlin, and testDebugUnitTest runs 180 tests with 0 failures.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Validates the new non-suspend display() -> PLYPresentationSession API
published locally from the SDK fix/presentation_builder branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…y package

Restores the architecture rule that only PurchaselyWrapper (and the
purchasely/ package) may import the SDK:
- PurchaselySdkMode / RunningModeRepository expose the app enum only;
  the PLYRunningMode mapping moves into the wrapper
- ShakerApp passes verboseLogging instead of the SDK LogLevel
- PremiumManagerImpl consumes the new SDK-free SubscriptionInfo model
  via wrapper.fetchSubscriptions
- Settings help text aligned with the renamed "Observer" label

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
display(handle) and showSuccessPaymentScreen now use
presentation.display(activity).await() instead of bridging the outcome
callback through suspendCancellableCoroutine. The builder-set onDismissed
stays active (no inline callback replaces it) and render failures surface
as caught PLYErrors instead of being silently swallowed.

Validated on emulator against SDK 6.0.0-beta2: onPresented fires on render,
builder onDismissed receives CANCELLED/BUTTON on close.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
onFilterClick / showRecipePresentation / showFavoritesPresentation ignored
FetchResult.Error and Deactivated, leaving dead buttons with no trace.
Errors are now logged and the filters prefetch is retried on tap.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
proguard-rules.pro was referenced by build.gradle.kts but missing — release
builds would strip SDK classes. Mock SDK version updated from the stale
5.7.3 to 6.0.0-beta2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ent fetch failures

- Architecture: Android now enforces zero io.purchasely imports outside the
  purchasely/ package, with the SDK->app type mapping table (PresentationHandle,
  FetchResult, DisplayResult, SubscriptionInfo, PurchaselySdkMode, ConsentPurpose)
- Presentation loading: v6 DSL + preload() + display(activity).await() pattern,
  and why the session API beats the inline-callback overload
- Error handling: handle every FetchResult variant explicitly (log + retry),
  never else -> {} on a paywall fetch
- Async: display() documented as non-suspend returning PLYPresentationSession

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… attributes

- CocktailMood (Refreshing/Strong/Sweet/Zero proof/Party) filters the catalog
  by tags (spirit for zero-proof) and reports preferred_mood to Purchasely for
  audience targeting; mood chips replace the category chips on Home
- Floating "Surprise me" action picks a random cocktail from the filtered list
  and increments the surprise_me_count attribute (campaign trigger material)
- GetFilteredCocktailsUseCase gains an optional mood parameter
- Unit tests: mood matching, use case filtering, attribute reporting,
  surprise-me behavior (including the empty-list guard)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
kherembourg and others added 10 commits June 10, 2026 10:39
- Deep bottle-green primary, burnished gold accents, warm cream background
  (light) / charred green (dark) — token values only, no structural change
- Home header becomes a time-of-day greeting ("Good evening, What are we
  mixing?") in serif; SHAKER wordmark moves to a letterspaced overline
- Serif display type on the Tonight's Pick hero and the Detail title

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…orktree

The symlink targeted ../iOS/Purchasely, i.e. whatever branch the main SDK
checkout happens to be on (currently a feature branch). It now targets the
.worktrees/ios-develop worktree — origin/develop plus the v6 builder fixes —
so Shaker builds against a stable v6 snapshot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ndroid)

Restores the architecture rule that only the Purchasely/ package may
import the SDK — `grep -rl 'import Purchasely' Shaker` now only matches
PurchaselyWrapper, PresentationHandle and EmbeddedScreenBanner:
- PresentationHandle wraps `any PLYPresentation`; FetchResult carries the
  handle and every display call site goes through display(handle:from:)
- SubscriptionInfo replaces [PLYSubscription] in PremiumManager via
  wrapper.fetchSubscriptions (same model as Android)
- ConsentPurpose (app enum) replaces PLYDataProcessingPurpose in Settings
- PurchaselySDKMode drops PLYRunningMode (mapping moved into the wrapper)
  and its label aligns with Android ("Observer")
- initialize() takes verboseLogging instead of PLYLogger.PLYLogLevel
- EmbeddedScreenBanner accepts FetchResult and resolves the SDK controller
  internally (getController removed from the protocol)
- RunningModeRepository removed: it was a second, dead mode setting that no
  code read — PurchaselySDKMode is the single source of truth like Android

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- CocktailMood mirrors the Android enum exactly (same keys, labels, emojis,
  matching tags) so preferred_mood audiences behave identically
- Mood chips replace the category chips; selecting a mood filters the
  catalog and reports setUserAttribute("preferred_mood", key)
- Floating "Surprise me" button picks a random cocktail from the filtered
  list, increments surprise_me_count and navigates to its detail
- displayFiltersPaywall logs + retries on fetch error instead of failing
  silently (parity with the Android fix)
- Tests mirror the Android suites: mood matching, pinned attribute keys,
  selectMood/surpriseMe behavior including the empty-list guard

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… Android

Same hex values as the Android ShakerTokens: deep bottle green primary,
burnished gold accents, warm cream background (light) / charred green
(dark). Home header becomes the letterspaced SHAKER overline + time-of-day
greeting in serif; Tonight's Pick hero and Detail title go serif.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ule, verified APIs

- best-practices: iOS gets the same strict wrapper-boundary table as Android
  (PresentationHandle, SubscriptionInfo, ConsentPurpose, PurchaselySDKMode);
  section 4 retitled build -> preload -> display (v6 rule, both platforms);
  iOS async patterns updated to the v6 PLYPresentationBuilder flow with the
  platform note on session.await() (Android) vs fetch-bound onDismissed (iOS)
- CLAUDE.md: Verified SDK APIs rewritten for v6 on both platforms (incl. the
  iOS v6 default-runningMode=.observer trap), shared components updated

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Use io.purchasely 6.2.0 from Maven Central (remove mavenLocal), compileSdk/targetSdk 36
- Apply stored consent revocations at SDK start and after restart
- Refresh premium status after login and logout
- Treat a PENDING purchase as not completed
- PurchaseManager: replay = 0 with a suspending emit, so old results are not replayed
- Handle the deeplink only on first creation, not on rotation
- Emit custom events (recipe_viewed, favorite_added, surprise_me, cocktail_mixed)
- Register the 6.1 web redemption listener (SDK shows the alert)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Use the remote Purchasely-iOS package 6.2.0 (remove the local SPM shim)
- onClose -> onCloseRequested; key presentation finishers by ObjectIdentifier
- Apply stored consent revocations at SDK start and after restart
- Emit custom events (recipe_viewed, favorite_added, surprise_me)
- Register the 6.1 web redemption delegate and refresh premium on success

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Oct 8, 2026

Copy link
Copy Markdown

Automatic reviews are disabled for this repository.

Xcode 16.4 on CI rejects `try await super.setUp()` in Swift 6 mode
(non-Sendable XCTestCase). The app target stays on Swift 6 with
complete strict concurrency.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kherembourg kherembourg changed the title Upgrade Purchasely SDK to 6.0.0 Upgrade Purchasely SDK to 6.2.0 Oct 8, 2026
@kherembourg
kherembourg merged commit 708f790 into main Oct 8, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants