Skip to content

Add workflowTrigger to ButtonComponent.Action#6693

Merged
facumenzella merged 4 commits into
mainfrom
facundo/workflow-trigger-action-rename
Apr 24, 2026
Merged

Add workflowTrigger to ButtonComponent.Action#6693
facumenzella merged 4 commits into
mainfrom
facundo/workflow-trigger-action-rename

Conversation

@facumenzella

@facumenzella facumenzella commented Apr 24, 2026

Copy link
Copy Markdown
Member

Summary

Mirrors Android's rename of `Action.Workflow` → `Action.WorkflowTrigger` from purchases-android#3380.

This PR just introduces the type — the actual workflow navigation behavior lives in #6692. Without this, buttons with `"type": "workflow"` from the backend decode as `.unknown` and are hidden; with it they render and track correctly.

Changes:

  • Adds `case workflowTrigger` to `PaywallButtonComponent.Action`, decoding from `"type": "workflow"`
  • Adds `case workflowTrigger` to `ButtonComponentViewModel.Action` with `hasUnknownAction = false` (so the button renders) and analytics value `"workflow_trigger"`

Triggered by

#6692

Test plan

  • Verify workflow buttons are no longer hidden when the backend sends `"type": "workflow"`
  • Verify analytics track `"workflow_trigger"` (not `"unknown"`) for workflow button taps

🤖 Generated with Claude Code


Note

Low Risk
Low risk: adds a new enum case and wiring for decoding/analytics, with no behavioral workflow navigation yet (action is currently a no-op).

Overview
Backend paywall button actions now recognize "type": "workflow" by adding case workflowTrigger to PaywallComponent.ButtonComponent.Action (including Codable support) and mirroring it in ButtonComponentViewModel.Action.

UI handling is updated so workflow-trigger buttons are not treated as unknown/hidden and they report analytics as workflow_trigger (with no URL); cache warming and unit tests are updated accordingly.

Reviewed by Cursor Bugbot for commit 2229079. Bugbot is set up for automated code reviews on this repo. Configure here.

