reactiveui/ReactiveUI

[Bug]: Router State is No Longer Saved To Disk

worldbeater opened this issue ยท 4 comments

Describe the bug ๐Ÿž

Earlier, we were able to save RoutingState.NavigationStack to disk, as the collection was annotated as a [DataMember]: https://github.com/reactiveui/ReactiveUI/blame/a43e99ae2fbcd644460f245f471f573b7d3b66ec/src/ReactiveUI/Routing/RoutingState.cs#L26-L27

But now, the observable collection is [IgnoreDataMember]d: https://github.com/reactiveui/ReactiveUI/blob/main/src/ReactiveUI/Routing/RoutingState.cs#L50-L51

Step to reproduce

  1. Clone https://github.com/reactiveui/ReactiveUI.Samples/
  2. Navigate to https://github.com/reactiveui/ReactiveUI.Samples/tree/main/avalonia/suspension/
  3. Launch ReactiveUI.Samples.Suspension.sln,
  4. Try updating ReactiveUI to the latest version,
  5. RoutingState.NavigationStack is no longer saved to disk when the app is closed.

Reproduction repository

https://github.com/reactiveui/ReactiveUI.Samples/

Expected behavior

ReactiveUI suspension feature used to be working according to this guide: https://habr.com/en/post/462307/
And according to the documentation page: https://www.reactiveui.net/docs/handbook/data-persistence/
Probably worth marking NavigationStack as DataMember again and adding back a default ctor, if this won't cause issues, and if these were removed unintentionally.

ReactiveUI Version

17.0.1+

I encountered this issue and wrote this hack to get around it:

private sealed class NavigationStackResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var props = base.CreateProperties(type, memberSerialization);
        foreach (var prop in props)
        {
            if (prop.DeclaringType == typeof(RoutingState)
                && prop.PropertyName == nameof(RoutingState.NavigationStack))
            {
                prop.Ignored = false;
                prop.Writable = true;
                prop.ValueProvider = new NavigationStackValueProvider(prop.ValueProvider!);
            }
        }
        return props;
    }
}

private sealed class NavigationStackValueProvider : IValueProvider
{
    private readonly IValueProvider _Original;

    public NavigationStackValueProvider(IValueProvider original)
    {
        _Original = original;
    }

    public object? GetValue(object target)
        => _Original.GetValue(target);

    public void SetValue(object target, object? value)
    {
        var castedTarget = (RoutingState)target!;
        var castedValue = (IEnumerable<IRoutableViewModel>)value!;

        castedTarget.NavigationStack.Clear();
        foreach (var vm in castedValue)
        {
            castedTarget.NavigationStack.Add(vm);
        }
    }
}

If anyone is willing, be happy to take a PR for the affected systems. I been under the pump lately and should be a easy PR for anyone to pick up.

Also thanks for the workaround @advorange