Commit Graph
10 Commits
Author SHA1 Message Date
0b65086ec2 Add workflows network layer for multipage paywalls (#6557)
* Add workflows network layer for multipage paywalls

- Add `getWorkflows` and `getWorkflow` endpoint paths
- Add `WorkflowsListResponse` and `PublishedWorkflow` response models
- Add `WorkflowDetailProcessor` to handle `inline`/`use_cdn` response actions
- Add `WorkflowCdnFetcher` protocol and `DirectWorkflowCdnFetcher` implementation
- Add `GetWorkflowsOperation` and `GetWorkflowOperation` cacheable network operations
- Add `WorkflowsAPI` facade and wire into `Backend`
- Add unit tests for all new components

iOS equivalent of https://github.com/RevenueCat/purchases-android/pull/3300

Made-with: Cursor

* fix xcodeproj

* reset Package.resolved

* Update WorkflowStep models to match actual backend response

- Add WorkflowTrigger struct (name, type, action_id, component_id)
- Add triggers, outputs, and metadata fields to WorkflowStep
- Add metadata field to PublishedWorkflow
- Remove value field from WorkflowTriggerAction (backend uses step_id only)

Made-with: Cursor

* Update WorkflowResponseTests to match updated models

- Replace value/resolvedTargetStepId assertions with stepId
- Remove testDecodeWorkflowTriggerActionValueTakesPrecedence (value field removed)
- Add testDecodeWorkflowTrigger for new WorkflowTrigger struct
- Add testDecodePublishedWorkflowWithMetadata
- Update testDecodeWorkflowStepDefaults to cover triggers, outputs, metadata
- Add testDecodeWorkflowStepMatchingActualBackendResponse with real backend payload

Made-with: Cursor

* Disable file_length lint rule in HTTPRequestPath.swift

Made-with: Cursor

* Fix GetWorkflowOperation to compute result once before distributing to callbacks

CDN fetch and JSON decoding were running once per deduplicated callback.
Compute the Result<WorkflowFetchResult, BackendError> once outside the
performOnAllItemsAndRemoveFromCache loop, matching the pattern used by
GetOfferingsOperation and other operations in the codebase.

Made-with: Cursor

* Fix CDN fetcher: use URLSession instead of Data(contentsOf:), classify errors correctly

- Replace Data(contentsOf:) with URLSession.shared.dataTask + DispatchSemaphore
  in DirectWorkflowCdnFetcher; gets URLSession timeout, HTTP status validation,
  and proper network stack semantics
- Add WorkflowDetailProcessingError.cdnFetchFailed typed error so CDN I/O
  failures are distinguishable from envelope parsing failures
- Catch cdnFetchFailed in GetWorkflowOperation and map to NetworkError.networkError
  instead of NetworkError.decoding, fixing misleading error classification
- Update WorkflowDetailProcessorTests to assert the typed error is thrown

Made-with: Cursor

* Replace semaphore CDN fetch with async/await using withCheckedThrowingContinuation

- Make WorkflowCdnFetcher.fetchCompiledWorkflowData async throws; use
  withCheckedThrowingContinuation to bridge URLSession.dataTask into async,
  avoiding any thread-blocking
- Make WorkflowDetailProcessor.process async throws to propagate async
- Bridge into async in GetWorkflowOperation via Task {}; completion() is
  called inside the Task after CDN fetch and decoding complete
- Update WorkflowDetailProcessorTests to async throws with await

Made-with: Cursor

* Make CDN fetcher and processor completion-handler based for consistency

Avoids Task{} in GetWorkflowOperation and keeps all operations calling
completion() synchronously from within the HTTP callback, matching every
other operation in the codebase.

- WorkflowCdnFetcher.fetchCompiledWorkflowData now takes a completion handler;
  DirectWorkflowCdnFetcher uses URLSession.dataTask (non-blocking, no semaphore)
- WorkflowDetailProcessor.process now takes a completion handler; inline action
  completes synchronously, use_cdn fans out to the fetcher callback
- GetWorkflowOperation splits into getWorkflow/handleResponse/backendResult/
  distribute helpers to stay within line-length limits
- WorkflowDetailProcessorTests updated to use waitUntilValue pattern

Made-with: Cursor

* Fix ambiguous cache key delimiter in GetWorkflowOperation

Space-separated appUserID+workflowId could collide (e.g. user 'a b' + workflow 'c'
== user 'a' + workflow 'b c'). Use newline as delimiter, matching the precedent
set by GetWebBillingProductsOperation.

Made-with: Cursor

* PR comments

* remove cdn fetcher

* fix response in BackendGetWorkflowsTests.swift

* fix WorkflowResponseTests

* fix error

* [skip ci] Generating new test snapshots (#6584)

* [skip ci] Generating new test snapshots (#6585)

* [skip ci] Generating new test snapshots (#6586)

* [skip ci] Generating new test snapshots (#6587)

* [skip ci] Generating new test snapshots (#6588)

* [skip ci] Generating new test snapshots (#6589)

* [skip ci] Generating new test snapshots (#6590)

* Test CDN mock is not re-assignable per test

* [skip ci] Generating new test snapshots (#6597)

* [skip ci] Generating new test snapshots (#6598)

* [skip ci] Generating new test snapshots (#6599)

* [skip ci] Generating new test snapshots (#6600)

* [skip ci] Generating new test snapshots (#6601)

* [skip ci] Generating new test snapshots (#6602)

* [skip ci] Generating new test snapshots (#6603)

* getWorkflow is signed

* add type parameter

* add response verification for CDN response

* remove workflows list

* add value to WorkflowTriggerAction

* step id

* hash and filerepo

* missingCdnHash

* linter and project

* fix project

* revert Package.resolved

* @unchecked Sendable

* use hash in generateOrGetCachedFileURL

* fix compilation

* skip responseVerificationMode in cdn

* change basePath of caches

* remove reserialization

* Use explicit type in GetWorkflowOperation.createFactory for greppability

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

* Fix swiftlint identifier_name violation in WorkflowDetailProcessorTests

Rename short variable `d` to `doubleValue` in two `if case .double(let d)`
patterns to satisfy the identifier_name rule requiring names >= 3 characters.

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

* remove default FileRepositoryType

* fix project

* fix test

* test preserves camel case

* Generating new test snapshots for `feat/workflows-network-layer` - ios-15 (#6654)

* Generating new test snapshots for `feat/workflows-network-layer` - ios-14 (#6656)

---------

Co-authored-by: RevenueCat Git Bot <72824662+RCGitBot@users.noreply.github.com>
Co-authored-by: Facundo Menzella <facumenzella@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Facundo Menzella <facumenzella@users.noreply.github.com>
2026-04-21 12:32:01 +02:00
Rick d3b845c8e5 Fix cache files visible in documents directory: Etags, offerings and product entitlements mapping (#6020)
* Renamed applicationSupport directory type to persistence

* Only deleting the old diagnostics file when the new diagnostics file does not exist yet

* Implemented PR feedback

* Changed testing-only check from assertion to #if DEBUG

* Implemented migration + deleting LargeItemCache files from the Documents directory for Etags + DeviceCache caching

* Fixed compile issue after rebase

* Fixed test compilation after rebase

* Updated test

* Refactored logica to the specific classes that actually store the data, removing the logic from the large item cache

* Fix compilation issue

* Moved comment

* Implemented PR feedback

* Implemented most PR feedback

* Implemented PR feedback

* Simplified ETagManager's old Documents directory removal by just doing it on init if needed

* Simplified old documents directory removal in EtagManager

* Also deleting old files from documents if we're clearing caches

* Fixed swiftlint warning

* Simplified directory cleanup between tests

* Updated FileRepository to keep using the existing directory in order to avoid duplicate cache files or having to migrate them right now

* Fixed tests not compiling

* Added additional test assertions that check whether new cache directory was created

* Add locking to DeviceCache migration logic to be safe

* Handle multiple concurrent reads across threads only actually migrating the data once

* PR feedback tweak
2026-01-22 17:17:50 +01:00
Rick fef67cd5b5 Removed the use of @autoclosure from Logging methods in order to reduce binary size footprint (#5956)
* Removed the use of @autoclosure from Logging methods in order to reduce binary size footprint

* Restore change
2025-12-24 16:40:15 +01:00
Jacob Rakidzich ef3357f416 HOTFIX Media not loading (#5678)
* fix media not loading

* update logic and tests

* add docs
2025-10-17 14:38:04 -05:00
Jacob Rakidzich 04bc10266b Finalize video component to include a checksum, dark mode support, and optimize memory usage for large file downloads (#5631)
* Checksum and tests

* add checksum to video component models

* Update cache warming

* put the checksum in the right spot and update the view model

* File repo updates

* add missing assertion

* revert accidental trailiing comma

* Checksum from file

* Ensure cache invalidates when the checksum changes

* Use checksum as filename if available

* greedily fill space while loading occurs

* update remote image usage of the repository

* Reduce memory footprint of large file downloads by streaming the download to a temp file

* Update keyed deferred value storage key to include checksum

* remove unused argument

* remove wip comment

* Get tests to compile

* Write tests for the large item cache type

* remove test from the file repository suite

* trailing commas

* remove cache warming of videos

* remove un-needed change causing existential types

* delete the temporary file after success or failure of the code block

* log with the checksums

* optimize checksum generation to occur at the same time as the temp file storage

* starting to work on border gradients,

* Revert "starting to work on border gradients,"

This reverts commit 429e6d2fab.

* clarity regarding dark mode urls
2025-10-14 08:05:01 -05:00
Jacob Rakidzich 2316c689f4 Video Component Views (#5527)
This adds a video component for apple platforms: iOS, macOS, and watchOS. There is are differences in them. But the primary difference that is noticeable in the UX is that iOS is the only one that does not have a black background if the video aspect ratio is not the same as the view's and does not fill its size.

> [!NOTE]
> macOS and watchOS will ship with the standard black background on the video component. I don't have a good solution for handling that right now.
2025-09-29 15:16:13 -05:00
Josh Holtz c63523076a Store file repostory contents in base directory in cache (#5557)
* Store file repostory contents in base directory in cache

* Fixed tests
2025-09-11 18:29:36 -05:00
Josh Holtz a34be303b6 Paywalls image loading from cache is now synchronous (#5528)
* Paywalls image loading from cache is now synchronous

* Some improvements

* Does this fix emerge?

* This feels better

* Same image for loop

* Maybe this

* Maybe this

* This

* Maybe

* Maybe this

* This is it... paired with mark

* Fix some tabbing

* Fix zero test on expected size in remoteimage view

* Make work on tvos

* Fix lint

* How about this

* Be happy lint
2025-09-11 11:21:04 -05:00
Jacob Rakidzich 6c8140e058 Fix the tests and implicit returns that broke our CI on main (#5493)
* explicit returns

* wrap prefetch in high priority task to ensure yield picks things up.

* more explicit returns

* explicit self in closure
2025-08-21 22:05:35 -05:00
Jacob Rakidzich f730d9df52 Create File Repository for use in upcoming feature work (#5477)
* Pull files from staging branch to introduce a file repository and intelligent task sharing

* removed typed throws and fix some docs

* Make tests satisfy predictive swift 6 failures

* @_spi(Internal) public

* Stop using the shared instance in an instance method

* Pull cache directory logic out of file repository

* update cache naming conventions

* @fire-at-will feedback -> use url instead of path

* Use strings file pattern

* remove priority from repository

* Remove RC Internal access from keyed deferred value store

* Remove spi internal from cache and network service

* makeSUT -> makeSystemUnderTest

* remove public scope in extensions

* rename function for clarity
2025-08-21 05:41:52 -05:00