Search code examples
iosxamarin.iosdynamic-datareactiveuixamarin.ios-binding

How do I chain SourceList observation using ReactiveUI and DynamicData?


Apologies if the terminology is off; I'm an iOS developer having to use Xamarin.iOS to develop an app. I'm using ReactiveUI with DynamicData and an MVVM architecture. I'm fairly happy with RxSwift, and FRP concepts in general. I have a Model that publishes a SourceList<MyThing>, according to the docs, like so:

// Property declarations
private readonly SourceList<MyThing> Things;
public IObservableCollection<MyThing> ThingsBindable { get; }

// Later, in the constructor...
Things = new SourceList<MyThing>();
// Is this of the right type?
ThingsBindable = new ObservableCollectionExtended<MyThing>();
Things
    .Connect()
    .Bind(ThingsBindable)
    .Subscribe();

I can successfully use .BindTo() in my View (i.e. ViewController in iOS-land) to get a UITableView to update when the Model changes:

Model
    .WhenAnyValue(model => model.ThingsBindable)
    .BindTo<MyThing, MyThingTableViewCell>(
        tableView,
        new NSString("ThingCellIdentifier"),
        46, // Cell height
        cell => cell.Initialize());  

I'd like, instead of binding directly to the Model, to have the ViewModel subscribe-and-publish (or otherwise proxy) the SourceList<MyThing>, or the bindable version of this, so that the View is only using the ViewModel properties. The SourceList is declared private in the docs; I'm unsure of best practice here: do I make it public and do my Connect() in the ViewModel? Or is there a way of passing on the publicly exposed IObservableCollection<MyThing> ThingsBindable from the ViewModel? I'm also not convinced that ObservableCollectionExtended<MyThing> is the right type for the Bindable property, but it seems to work.

I've tried various combinations of .ToProperty(), .Bind(), .Publish() etc. and making a version of the View-binding Observable in the ViewModel to no avail and am now just throwing autocomplete at the wall to see what sticks. Any direction appreciated. TIA.


Solution

  • I think it was beginners misunderstanding. Here's what I've got working the way I want; maybe it will help other Xamarin.iOS/ReactiveUI/DynamicData newbies.

    In my model I declare both a private SourceList and a publicly exposed IObservableList<MyThing>:

    private readonly SourceList<MyThing> _ModelThings;
    public IObservableList<MyThing> ModelThings;
    

    Then instantiate them in my constructor:

    _ModelThings = new SourceList<MyThing>();
    ModelThings = _Things.AsObservableList();
    

    In my ViewModel I declare a local ObservableCollectionExtended<MyThing> and bind that to the Model's public property:

    public ObservableCollectionExtended<MyThing> ViewModelThings;
    
    // Then, in the constructor:
    ViewModelThings = new ObservableCollectionExtended<MyThing>();
    
    model.ModelThings
        .Connect()
        .Bind(ViewModelThings)
        .Subscribe();
    

    In my ViewController I bind the table to the ViewModel.ViewModelThings, as in the question. If I wanted to have another level of Model I could simply pass through the Model.ModelThings and .Connect().Bind() lower down, as Glenn hinted in his comment.

    FWIW, I found Roland's Blog (specifically the sections on Observable Lists/Caches) to be more straightforward to understand than the GitHub docs.