Mirrors Android's rename of Action.Workflow → Action.WorkflowTrigger
(purchases-android#3380). Decodes backend "type": "workflow" into
.workflowTrigger so workflow buttons are rendered (previously decoded
as .unknown and hidden) and track as "workflow_trigger" in analytics.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@facumenzella facumenzella marked this pull request as ready for review April 24, 2026 12:56
@facumenzella facumenzella requested review from a team as code owners April 24, 2026 12:56
@facumenzella facumenzella requested review from vegaro April 24, 2026 13:33
@vegaro

vegaro commented Apr 24, 2026

Copy link
Copy Markdown
Member

maybe adding some tests would be a good idea?

@facumenzella

Copy link
Copy Markdown
Member Author

@vegaro added tests cd3eb98

@facumenzella facumenzella enabled auto-merge (squash) April 24, 2026 15:29
@facumenzella facumenzella merged commit b8cb803 into main Apr 24, 2026
16 of 18 checks passed
@facumenzella facumenzella deleted the facundo/workflow-trigger-action-rename branch April 24, 2026 15:38
polmiro pushed a commit that referenced this pull request Apr 24, 2026
* Add workflowTrigger case to ButtonComponent.Action

Mirrors Android's rename of Action.Workflow → Action.WorkflowTrigger
(purchases-android#3380). Decodes backend "type": "workflow" into
.workflowTrigger so workflow buttons are rendered (previously decoded
as .unknown and hidden) and track as "workflow_trigger" in analytics.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add tests for workflowTrigger action decoding and analytics value

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
JZDesign pushed a commit that referenced this pull request Apr 24, 2026
* Replace print with Logger.error in ISODurationFormatter (#6691)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* Decode reward payload in RewardVerification poll response (#6678)

* Decode reward payload in RewardVerification poll response

Plumbs the verified-reward payload from the backend response through the
internal `pollRewardVerificationStatus` SPI so RC ad adapters can dispatch
meaningful "verified" events with the granted reward.

* Adds `VirtualCurrencyReward` and `VerifiedReward` (`@_spi(Internal) public`)
  under `Sources/Ads/RewardVerification/`.
* Extends `RewardVerificationStatusResponse` to optionally decode a typed nested
  reward payload (`{ type, code, amount }`) on `verified` responses.
  - `reward` absent or null → `.noReward`
  - `type == "virtual_currency"` with valid `code`+`amount` → `.virtualCurrency`
  - any other `type` (or malformed `virtual_currency`) → `.unsupportedReward` + warn
* `RewardVerificationPollStatus.verified` now carries the `VerifiedReward`.
* `Purchases.pollRewardVerificationStatus` returns `.verified(reward)`.
* Test mocks injected via a new `BasePurchasesTests.MockBackend` convenience
  init so `backend.adsAPI` resolves to `MockAdsAPI`.

* Add unit tests for VerifiedReward and VirtualCurrencyReward

Co-locates the unit tests next to the types this PR introduces under
`Sources/Ads/RewardVerification/`:

- `Tests/UnitTests/Ads/RewardVerification/VirtualCurrencyRewardTests.swift`
  Covers field storage, equality (both fields must match), and decimal
  precision preservation.

- `Tests/UnitTests/Ads/RewardVerification/VerifiedRewardTests.swift`
  Covers `.virtualCurrency(_)` associated-value carrying, `.noReward`
  vs `.unsupportedReward` distinct-case identity, equality (matching
  associated reward required), and an exhaustive switch coverage test
  to guard against silently adding cases.

Both tests pass against the existing `Sources/Ads/RewardVerification/`
types and require no changes to them.

(Originally written on top of #6663; moved to this base PR so the type
and its tests ship together rather than splitting across PRs.)

* fix: warn when reward verification reward value is not a JSON object

Previously, `decodeVerifiedReward` silently returned `.noReward` for any
of: missing reward key, null reward, or non-object reward value (e.g. a
string/number/array). The first two are expected; the last indicates a
backend mismatch and should be surfaced.

Now distinguish:
- absent or null reward → `.noReward` (silent, expected)
- present but not a JSON object → `.unsupportedReward` + `Logger.warn`

This matches the existing convention in this file of logging warnings
when decoding unrecognized/malformed values into fallback cases.

* Fix virtual currency reward amount type to integer.

Align reward payload modeling with backend semantics by decoding `amount` as Int and updating tests to reject fractional values as malformed rewards.

* fix: address review — couple verified status to reward

Make the reward payload part of the verified status case so invalid state combinations are unrepresentable and remove the verified fallback mapping.

* fix: address review — reject non-positive reward amounts

Treat virtual-currency rewards with non-positive amounts as malformed payloads and return unsupported reward with warning logs.

* fix: address review — reject empty reward currency code

Validate virtual-currency payloads require a non-empty code and treat empty code values as malformed unsupported rewards.

* fix: address review — remove non-actionable enum switch test

Drop the exhaustive switch smoke test in VerifiedRewardTests since it did not validate behavior and duplicated compiler guarantees.

* fix: address review — remove synthesized equatable assertions

Drop VerifiedReward tests that only mirrored compiler-synthesized Equatable behavior and keep behavior-focused coverage.

* Delete claude.yml workflow (#6688)

* Add workflowTrigger to ButtonComponent.Action (#6693)

* Add workflowTrigger case to ButtonComponent.Action

Mirrors Android's rename of Action.Workflow → Action.WorkflowTrigger
(purchases-android#3380). Decodes backend "type": "workflow" into
.workflowTrigger so workflow buttons are rendered (previously decoded
as .unknown and hidden) and track as "workflow_trigger" in analytics.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add tests for workflowTrigger action decoding and analytics value

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* Cache decoded images by file URL in `FileImageLoader`

`URL.asImageAndSize` (used by `FileImageLoader.loadFromCache`) previously
called `UIImage(contentsOfFile:)` / `NSImage(contentsOfFile:)` on every
access. A SwiftUI paywall with many `RemoteImage` instances (e.g. a looping
carousel that materialises multiple copies of each page) ends up hitting
this path hundreds of times because `StateObject(wrappedValue:)` evaluates
its closure on every host view re-init. UIKit/AppKit's internal caches
still cost ~0.5–1 ms per call, which adds up to ~100+ ms of main-thread
blocking for only a handful of unique URLs.

Introduce a process-wide `DecodedImageCache` backed by `NSCache<NSURL, _>`
so repeated lookups for the same URL become a dictionary hit. `NSCache`
evicts on memory pressure, so we don't pin images forever. Reads/writes
are serialised through a concurrent dispatch queue with a barrier on
writes to keep the cache thread-safe.

Made-with: Cursor

---------

Co-authored-by: Facundo Menzella <facumenzella@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Pol Miro <polmiro@gmail.com>
Co-authored-by: Cesar de la Vega <664544+vegaro@users.noreply.github.com>
This was referenced Apr 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants