fix: restore backward compatibility in state subscription API#123
Conversation
Remove @mainactor requirement from Store extension methods that was accidentally introduced in v3.14.8. This restores compatibility with existing client code while preserving all thread safety improvements. - Store.subscribe() methods can now be called from any context - ObservableState classes remain @mainactor for thread safety - All crash fixes from v3.14.8 are preserved - Fixes splash screen hanging issue with existing subscription patterns Fixes issue where combinedRowndState. pattern was broken.
PR Compliance Guide 🔍Below is a summary of compliance checks for this PR:
Compliance status legend🟢 - Fully Compliant🟡 - Partial Compliant 🔴 - Not Compliant ⚪ - Requires Further Human Verification 🏷️ - Compliance label |
|||||||||||||||||||||||
PR Code Suggestions ✨Latest suggestions up to 913e087
Previous suggestionsSuggestions up to commit bb1fcdb
|
||||||||||||||||||||||||
Reviewer's guide (collapsed on small PRs)Reviewer's GuideRestores backward-compatible state subscription APIs by removing @mainactor from Store extension subscribe/subscribeThrottled helpers while retaining MainActor isolation inside ObservableState types for thread safety and crash fixes. Sequence diagram for state subscription from non_MainActor contextsequenceDiagram
actor App
participant BackgroundContext
participant RowndSDK
participant Store
participant ObservableState as ObservableState_MainActor
App->>BackgroundContext: launchAndInitialize()
BackgroundContext->>RowndSDK: Rownd.getInstance()
RowndSDK->>Store: state()
Note over BackgroundContext,Store: Non_MainActor context
BackgroundContext->>Store: subscribe(select, animation)
Store->>ObservableState: init(select, animation)
activate ObservableState
ObservableState-->>Store: ObservableState<T>
deactivate ObservableState
Store-->>BackgroundContext: ObservableState<T>
BackgroundContext->>ObservableState: access $current (Combine pipeline)
ObservableState->>ObservableState: updateOnMainActor()
ObservableState-->>BackgroundContext: emit state updates
BackgroundContext->>App: updateUIOnMainThread()
Class diagram for Store extensions and ObservableState typesclassDiagram
direction LR
class RowndState
class Store {
RowndState state
+subscribe<T>(selector, animation) ObservableState_T
+subscribe<Original,Derived>(selector, transform, animation) ObservableDerivedState_Original_Derived
+subscribeThrottled<T>(selector, throttleInMs, animation) ObservableThrottledState_T
+subscribeThrottled<Original,Derived>(selector, transform, throttleInMs, animation) ObservableDerivedThrottledState_Original_Derived
}
class ObservableState_T {
-T current
+init(select, animation)
+publisher() T
}
class ObservableDerivedState_Original_Derived {
-Derived current
+init(select, transform, animation)
+publisher() Derived
}
class ObservableThrottledState_T {
-T current
-Int throttleInMs
+init(select, animation, throttleInMs)
+publisher() T
}
class ObservableDerivedThrottledState_Original_Derived {
-Derived current
-Int throttleInMs
+init(select, transform, animation, throttleInMs)
+publisher() Derived
}
Store --> RowndState : has
Store --> ObservableState_T : creates
Store --> ObservableDerivedState_Original_Derived : creates
Store --> ObservableThrottledState_T : creates
Store --> ObservableDerivedThrottledState_Original_Derived : creates
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
…tibility - Mark initializers as nonisolated to allow calling from non-MainActor contexts - Keep classes @mainactor for thread safety of @published properties - Fix compilation errors introduced by removing @mainactor from Store extensions This preserves all thread safety improvements while restoring API compatibility.
The previous crash fix (7fe6b4a) added @mainactor to ObservableState and ObservableDerivedState, which broke customers who call store.subscribe(select:) from non-MainActor contexts. The follow-up attempts to mark init/subscribe as nonisolated caused compiler errors since nonisolated methods can't access actor-isolated properties. Instead, remove @mainactor from the class declarations entirely and keep thread safety through the existing dispatchToMainActor helper, which ensures all @published property mutations happen on the main thread. The applyStateUpdate methods are explicitly marked @mainactor so the compiler still enforces safety at the mutation callsite. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
User description
Problem
Version 3.14.8 accidentally introduced breaking changes to the state subscription API by adding
@MainActorrequirements to Store extension methods. This caused existing client code using patterns likeRownd.getInstance().state().subscribe(...)to fail with compiler errors when called from non-MainActor contexts.The most visible symptom was apps getting stuck on splash screens because state subscription wasn't working.
Solution
This PR restores backward compatibility by:
@MainActorrequirement from Store extension methodsImpact
combinedRowndState.$currentusage in client codeTesting
Existing client code patterns like this will work again:
Ready for v3.14.9 release.
PR Type
Bug fix
Description
Removes @mainactor requirement from Store extension methods
Restores backward compatibility with existing subscription patterns
Preserves thread safety of ObservableState classes
Fixes splash screen hanging with state subscription API
Diagram Walkthrough
File Walkthrough
ReSwiftObserver.swift
Remove @MainActor from Store extension methodsSources/Rownd/Models/Context/ReSwiftObserver.swift
Summary by Sourcery
Restore backward-compatible state subscription APIs for Rownd stores by relaxing MainActor constraints while preserving observable state safety.
Bug Fixes:
Enhancements: