Skip to content

Commit 61276c3

Browse files
authored
fix(activation)!: let .Reactive blocks use System.Reactive DisposeWith (#4452)
## What kind of change does this PR introduce? Bug fix, plus a dependency refresh. ## What is the new behavior? **On the `*.Reactive` leaves, the container `WhenActivated` hands to an activation block is usable as a System.Reactive `CompositeDisposable`.** - **A block can keep the disposal idiom a System.Reactive consumer already has.** `subscription.DisposeWith(d)` against `System.Reactive.Disposables.Fluent` compiles, and the subscription is disposed on deactivation. - **The block parameter is bound through a seam alias, `ActivationDisposables`.** It resolves to `MultipleDisposable` on the lean leaves and to `ReactiveUI.Primitives.Reactive.Disposables.ContainerDisposable` on `*.Reactive`, which converts to a `CompositeDisposable` it owns and disposes. - **Both the `IActivatableView` and `IActivatableViewModel` overloads are covered**, in the core mixins and the WPF ones. **Dependencies move to their current releases.** ReactiveUI.Primitives goes to 7.3.0, which is what supplies `ContainerDisposable`; TUnit, the analyzer set, MAUI, Xaml.Behaviors, the Microsoft.Testing packages and AndroidX Fragment come along with it. ## What is the current behavior? Closes #4434 - `WhenActivated` hands every leaf a `MultipleDisposable`. System.Reactive's fluent `DisposeWith` takes a `CompositeDisposable`, so a `*.Reactive` consumer gets `CS1503` with no overload to fall back to. - Adding a `WhenActivated(Action)` overload does not fix that. `Action` is contravariant, so `Action` wins as the better conversion target and an untyped `d => ...` still binds to the parameter that does not convert. The parameter has to be replaced rather than overloaded. ## What might this PR break? - **Binary breaking on the `*.Reactive` packages only.** `WhenActivated(Action)` becomes `WhenActivated(Action)`, so compiled consumers must recompile. Source that passes an untyped `d => ...` lambda is unaffected, and the lean packages are unchanged. - **`DisposeWith(null)` now throws.** ReactiveUI.Primitives 7.3.0 validates the container instead of treating null as a silent no-op, so a registration that previously vanished is now an `ArgumentNullException`. ## Checklist - [x] I have read the [Contribute guide](https://www.reactiveui.net/contribute/index.html) - [x] Tests have been added or updated (for bug fixes / features) - [ ] Docs have been added or updated (for bug fixes / features) - [x] Changes target the `main` branch - [x] PR title follows [Conventional Commits](https://www.conventionalcommits.org/) ## Additional information Most of the diff is regenerated public-API baselines. The authored changes are: - `src/ReactiveShim.props` - the `ActivationDisposables` alias on both sides of the seam - `src/ReactiveUI.Shared/Activation/ViewForMixins.cs` - `src/ReactiveUI.Wpf.Shared/WpfViewForMixins.cs` - `src/Directory.Packages.props` - four test files under `src/tests`
1 parent 6a2ef8a commit 61276c3

36 files changed

Lines changed: 204 additions & 141 deletions

File tree

‎src/Directory.Packages.props‎

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,16 @@
66

77
<PropertyGroup Label="Shared Version Variables">
88
<SplatVersion>21.0.0SplatVersion>
9-
<PrimitivesVersion>7.2.0PrimitivesVersion>
10-
<TUnitVersion>1.63.0TUnitVersion>
9+
<PrimitivesVersion>7.3.0PrimitivesVersion>
10+
<TUnitVersion>1.65.68TUnitVersion>
1111
13-
<RoslynCommonAnalyzersVersion>3.45.0RoslynCommonAnalyzersVersion>
13+
<RoslynCommonAnalyzersVersion>3.46.0RoslynCommonAnalyzersVersion>
1414
<XamarinAndroidXLifecycleLiveDataVersion>2.11.0.1XamarinAndroidXLifecycleLiveDataVersion>
1515
PropertyGroup>
1616

1717
<PropertyGroup Label="Framework-Aligned Versions">
18-
<MauiVersion Condition="$(TargetFramework.StartsWith('net10'))">10.0.90MauiVersion>
18+
<MauiVersion Condition="$(TargetFramework.StartsWith('net10'))">10.0.100MauiVersion>
1919
<MauiVersion Condition="$(TargetFramework.StartsWith('net11'))">11.0.0-preview.7.26406.9MauiVersion>
2020

2121
<AspNetVersion Condition="$(TargetFramework.StartsWith('netstandard'))">3.1.32AspNetVersion>
@@ -32,11 +32,11 @@
3232
<MicrosoftExtensionsVersion>10.0.11MicrosoftExtensionsVersion>
3333
<MicrosoftExtensionsVersion Condition="$(TargetFramework.StartsWith('net11'))">11.0.0-preview.7.26381.103MicrosoftExtensionsVersion>
3434

35-
<XamlBehaviorsWpfVersion Condition="$(TargetFramework.StartsWith('net4'))">1.1.142XamlBehaviorsWpfVersion>
36-
<XamlBehaviorsWpfVersion Condition="$(TargetFramework.StartsWith('net8'))">1.1.142XamlBehaviorsWpfVersion>
37-
<XamlBehaviorsWpfVersion Condition="$(TargetFramework.StartsWith('net9'))">1.1.142XamlBehaviorsWpfVersion>
38-
<XamlBehaviorsWpfVersion Condition="$(TargetFramework.StartsWith('net10'))">1.1.142XamlBehaviorsWpfVersion>
39-
<XamlBehaviorsWpfVersion Condition="$(TargetFramework.StartsWith('net11'))">1.1.142XamlBehaviorsWpfVersion>
35+
<XamlBehaviorsWpfVersion Condition="$(TargetFramework.StartsWith('net4'))">1.1.158XamlBehaviorsWpfVersion>
36+
<XamlBehaviorsWpfVersion Condition="$(TargetFramework.StartsWith('net8'))">1.1.158XamlBehaviorsWpfVersion>
37+
<XamlBehaviorsWpfVersion Condition="$(TargetFramework.StartsWith('net9'))">1.1.158XamlBehaviorsWpfVersion>
38+
<XamlBehaviorsWpfVersion Condition="$(TargetFramework.StartsWith('net10'))">1.1.158XamlBehaviorsWpfVersion>
39+
<XamlBehaviorsWpfVersion Condition="$(TargetFramework.StartsWith('net11'))">1.1.158XamlBehaviorsWpfVersion>
4040
PropertyGroup>
4141

4242
<ItemGroup Label="ReactiveUI Primitives">
@@ -75,8 +75,8 @@
7575
<PackageVersion Include="TUnit" Version="$(TUnitVersion)"/>
7676
<PackageVersion Include="TUnit.Core" Version="$(TUnitVersion)"/>
7777
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.9.0"/>
78-
<PackageVersion Include="Microsoft.Testing.Extensions.CodeCoverage" Version="18.10.0"/>
79-
<PackageVersion Include="Microsoft.Testing.Platform.MSBuild" Version="2.3.3"/>
78+
<PackageVersion Include="Microsoft.Testing.Extensions.CodeCoverage" Version="18.11.0"/>
79+
<PackageVersion Include="Microsoft.Testing.Platform.MSBuild" Version="2.4.0"/>
8080
<PackageVersion Include="Microsoft.Reactive.Testing" Version="7.0.0"/>
8181
<PackageVersion Include="System.Reactive" Version="7.0.0"/>
8282
<PackageVersion Include="Mocks.Maui" Version="1.2.5"/>
@@ -143,8 +143,8 @@
143143
ItemGroup>
144144

145145
<ItemGroup Label="Platform - Android (Fragment / Collection / SavedState / Preference)">
146-
<PackageVersion Include="Xamarin.AndroidX.Fragment" Version="1.8.9.3"/>
147-
<PackageVersion Include="Xamarin.AndroidX.Fragment.Ktx" Version="1.8.9.4"/>
146+
<PackageVersion Include="Xamarin.AndroidX.Fragment" Version="1.9.0"/>
147+
<PackageVersion Include="Xamarin.AndroidX.Fragment.Ktx" Version="1.9.0"/>
148148
<PackageVersion Include="Xamarin.AndroidX.Collection.Jvm" Version="1.6.0.1"/>
149149
<PackageVersion Include="Xamarin.AndroidX.Collection.Ktx" Version="1.6.0.1"/>
150150
<PackageVersion Include="Xamarin.AndroidX.SavedState" Version="1.5.0.1"/>

‎src/ReactiveShim.props‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@
4444
Signal.* factories. The shared source constructs these directly in place of in-repo sink forks; only
4545
the seam-neutral (both-leaf) concretes are named so the .Reactive leaf below stays buildable. -->
4646
<Using Include="ReactiveUI.Primitives.Advanced" />
47+
49+
<Using Include="ReactiveUI.Primitives.Disposables.MultipleDisposable" Alias="ActivationDisposables"/>
4750
ItemGroup>
4851

4952
7275
<Using Include="ReactiveUI.Primitives.Reactive.Advanced" />
76+
83+
<Using Include="ReactiveUI.Primitives.Reactive.Disposables.ContainerDisposable" Alias="ActivationDisposables"/>
7384
ItemGroup>
7485

7586
src/ReactiveUI.Reactive/PublicAPI/net10.0-android36.0/PublicAPI.txt‎
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1545,7 +1545,7 @@ namespace ReactiveUI.Reactive
15451545
public System.IDisposable WhenActivated() { }
15461546
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")]
15471547
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
1548-
public System.IDisposable WhenActivated(System.ActionMultipleDisposable> block) { }
1548+
public System.IDisposable WhenActivated(System.ActionReactive.Disposables.ContainerDisposable> block) { }
15491549
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")]
15501550
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
15511551
public System.IDisposable WhenActivated(System.Action> block) { }
@@ -1555,9 +1555,9 @@ namespace ReactiveUI.Reactive
15551555
public System.IDisposable WhenActivated(System.IObservable viewModelChanged) { }
15561556
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")]
15571557
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
1558-
public System.IDisposable WhenActivated(System.ActionMultipleDisposable> block, ReactiveUI.IViewFor? view) { }
1558+
public System.IDisposable WhenActivated(System.ActionReactive.Disposables.ContainerDisposable> block, ReactiveUI.IViewFor? view) { }
15591559
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
1560-
public System.IDisposable WhenActivated(System.ActionMultipleDisposable> block, System.IObservable viewModelChanged) { }
1560+
public System.IDisposable WhenActivated(System.ActionReactive.Disposables.ContainerDisposable> block, System.IObservable viewModelChanged) { }
15611561
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")]
15621562
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
15631563
public System.IDisposable WhenActivated(System.Action> block, ReactiveUI.IViewFor view) { }
@@ -1569,7 +1569,7 @@ namespace ReactiveUI.Reactive
15691569
}
15701570
extension(ReactiveUI.Reactive.IActivatableViewModel item)
15711571
{
1572-
public void WhenActivated(System.ActionMultipleDisposable> block) { }
1572+
public void WhenActivated(System.ActionReactive.Disposables.ContainerDisposable> block) { }
15731573
public void WhenActivated(System.Action> block) { }
15741574
public void WhenActivated(System.Func> block) { }
15751575
}

‎src/ReactiveUI.Reactive/PublicAPI/net10.0-ios/PublicAPI.txt‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2223,7 +2223,7 @@ namespace ReactiveUI.Reactive
22232223
public System.IDisposable WhenActivated() { }
22242224
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")]
22252225
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
2226-
public System.IDisposable WhenActivated(System.ActionMultipleDisposable> block) { }
2226+
public System.IDisposable WhenActivated(System.ActionReactive.Disposables.ContainerDisposable> block) { }
22272227
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")]
22282228
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
22292229
public System.IDisposable WhenActivated(System.Action> block) { }
@@ -2233,9 +2233,9 @@ namespace ReactiveUI.Reactive
22332233
public System.IDisposable WhenActivated(System.IObservable viewModelChanged) { }
22342234
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")]
22352235
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
2236-
public System.IDisposable WhenActivated(System.ActionMultipleDisposable> block, ReactiveUI.IViewFor? view) { }
2236+
public System.IDisposable WhenActivated(System.ActionReactive.Disposables.ContainerDisposable> block, ReactiveUI.IViewFor? view) { }
22372237
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
2238-
public System.IDisposable WhenActivated(System.ActionMultipleDisposable> block, System.IObservable viewModelChanged) { }
2238+
public System.IDisposable WhenActivated(System.ActionReactive.Disposables.ContainerDisposable> block, System.IObservable viewModelChanged) { }
22392239
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")]
22402240
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
22412241
public System.IDisposable WhenActivated(System.Action> block, ReactiveUI.IViewFor view) { }
@@ -2247,7 +2247,7 @@ namespace ReactiveUI.Reactive
22472247
}
22482248
extension(ReactiveUI.Reactive.IActivatableViewModel item)
22492249
{
2250-
public void WhenActivated(System.ActionMultipleDisposable> block) { }
2250+
public void WhenActivated(System.ActionReactive.Disposables.ContainerDisposable> block) { }
22512251
public void WhenActivated(System.Action> block) { }
22522252
public void WhenActivated(System.Func> block) { }
22532253
}

