* 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>
* 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
* 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
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.
* 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
* 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