When using RxApp.MainThreadScheduler or RxApp.TaskpoolScheduler in your code, since the entire RxApp class triggers initialization that is marked with RequiresUnreferencedCode attributes, any code that consumes these schedulers must also be marked with the same attributes.
This is particularly problematic when creating observables in ViewModels, Repositories, or other deeper code that is consumed by multiple sources, as it forces all consumers to add RequiresUnreferencedCode attributes.
The new RxSchedulers static class provides access to the same scheduler functionality without requiring unreferenced code attributes. This class contains only the scheduler properties and doesn't trigger the Splat dependency injection initialization that requires reflection.
// Old way - requires RequiresUnreferencedCode attribute
[RequiresUnreferencedCode("Uses RxApp which may require unreferenced code")]
public IObservable<string> GetDataOld()
{
return Observable.Return("data")
.ObserveOn(RxApp.MainThreadScheduler); // Triggers RequiresUnreferencedCode
}
// New way - no attributes required
public IObservable<string> GetDataNew()
{
return Observable.Return("data")
.ObserveOn(RxSchedulers.MainThreadScheduler); // No attributes needed!
}public class MyViewModel : ReactiveObject
{
private readonly ObservableAsPropertyHelper<string> _greeting;
public MyViewModel()
{
// Using RxSchedulers avoids RequiresUnreferencedCode
_greeting = this.WhenAnyValue(x => x.Name)
.Select(name => $"Hello, {name ?? "World"}!")
.ObserveOn(RxSchedulers.MainThreadScheduler) // No attributes needed!
.ToProperty(this, nameof(Greeting), scheduler: RxSchedulers.MainThreadScheduler);
}
public string? Name { get; set; }
public string Greeting => _greeting.Value;
}public class DataRepository
{
public IObservable<string> GetProcessedData()
{
// Using RxSchedulers in repository code doesn't force consumers
// to add RequiresUnreferencedCode attributes
return GetRawData()
.ObserveOn(RxSchedulers.TaskpoolScheduler) // Background processing
.Select(ProcessData)
.ObserveOn(RxSchedulers.MainThreadScheduler); // UI updates
}
}// New factory methods that use RxSchedulers internally
var property1 = ReactiveProperty<string>.Create(); // No attributes required
var property2 = ReactiveProperty<string>.Create("initial value");
var property3 = ReactiveProperty<int>.Create(42, skipCurrentValueOnSubscribe: false, allowDuplicateValues: true);RxSchedulers.MainThreadScheduler- Scheduler for UI thread operations (no unit test detection)RxSchedulers.TaskpoolScheduler- Scheduler for background operations (no unit test detection)
ReactiveProperty<T>.Create()- Creates with default schedulerReactiveProperty<T>.Create(T initialValue)- Creates with initial valueReactiveProperty<T>.Create(T initialValue, bool skipCurrentValueOnSubscribe, bool allowDuplicateValues)- Full configurationReactiveProperty<T>.Create(T initialValue, IScheduler scheduler, bool skipCurrentValueOnSubscribe, bool allowDuplicateValues)- Custom scheduler
RxAppschedulers still work as before - no breaking changesRxAppandRxSchedulersare kept synchronized when schedulers are set- For code that needs unit test detection, continue using
RxAppschedulers - For new code that doesn't need unit test detection, prefer
RxSchedulers
- Creating library code that shouldn't require
RequiresUnreferencedCodeattributes - Building ViewModels, repositories, or services consumed by multiple sources
- You don't need automatic unit test scheduler detection
- You want to avoid triggering ReactiveUI's dependency injection initialization
- You need automatic unit test scheduler detection
- You're already using other
RxAppfeatures - Existing code that's already marked with
RequiresUnreferencedCode - You need the full ReactiveUI initialization sequence
To migrate existing code from RxApp to RxSchedulers:
- Replace
RxApp.MainThreadSchedulerwithRxSchedulers.MainThreadScheduler - Replace
RxApp.TaskpoolSchedulerwithRxSchedulers.TaskpoolScheduler - Remove
RequiresUnreferencedCodeandRequiresDynamicCodeattributes if they were only needed for scheduler access - Use
ReactiveProperty<T>.Create()factory methods instead of constructors - Test that unit tests still work (you may need to manually set test schedulers if you relied on automatic detection)
RxSchedulersprovides a simplified version without unit test detection- In unit test environments, you may need to manually set the schedulers if you were relying on automatic detection
- The schedulers default to
DefaultScheduler.Instancefor main thread andTaskPoolScheduler.Defaultfor background - This solution maintains full backwards compatibility with existing code