‎src/ReactiveUI.Reactive/PublicAPI/net10.0-maccatalyst/PublicAPI.txt‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2223,7 +2223,7 @@ namespace ReactiveUI.Reactive
22232223
public System.IDisposable WhenActivated() { }
22242224
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")]
22252225
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
2226-
public System.IDisposable WhenActivated(System.ActionMultipleDisposable> block) { }
2226+
public System.IDisposable WhenActivated(System.ActionReactive.Disposables.ContainerDisposable> block) { }
22272227
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")]
22282228
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
22292229
public System.IDisposable WhenActivated(System.Action> block) { }
@@ -2233,9 +2233,9 @@ namespace ReactiveUI.Reactive
22332233
public System.IDisposable WhenActivated(System.IObservable viewModelChanged) { }
22342234
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")]
22352235
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
2236-
public System.IDisposable WhenActivated(System.ActionMultipleDisposable> block, ReactiveUI.IViewFor? view) { }
2236+
public System.IDisposable WhenActivated(System.ActionReactive.Disposables.ContainerDisposable> block, ReactiveUI.IViewFor? view) { }
22372237
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
2238-
public System.IDisposable WhenActivated(System.ActionMultipleDisposable> block, System.IObservable viewModelChanged) { }
2238+
public System.IDisposable WhenActivated(System.ActionReactive.Disposables.ContainerDisposable> block, System.IObservable viewModelChanged) { }
22392239
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")]
22402240
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
22412241
public System.IDisposable WhenActivated(System.Action> block, ReactiveUI.IViewFor view) { }
@@ -2247,7 +2247,7 @@ namespace ReactiveUI.Reactive
22472247
}
22482248
extension(ReactiveUI.Reactive.IActivatableViewModel item)
22492249
{
2250-
public void WhenActivated(System.ActionMultipleDisposable> block) { }
2250+
public void WhenActivated(System.ActionReactive.Disposables.ContainerDisposable> block) { }
22512251
public void WhenActivated(System.Action> block) { }
22522252
public void WhenActivated(System.Func> block) { }
22532253
}

