Skip to content

Upgrade Purchasely SDK to 6.0.0#9

Open
chouaibMo wants to merge 25 commits into
mainfrom
chore/sdk-v6-migration
Open

Upgrade Purchasely SDK to 6.0.0#9
chouaibMo wants to merge 25 commits into
mainfrom
chore/sdk-v6-migration

Conversation

@chouaibMo

@chouaibMo chouaibMo commented May 18, 2026

Copy link
Copy Markdown

Summary

Migrate the Android integration to Purchasely SDK v6 per MIGRATION_V6.md.

  • Replace removed setPaywallActionsInterceptor with per-action Purchasely.interceptAction<…> suspend handlers; bridge them to the existing Observer-mode billing flow via a pendingResult continuation resolved to PLYInterceptResult.{SUCCESS,NOT_HANDLED,FAILED} based on 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 (PLYPurchaseResultDisplayResult).
  • Re-anchor presentation imports under io.purchasely.ext.presentation.

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

mavenLocal() is temporarily added to dependency resolution — remove once 6.0.0 ships to Maven Central.

Test plan

  • ./gradlew :app:testDebugUnitTest — 178/178 passing locally against a publishToMavenLocal build of io.purchasely:core:6.0.0 + io.purchasely:google-play:6.0.0.
  • Smoke test paywall display + Observer-mode purchase + restore on a device.
  • Verify the success_payment chain still fires after a successful purchase.
  • Verify deeplink handling via handleDeeplink.
  • Remove mavenLocal() from settings.gradle.kts once 6.0.0 is on Maven Central, and re-run the test suite.

🤖 Generated with Claude Code


Android v6 Presentation coverage added

  • Removed Paywall wording from Android source in favor of Presentation / Screen naming.
  • Added Android examples for onPresented, onCloseRequested, runtime colors, UI flags, screenId, flowId, and prepared display(...).
  • Verified with cd android && ./gradlew :app:assembleDebug --quiet.

SDK 6.0.0-beta2 + v6 session API + platform parity (Android & iOS)

Android

  • Bump to 6.0.0-beta2 (mavenLocal) — validates the new non-suspend display()PLYPresentationSession API: the wrapper now uses presentation.display(activity).await() instead of bridging callbacks through suspendCancellableCoroutine; builder-set onDismissed stays active and render failures surface as typed PLYErrors.
  • Strict wrapper boundary restored: zero io.purchasely imports outside purchasely/ (new SubscriptionInfo boundary type; PLYRunningMode/LogLevel mapping moved into the wrapper).
  • No more silent paywall failures: FetchResult.Error/Deactivated are logged and the filters prefetch retries on tap.
  • ProGuard keep rules added (file was referenced but missing — release builds would strip the SDK).

iOS

  • Local Swift package now targets the .worktrees/ios-develop worktree (origin/develop + v6 builder fixes) instead of whatever branch the main SDK checkout is on.
  • Same strict wrapper boundary as Android: only Purchasely/ imports the SDK (PresentationHandle, SubscriptionInfo, ConsentPurpose boundary types); removed the dead second mode setting (RunningModeRepository).
  • Verified: xcodebuild test108/108 passing against the v6 develop SDK.

Features (identical on both platforms)

  • Mood discovery 🌿🥃🍑🧃🎉 — CocktailMood filters the catalog by tags and reports setUserAttribute("preferred_mood", key); same keys/labels/tags on both OSes so console audiences behave identically.
  • Surprise me 🎲 — random pick from the filtered list + incrementUserAttribute("surprise_me_count") (campaign-trigger material).

Design ("cocktail lounge", same hex values on both platforms)

  • Deep bottle-green primary, burnished gold accents, warm cream / charred green backgrounds.
  • Letterspaced SHAKER overline + time-of-day greeting in serif; serif display type on hero and detail titles.

⚠️ Reviewer setup (local, unpublished dependencies)

  1. Android: publish the SDK locally from Purchasely-Android-Sources branch fix/presentation_builder: bump versionName to 6.0.0-beta2 in gradle/libs.versions.toml, run ./gradlew publishToMavenLocal, restore the file.
  2. iOS: the SPM symlink expects a worktree at ~/Purchasely/.worktrees/ios-develop (git worktree add from Purchasely-iOS-Sources develop).

Test plan (additions)

  • Android unit tests — all passing (incl. mood/surprise suites)
  • iOS unit tests — 108/108 passing
  • Android emulator: onPresented fires on render; builder onDismissed receives CANCELLED/BUTTON on close (v6 session API end-to-end)
  • iOS simulator: onboarding paywall renders against v6 develop; Home shows the inline A/B test (variant A vs variant B on Android)

🤖 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_OBSERVEROBSERVER 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
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
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
- 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 8 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>
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.

3 participants