‎src/ReactiveUI.Reactive/PublicAPI/net10.0-macos/PublicAPI.txt‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1624,7 +1624,7 @@ namespace ReactiveUI.Reactive
16241624
public System.IDisposable WhenActivated() { }
16251625
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")]
16261626
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
1627-
public System.IDisposable WhenActivated(System.ActionMultipleDisposable> block) { }
1627+
public System.IDisposable WhenActivated(System.ActionReactive.Disposables.ContainerDisposable> block) { }
16281628
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")]
16291629
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
16301630
public System.IDisposable WhenActivated(System.Action> block) { }
@@ -1634,9 +1634,9 @@ namespace ReactiveUI.Reactive
16341634
public System.IDisposable WhenActivated(System.IObservable viewModelChanged) { }
16351635
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")]
16361636
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
1637-
public System.IDisposable WhenActivated(System.ActionMultipleDisposable> block, ReactiveUI.IViewFor? view) { }
1637+
public System.IDisposable WhenActivated(System.ActionReactive.Disposables.ContainerDisposable> block, ReactiveUI.IViewFor? view) { }
16381638
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
1639-
public System.IDisposable WhenActivated(System.ActionMultipleDisposable> block, System.IObservable viewModelChanged) { }
1639+
public System.IDisposable WhenActivated(System.ActionReactive.Disposables.ContainerDisposable> block, System.IObservable viewModelChanged) { }
16401640
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Evaluates expression-based member chains via reflection; members may be trimmed.")]
16411641
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
16421642
public System.IDisposable WhenActivated(System.Action> block, ReactiveUI.IViewFor view) { }
@@ -1648,7 +1648,7 @@ namespace ReactiveUI.Reactive
16481648
}
16491649
extension(ReactiveUI.Reactive.IActivatableViewModel item)
16501650
{
1651-
public void WhenActivated(System.ActionMultipleDisposable> block) { }
1651+
public void WhenActivated(System.ActionReactive.Disposables.ContainerDisposable> block) { }
16521652
public void WhenActivated(System.Action> block) { }
16531653
public void WhenActivated(System.Func> block) { }
16541654
}

0 commit comments

Comments
 (0)