├── Backpressure.md ├── IObservableExtensions.cs ├── IObservableUtil.MergeSignals.cs ├── Old Hands-on-Lab Rx Samples.pdf ├── OperatorsByCategroy.md ├── README.md ├── ReactiveUIExtensions ├── Rx Design Guidelines.pdf ├── RxGlossary.md ├── RxNet.md ├── RxUI.md └── Samples └── Xamarin.Android └── ReactiveRecyclerView.md /Backpressure.md: -------------------------------------------------------------------------------- 1 | # Backpressure 2 | 3 | Strategies for coping with Observables that produce items more rapidly than their observers consume them 4 | 5 | In ReactiveX it is not difficult to get into a situation in which an Observable is emitting items more rapidly than an operator or observer can consume them. This presents the problem of what to do with such a growing backlog of unconsumed items. 6 | 7 | For example, imagine using the Zip operator to zip together two infinite Observables, one of which emits items twice as frequently as the other. A naive implementation of the operator would have to maintain an ever-expanding buffer of items emitted by the faster Observable to eventually combine with items emitted by the slower one. This could cause ReactiveX to seize an unwieldy amount of system resources. 8 | 9 | There are a variety of strategies with which you can exercise flow control and backpressure in ReactiveX in order to alleviate the problems caused when a quickly-producing Observable meets a slow-consuming observer, which include, in some ReactiveX implementations, reactive pull backpressure and some backpressure-specific operators. 10 | 11 | A cold Observable emits a particular sequence of items, but can begin emitting this sequence when its observer finds it to be convenient, and at whatever rate the observer desires, without disrupting the integrity of the sequence. For example if you convert a static iterable into an Observable, that Observable will emit the same sequence of items no matter when it is later subscribed to or how frequently those items are observed. Examples of items emitted by a cold Observable might include the results of a database query, file retrieval, or web request. 12 | 13 | A hot Observable begins generating items to emit immediately when it is created. Subscribers typically begin observing the sequence of items emitted by a hot Observable from somewhere in the middle of the sequence, beginning with the first item emitted by the Observable subsequent to the establishment of the subscription. Such an Observable emits items at its own pace, and it is up to its observers to keep up. Examples of items emitted by a hot Observable might include mouse & keyboard events, system events, or stock prices. 14 | 15 | When a cold Observable is multicast (when it is converted into a connectable Observable and its Connect method is called), it effectively becomes hot and for the purposes of backpressure and flow-control it should be treated as a hot Observable. 16 | 17 | Cold Observables are ideal for the reactive pull model of backpressure implemented by some implementations of ReactiveX (which is described elsewhere). Hot Observables typically do not cope well with a reactive pull model, and are better candidates for other flow control strategies, such as the use of the operators described on this page, or operators like Buffer, Sample, Debounce, or Window. -------------------------------------------------------------------------------- /IObservableExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reactive; 4 | using System.Reactive.Disposables; 5 | using System.Reactive.Linq; 6 | using System.Runtime.CompilerServices; 7 | using System.Threading; 8 | using ReactiveUI; 9 | 10 | namespace RxCheatsheet 11 | { 12 | public static class IObservableExtensions 13 | { 14 | /// 15 | /// Convenience method for Where(x => x != null). 16 | /// 17 | /// 18 | // Credit: Kent Boogaart 19 | /// 20 | public static IObservable WhereNotNull(this IObservable @this) 21 | { 22 | return @this.Where(x => x != null); 23 | } 24 | 25 | /// 26 | /// Convenience method for Select(_ => Unit.Default). 27 | /// 28 | /// 29 | // Credit: Kent Boogaart 30 | /// 31 | public static IObservable ToSignal(this IObservable @this) 32 | { 33 | return @this.Select(_ => Unit.Default); 34 | } 35 | 36 | /// 37 | /// Allows user to invoke actions upon subscription and disposal. 38 | /// 39 | /// 40 | // Credit: Kent Boogaart 41 | // https://github.com/kentcb/YouIandReactiveUI 42 | /// 43 | public static IObservable DoLifetime(this IObservable @this, Action subscribed, Action unsubscribed) 44 | { 45 | return Observable 46 | .Create( 47 | observer => 48 | { 49 | subscribed(); 50 | 51 | var disposables = new CompositeDisposable(); 52 | @this 53 | .Subscribe(observer) 54 | .DisposeWith(disposables); 55 | Disposable 56 | .Create(() => unsubscribed()) 57 | .DisposeWith(disposables); 58 | 59 | return disposables; 60 | }); 61 | } 62 | 63 | /// 64 | /// Subscribes to the given observable and provides source code info about the caller when an 65 | /// exception is thrown, without the user needing to supply an onError handler. 66 | /// 67 | /// 68 | // Credit: Kent Boogaart 69 | // https://github.com/kentcb/YouIandReactiveUI 70 | /// 71 | public static IDisposable SubscribeSafe( 72 | this IObservable @this, 73 | [CallerMemberName]string callerMemberName = null, 74 | [CallerFilePath]string callerFilePath = null, 75 | [CallerLineNumber]int callerLineNumber = 0) 76 | { 77 | return @this 78 | .Subscribe( 79 | _ => { }, 80 | ex => 81 | { 82 | // Replace with your logger library. 83 | Console.Error.WriteLine( 84 | "An exception went unhandled: {0}" + 85 | "Caller member name: {1}, " + 86 | "caller file path: {2}, " + 87 | "caller line number: {3}.", 88 | ex, 89 | callerMemberName, 90 | callerFilePath, 91 | callerLineNumber); 92 | 93 | // Delete this line if you're not using ReactiveUI. 94 | RxApp.DefaultExceptionHandler.OnNext(ex); 95 | }); 96 | } 97 | 98 | /// 99 | /// Subscribes to the given observable and provides source code info about the caller when an 100 | /// exception is thrown, without the user needing to supply an onError handler. 101 | /// 102 | /// 103 | // Credit: Kent Boogaart 104 | // https://github.com/kentcb/YouIandReactiveUI 105 | /// 106 | public static IDisposable SubscribeSafe( 107 | this IObservable @this, 108 | Action onNext, 109 | [CallerMemberName]string callerMemberName = null, 110 | [CallerFilePath]string callerFilePath = null, 111 | [CallerLineNumber]int callerLineNumber = 0) 112 | { 113 | return @this 114 | .Subscribe( 115 | onNext, 116 | ex => 117 | { 118 | // Replace with your logger library. 119 | Console.Error.WriteLine( 120 | "An exception went unhandled: {0}" + 121 | "Caller member name: {1}, " + 122 | "caller file path: {2}, " + 123 | "caller line number: {3}.", 124 | ex, 125 | callerMemberName, 126 | callerFilePath, 127 | callerLineNumber); 128 | 129 | // Delete this line if you're not using ReactiveUI. 130 | RxApp.DefaultExceptionHandler.OnNext(ex); 131 | }); 132 | } 133 | 134 | /// 135 | /// Allows the user to perform an action based on the current and previous emitted items. 136 | /// 137 | /// 138 | // Credit: James World 139 | // http://www.zerobugbuild.com/?p=213 140 | /// 141 | public static IObservable WithPrevious(this IObservable @this, Func projection) 142 | { 143 | return @this 144 | .Scan( 145 | Tuple.Create(default(T), default(T)), 146 | (acc, current) => Tuple.Create(acc.Item2, current)) 147 | .Select(t => projection(t.Item1, t.Item2)); 148 | } 149 | 150 | /// 151 | /// Limits the rate at which events arrive from an Rx stream. 152 | /// 153 | /// 154 | // Credit: James World 155 | // http://www.zerobugbuild.com/?p=323 156 | /// 157 | public static IObservable MaxRate(this IObservable @this, TimeSpan interval) 158 | { 159 | return @this 160 | .Select( 161 | x => 162 | { 163 | return Observable 164 | .Empty() 165 | .Delay(interval) 166 | .StartWith(x); 167 | }) 168 | .Concat(); 169 | } 170 | 171 | /// 172 | /// Like TakeWhile, except includes the emitted item that triggered the exit condition. 173 | /// 174 | /// 175 | /// Credit: Someone's answer on Stack Overflow 176 | /// 177 | public static IObservable TakeWhileInclusive(this IObservable @this, Func predicate) 178 | { 179 | return @this 180 | .Publish(x => x.TakeWhile(predicate) 181 | .Merge(x.SkipWhile(predicate).Take(1))); 182 | } 183 | 184 | /// 185 | /// Buffers items in a stream until the provided predicate is true. 186 | /// 187 | /// 188 | /// Credit: Someone's answer on Stack Overflow 189 | /// 190 | public static IObservable> BufferUntil(this IObservable @this, Func predicate) 191 | { 192 | var published = @this.Publish().RefCount(); 193 | return published.Buffer(() => published.Where(predicate)); 194 | } 195 | 196 | /// 197 | /// Prints a detailed log of what your Rx query is doing. 198 | /// 199 | /// 200 | // Credit: James World 201 | // https://stackoverflow.com/questions/20220755/how-can-i-see-what-my-reactive-extensions-query-is-doing 202 | /// 203 | public static IObservable Spy(this IObservable @this, string opName = null) 204 | { 205 | opName = opName ?? "IObservable"; 206 | Console.WriteLine("{0}: Observable obtained on Thread: {1}", opName, Thread.CurrentThread.ManagedThreadId); 207 | 208 | return Observable.Create( 209 | obs => 210 | { 211 | Console.WriteLine("{0}: Subscribed to on Thread: {1}", opName, Thread.CurrentThread.ManagedThreadId); 212 | 213 | try 214 | { 215 | var subscription = @this 216 | .Do( 217 | x => Console.WriteLine("{0}: OnNext({1}) on Thread: {2}", opName, x, Thread.CurrentThread.ManagedThreadId), 218 | ex => Console.WriteLine("{0}: OnError({1}) on Thread: {2}", opName, ex, Thread.CurrentThread.ManagedThreadId), 219 | () => Console.WriteLine("{0}: OnCompleted() on Thread: {1}", opName, Thread.CurrentThread.ManagedThreadId)) 220 | .Subscribe(obs); 221 | 222 | return new CompositeDisposable( 223 | subscription, 224 | Disposable.Create(() => Console.WriteLine("{0}: Cleaned up on Thread: {1}", opName, Thread.CurrentThread.ManagedThreadId))); 225 | } 226 | finally 227 | { 228 | Console.WriteLine("{0}: Subscription completed.", opName); 229 | } 230 | }); 231 | } 232 | 233 | /// 234 | /// Prints the provided name next to stream emissions (useful for debugging). 235 | /// 236 | /// 237 | // Credit: Lee Campbell 238 | // http://www.introtorx.com/Content/v1.0.10621.0/07_Aggregation.html#Aggregation 239 | /// 240 | public static void Dump(this IObservable @this, string name) 241 | { 242 | @this.Subscribe( 243 | i => Console.WriteLine("{0}-->{1}", name, i), 244 | ex => Console.WriteLine("{0} failed-->{1}", name, ex.Message), 245 | () => Console.WriteLine("{0} completed", name)); 246 | } 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /IObservableUtil.MergeSignals.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reactive; 4 | using System.Reactive.Linq; 5 | 6 | namespace RxCheatsheet 7 | { 8 | public static class IObservableUtil 9 | { 10 | /// 11 | /// Merges observables of any type into a single IObservable. 12 | /// 13 | public static IObservable MergeSignals( 14 | IObservable o1, 15 | IObservable o2) 16 | { 17 | return Observable.Merge( 18 | o1.Select(_ => Unit.Default), 19 | o2.Select(_ => Unit.Default)); 20 | } 21 | 22 | /// 23 | /// Merges observables of any type into a single IObservable. 24 | /// 25 | public static IObservable MergeSignals( 26 | IObservable o1, 27 | IObservable o2, 28 | IObservable o3) 29 | { 30 | return Observable.Merge( 31 | o1.Select(_ => Unit.Default), 32 | o2.Select(_ => Unit.Default), 33 | o3.Select(_ => Unit.Default)); 34 | } 35 | 36 | /// 37 | /// Merges observables of any type into a single IObservable. 38 | /// 39 | public static IObservable MergeSignals( 40 | IObservable o1, 41 | IObservable o2, 42 | IObservable o3, 43 | IObservable o4) 44 | { 45 | return Observable.Merge( 46 | o1.Select(_ => Unit.Default), 47 | o2.Select(_ => Unit.Default), 48 | o3.Select(_ => Unit.Default), 49 | o4.Select(_ => Unit.Default)); 50 | } 51 | 52 | /// 53 | /// Merges observables of any type into a single IObservable. 54 | /// 55 | public static IObservable MergeSignals( 56 | IObservable o1, 57 | IObservable o2, 58 | IObservable o3, 59 | IObservable o4, 60 | IObservable o5) 61 | { 62 | return Observable.Merge( 63 | o1.Select(_ => Unit.Default), 64 | o2.Select(_ => Unit.Default), 65 | o3.Select(_ => Unit.Default), 66 | o4.Select(_ => Unit.Default), 67 | o5.Select(_ => Unit.Default)); 68 | } 69 | 70 | /// 71 | /// Merges observables of any type into a single IObservable. 72 | /// 73 | public static IObservable MergeSignals( 74 | IObservable o1, 75 | IObservable o2, 76 | IObservable o3, 77 | IObservable o4, 78 | IObservable o5, 79 | IObservable o6) 80 | { 81 | return Observable.Merge( 82 | o1.Select(_ => Unit.Default), 83 | o2.Select(_ => Unit.Default), 84 | o3.Select(_ => Unit.Default), 85 | o4.Select(_ => Unit.Default), 86 | o5.Select(_ => Unit.Default), 87 | o6.Select(_ => Unit.Default)); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Old Hands-on-Lab Rx Samples.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cabauman/Rx.Net-ReactiveUI-CheatSheet/214afc077180a05f5ea5b3f4e4da7ba4d037f19a/Old Hands-on-Lab Rx Samples.pdf -------------------------------------------------------------------------------- /OperatorsByCategroy.md: -------------------------------------------------------------------------------- 1 | # Operators By Category 2 | 3 | ## Creating Observables 4 | 5 | Operators that originate new Observables. 6 | 7 | - Create: create an Observable from scratch by calling observer methods programmatically 8 | - Defer: do not create the Observable until the observer subscribes, and create a fresh Observable for each observer 9 | - Empty/Never/Throw/Return: create Observables that have very precise and limited behavior 10 | - Interval: create an Observable that emits a sequence of integers spaced by a particular time interval 11 | - Range: create an Observable that emits a range of sequential integers 12 | - Repeat: create an Observable that emits a particular item or sequence of items repeatedly 13 | - Start: create an Observable that emits the return value of a function 14 | - Timer: create an Observable that emits a single item after a given delay 15 | 16 | 17 | ## Transforming Observables 18 | 19 | Operators that transform items that are emitted by an Observable. 20 | 21 | - Buffer: periodically gather items from an Observable into bundles and emit these bundles rather than emitting the items one at a time 22 | - SelectMany: transform the items emitted by an Observable into Observables, then flatten the emissions from those into a single - Observable 23 | - GroupBy: divide an Observable into a set of Observables that each emit a different group of items from the original Observable, - organized by key 24 | - Select: transform the items emitted by an Observable by applying a function to each item 25 | - Scan: apply a function to each item emitted by an Observable, sequentially, and emit each successive value 26 | - Window: periodically subdivide items from an Observable into Observable windows and emit these windows rather than emitting the items one at a time 27 | 28 | 29 | ## Filtering Observables 30 | 31 | Operators that selectively emit items from a source Observable. 32 | 33 | - Distinct: suppress duplicate items emitted by an Observable 34 | - ElementAt: emit only item n emitted by an Observable 35 | - Where: emit only those items from an Observable that pass a predicate test 36 | - First: emit only the first item, or the first item that meets a condition, from an Observable 37 | - IgnoreElements: do not emit any items from an Observable but mirror its termination notification 38 | - Last: emit only the last item emitted by an Observable 39 | - Sample: emit the most recent item emitted by an Observable within periodic time intervals 40 | - Skip: suppress the first n items emitted by an Observable 41 | - SkipLast: suppress the last n items emitted by an Observable 42 | - Take: emit only the first n items emitted by an Observable 43 | - TakeLast: emit only the last n items emitted by an Observable 44 | 45 | 46 | ## Combining Observables 47 | 48 | Operators that work with multiple source Observables to create a single Observable 49 | 50 | - And/Then/When: combine sets of items emitted by two or more Observables by means of Pattern and Plan intermediaries 51 | - CombineLatest: when an item is emitted by either of two Observables, combine the latest item emitted by each Observable via a - specified function and emit items based on the results of this function 52 | - Join: combine items emitted by two Observables whenever an item from one Observable is emitted during a time window defined - according to an item emitted by the other Observable 53 | - Merge: combine multiple Observables into one by merging their emissions 54 | - StartWith: emit a specified sequence of items before beginning to emit the items from the source Observable 55 | - Switch: convert an Observable that emits Observables into a single Observable that emits the items emitted by the - most-recently-emitted of those Observables 56 | - Zip: combine the emissions of multiple Observables together via a specified function and emit single items for each combination based on the results of this function 57 | 58 | 59 | ## Error Handling Operators 60 | 61 | Operators that help to recover from error notifications from an Observable 62 | 63 | - Catch: recover from an onError notification by continuing the sequence without error 64 | - Retry: if a source Observable sends an onError notification, resubscribe to it in the hopes that it will complete without error 65 | 66 | 67 | ## Observable Utility Operators 68 | 69 | A toolbox of useful Operators for working with Observables 70 | 71 | - Delay: shift the emissions from an Observable forward in time by a particular amount 72 | - Do: register an action to take upon a variety of Observable lifecycle events 73 | - Materialize/Dematerialize: represent both the items emitted and the notifications sent as emitted items, or reverse this process 74 | - ObserveOn: specify the scheduler on which an observer will observe this Observable 75 | - Serialize: force an Observable to make serialized calls and to be well-behaved 76 | - Subscribe: operate upon the emissions and notifications from an Observable 77 | - SubscribeOn: specify the scheduler an Observable should use when it is subscribed to 78 | - TimeInterval: convert an Observable that emits items into one that emits indications of the amount of time elapsed between those - emissions 79 | - Timeout: mirror the source Observable, but issue an error notification if a particular period of time elapses without any emitted - items 80 | - Timestamp: attach a timestamp to each item emitted by an Observable 81 | - Using: create a disposable resource that has the same lifespan as the Observable 82 | 83 | 84 | ## Conditional and Boolean Operators 85 | 86 | Operators that evaluate one or more Observables or items emitted by Observables 87 | 88 | - All: determine whether all items emitted by an Observable meet some criteria 89 | - Amb: given two or more source Observables, emit all of the items from only the first of these Observables to emit an item 90 | - Contains: determine whether an Observable emits a particular item or not 91 | - DefaultIfEmpty: emit items from the source Observable, or a default item if the source Observable emits nothing 92 | - SequenceEqual: determine whether two Observables emit the same sequence of items 93 | - SkipUntil: discard items emitted by an Observable until a second Observable emits an item 94 | - SkipWhile: discard items emitted by an Observable until a specified condition becomes false 95 | - TakeUntil: discard items emitted by an Observable after a second Observable emits an item or terminates 96 | - TakeWhile: discard items emitted by an Observable after a specified condition becomes false 97 | 98 | 99 | ## Mathematical and Aggregate Operators 100 | 101 | Operators that operate on the entire sequence of items emitted by an Observable 102 | 103 | - Average: calculates the average of numbers emitted by an Observable and emits this average 104 | - Concat: emit the emissions from two or more Observables without interleaving them 105 | - Count: count the number of items emitted by the source Observable and emit only this value 106 | - Max: determine, and emit, the maximum-valued item emitted by an Observable 107 | - Min: determine, and emit, the minimum-valued item emitted by an Observable 108 | - Aggregate/Reduce: apply a function to each item emitted by an Observable, sequentially, and emit the final value 109 | - Sum: calculate the sum of numbers emitted by an Observable and emit this sum 110 | 111 | 112 | ## Operators to Convert Observables 113 | 114 | Operators that convert an Observable into another object or data structure 115 | 116 | - ToTask 117 | 118 | 119 | ## Backpressure Operators 120 | 121 | Operators that help cope with Observables that produce items more rapidly than their observers consume them 122 | 123 | - Buffer 124 | - Sample 125 | - Window 126 | 127 | # Flattening Operators 128 | 129 | - Concat: for every Future Result, run them in original order, one at a time 130 | - Merge: for every Future Result, subscribe ASAP and return results as they come in, even if they're crazy out of order 131 | - Switch: only care about the newest Future Result, if a new Future Result comes in while the old one is still in-progress, throw it away 132 | - Exhaust (RsJS): opposite of Switch; only cares about one Future Result at a time and ignores any that comes in the interim -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rx.Net-ReactiveUI-CheatSheet 2 | A collection of links and snippets to resources, samples, answers, people, videos, etc. related to Rx.Net and ReactiveUI. 3 | 4 | [Rx.Net CheatSheet](RxNet.md) 5 | 6 | [ReactiveUI CheatSheet](RxUI.md) 7 | 8 | ## Contributing 9 | 10 | No contribution guidelines, at the moment. Just make a pull request if you have something to contribute, and we'll go from there. 11 | 12 | ## Authors 13 | 14 | * **Colt Bauman** - *Initial work* - [cabauman](https://github.com/cabauman) 15 | -------------------------------------------------------------------------------- /ReactiveUIExtensions: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reactive.Linq; 3 | using ReactiveUI; 4 | using Splat; 5 | 6 | namespace RxCheatsheet 7 | { 8 | public static class ReactiveUIExtensions 9 | { 10 | /// 11 | /// Turns *view model* activation into an IObservable stream. 12 | /// 13 | /// 14 | // Credit: Kent Boogaart 15 | // https://github.com/kentcb/YouIandReactiveUI 16 | /// 17 | public static IObservable GetIsActivated(this ISupportsActivation @this) => 18 | Observable 19 | .Merge( 20 | @this.Activator.Activated.Select(_ => true), 21 | @this.Activator.Deactivated.Select(_ => false)) 22 | .Replay(1) 23 | .RefCount(); 24 | 25 | /// 26 | /// Turns *view* activation into an IObservable stream. 27 | /// 28 | /// 29 | // Credit: Kent Boogaart 30 | // https://github.com/kentcb/YouIandReactiveUI 31 | /// 32 | public static IObservable GetIsActivated(this IActivatable @this) 33 | { 34 | var activationForViewFetcher = Locator.Current.GetService(); 35 | return activationForViewFetcher 36 | .GetActivationForView(@this) 37 | .Replay(1) 38 | .RefCount(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Rx Design Guidelines.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cabauman/Rx.Net-ReactiveUI-CheatSheet/214afc077180a05f5ea5b3f4e4da7ba4d037f19a/Rx Design Guidelines.pdf -------------------------------------------------------------------------------- /RxGlossary.md: -------------------------------------------------------------------------------- 1 | # Rx Glossary 2 | 3 | ## Observable.FromAsync vs Observable.FromAsync vs Task.ToObservable 4 | 5 | - FromAsync starts a new async operation for every subscription. 6 | - StartAsync and ToObservable require an already running task. 7 | - ToObservable doesn't support cancellation. 8 | - FromAsync is basically `Observable.Defer(() => Observable.StartAsync(...))` 9 | - One use for FromAsync is to control reentrancy for multiple calls to an async method. 10 | - Concat ensures that there will be no overlapping in the execution of the tasks. 11 | 12 | Source: https://github.com/dotnet/reactive/issues/459 13 | 14 | ## Subscribe overload that accepts a CancellationToken 15 | 16 | ``` 17 | var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); 18 | Observable.Interval(TimeSpan.FromSeconds(0.5)) 19 | .Subscribe(_ => Console.WriteLine(DateTime.UtcNow), cts.Token); 20 | Thread.Sleep(10000); 21 | ``` 22 | 23 | Source: https://stackoverflow.com/a/35367449/5984310 24 | 25 | ## Subject.Synchronize 26 | 27 | ### Use case: Sharing a subject across multiple threads 28 | 29 | - Avoid overlapping OnNext calls. 30 | - By default, subjects do not perform any synchronization across threads. 31 | - Hand out the sycnhronized subject to the producer threads. 32 | 33 | ```csharp 34 | var synchronizedSubject = Subject.Synchronize(subject); 35 | synchronizedSubject.OnNext(value); 36 | subscription = synchronizedSubject 37 | .ObserveOn(TaskPoolScheduler.Default) 38 | .Subscribe(...); 39 | ``` 40 | 41 | ## Async subscriptions 42 | 43 | Subscribers are not supposed to be long running, and therefore don't support execution of long running async methods in the Subscribe handlers. Instead, consider your async method to be a single value observable sequence that takes a value from another sequence. Now you can compose sequences, which is what Rx was designed to do. Otherwise, 1. you break the error model 2. you are mixing async models (rx here, task there). 44 | 45 | Source: https://stackoverflow.com/questions/37129159/subscribing-to-observable-sequence-with-async-function 46 | 47 | ## SerialDisposable 48 | 49 | "We only want one sidebar open at a time" 50 | "Autocomplete should only have one outstanding request in flight" 51 | "Animate this value from here to there and make sure we cancel an already-running animation if we try to start it again" 52 | "We only want to show one dialog on-screen at a time" 53 | "Connect to this websocket but if someone issues another connect() request close the first one" 54 | 55 | Source: https://twitter.com/anaisbetts/status/1034168666739200000 56 | 57 | ## Observable temperature 58 | 59 | Refers to the state of the observable at the moment of its subscription. This state describes the time an observable begins and stops its emissions and whether the emissions are shared between observers. 60 | 61 | - Convert cold to hot: Publish 62 | - COnvert hot to cold: Defer 63 | 64 | ## Hot observable 65 | 66 | An observable that emits notifications regardless of its observers (even if there are none). The notifications emitted by hot observables are shared among their observers. 67 | 68 | A hot observable is in an active state, like a singer performing live. 69 | 70 | ## Cold observable 71 | 72 | An observable that starts emitting notifications only when an observer subscribes, and each observer receives the full sequence of notifications without sharing them with other observers. 73 | 74 | A cold observable is in a passive state, like an album waiting to be played. 75 | 76 | ## Subject 77 | 78 | A type that implements the IObservable interface and IObserver interface is called a subject. This type acts as both an observer and an observable 79 | 80 | Subject: Broadcasts every observed notification to all observers. 81 | AsyncSubject: Represents an asynchronous operation that emits its value upon completion. 82 | ReplaySubject: Broadcasts notifications for current and future observers. 83 | BehaviorSubject: Broadcasts notifications and saves the latest value for future observers. When created, it’s initialized with a value that emits until changed. 84 | 85 | ### AsyncSubject 86 | 87 | One problem with Subject you may encounter is that if the source observable emits a value before an observer subscribes, this value will be lost. This is specifically problematic if the source always emits only a single notification. Luckily, AsyncSubject provides a remedy for those cases. 88 | 89 | Internally, AsyncSubject stores the most recent value so that when the source observable completes, it emits this value to current and future observers. -------------------------------------------------------------------------------- /RxNet.md: -------------------------------------------------------------------------------- 1 | # Rx.Net CheatSheet 2 | A collection of links and snippets to resources, samples, answers, people, videos, etc. related to Rx.Net. 3 | 4 | _Most of the blogs and videos are pretty dated but the core concepts still apply. The overall public API really hasn't changed much over the years, except for new operators and performance improvements._ 5 | 6 | ## Table of Contents 7 | 8 | #### [Timeline](#timeline-1) 9 | #### [Personal Favorites](#personal-favorites-1) 10 | #### [Learning Resources](#learning-resources-1) 11 | #### [Videos](#videos-1) 12 | #### [Tips](#tips-1) 13 | #### [Convenient References](#convenient-references-1) 14 | #### [Notable People to Follow](#notable-people-to-follow-1) 15 | 16 | ## Timeline 17 | 18 | Core Rx team at Microsoft: Erik Meijer, Brian Beckman, Wes Dyer, Bart De Smet, Jeffrey Van Gogh, and Matthew Podwysocki. 19 | 20 | [First preview release](https://docs.microsoft.com/en-us/archive/blogs/rxteam/announcing-reactive-extensions-rx-for-net-silverlight): 11/17/2009 21 | 22 | [First stable release](https://docs.microsoft.com/en-us/archive/blogs/rxteam/reactive-extensions-v1-0-stable-and-v1-1-experimental-available-now): 06/29/2011 23 | 24 | [Open Sourced](https://docs.microsoft.com/en-us/archive/blogs/interoperability/ms-open-tech-open-sources-rx-reactive-extensions-a-cure-for-asynchronous-data-streams-in-cloud-programming): 11/06/2012 25 | 26 | *Google searching tip:* Because there are so many languages that have adopted Rx, there's a need to filter the results to dotnet. To achieve this, I always structure my query as "C# observable [my specific query]". 27 | 28 | ## Learning Resources 29 | 30 | [Github Repo](https://github.com/dotnet/reactive) 31 | 32 | [Rx.Net Slack](https://reactivex.slack.com/messages/rxnet/) 33 | 34 | [IntroToRx](http://www.introtorx.com) - If I had to name a single resource to help someone get started with Rx.Net, it would be this invaluable online book by Lee Campbell. Even after years of reactive programming, I find myself going back time and time again as a reference/reminder of some of the smaller details. 35 | 36 | [Book: Rx.Net in Action](https://livebook.manning.com/book/rx-dot-net-in-action/about-this-book/) 37 | 38 | [Book: Programming Reactive Extensions and LINQ](https://www.apress.com/gp/book/9781430237471) 39 | 40 | [RxCookbook](https://github.com/LeeCampbell/RxCookbook) - Useful for learning how to utilize the power of Rx in common C# application scenarios, such as INotifyPropertyChanged and logging. 41 | 42 | [Bart De Smet Blog](http://blogs.bartdesmet.net/blogs/bart/archive/tags/Rx/default.aspx) 43 | 44 | [RxViz](https://rxviz.com/) - interactive JavaScript with visualizations (I just wish a DotNet version of this existed) 45 | 46 | [RxMarbles](http://rxmarbles.com/) - interactive visualizations 47 | 48 | [ReactiveX.io](http://reactivex.io/) - Rx hub for all languages 49 | 50 | [101 Rx Samples](http://rxwiki.wikidot.com/101samples) 51 | 52 | ## Videos 53 | 54 | [Channel9 Videos, filtered by Rx](https://channel9.msdn.com/tags/Rx/) 55 | 56 | [Channel9 Videos, filtered by Bart De Smit (one of the creators)](https://channel9.msdn.com/Tags/bart+de+smet) 57 | 58 | [Reactive Extensions for .NET Developers with Michael Stonis (November 2018)](https://channel9.msdn.com/Shows/On-NET/Reactive-Extensions-for-NET-Developers?WT.mc_id=ondotnet-channel9-cephilli) 59 | 60 | [Why You Should Be Building Better Mobile Apps with Reactive Programming – Michael Stonis](https://www.youtube.com/watch?v=DYEbUF4xs1Q) 61 | 62 | [UniRx playlist, but also includes intro to Rx theory and operators](https://www.youtube.com/playlist?list=PLKERDLXpXl_gdZ7NAHkAxKf12g3oNjTAc) 63 | 64 | ## Tips 65 | 66 | ### Best practices 67 | * Members that return a sequence should never return null. This applies to IEnumerable and IObservable sequences. Return an empty sequence instead. 68 | * Dispose of subscriptions. 69 | * Subscriptions should match their scope. 70 | * Always provide an OnError handler. 71 | * Avoid breaking the monad with blocking operators such as First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault and ForEach. 72 | * Avoid switching between monads, i.e. going from IObservable to IEnumerable and back to IObservable. 73 | * Favor lazy evaluation over eager evaluation. 74 | * Break large queries up into parts. Key indicators of a large query: 75 | * nesting 76 | * over 10 lines of query comprehension syntax 77 | * using the into statement 78 | * Name your queries well, i.e. avoid using the names like query, q, xs, ys, subject etc. 79 | * Avoid creating side effects. If you must create side effects, be explicit by using the Do operator. 80 | * **Minimize*** the use of the subject types. Rx is effectively a functional programming paradigm. Using subjects means we are now managing state, which is potentially mutating. Dealing with both mutating state and asynchronous programming at the same time is very hard to get right. Furthermore, many of the operators (extension methods) have been carefully written to ensure that correct and consistent lifetime of subscriptions and sequences is maintained; when you introduce subjects, you can break this. Future releases may also see significant performance degradation if you explicitly use subjects. 81 | * Avoid creating your own implementations of the IObservable interface. Favor using the Observable.Create factory method overloads instead. 82 | * Avoid creating your own implementations of the IObserver interface. Favor using the Subscribe extension method overloads instead. 83 | * The subscriber should define the concurrency model. The SubscribeOn and ObserveOn operators should only ever precede a Subscribe method. 84 | 85 | [Source](http://introtorx.com/Content/v1.0.10621.0/18_UsageGuidelines.html#UsageGuidelines) 86 | 87 | *If you check the source link above, you'll see I modified this point to say *Minimize* rather than *Avoid*. I did this because the opinions of some of the most respected Rx users on the web slightly differ when it comes to the degree of how much Subjects should be avoided. Here are some snippets of those opinions: 88 | 89 | > [Lee Campbell](https://stackoverflow.com/a/14460634/5984310): The reason I really don't like Subjects, is that is usually a case of the developer not really having a clear design on the problem. Hack in a subject, poke it here there and everywhere, and then let the poor support dev guess at WTF was going on. When you use the Create/Generate etc methods you are localizing the effects on the sequence. You can see it all in one method and you know no-one else is throwing in a nasty side effect. If I see a subject fields I now have to go looking for all the places in a class it is being used. If some MFer exposes one publicly, then all bets are off, who knows how this sequence is being used! Async/Concurrency/Rx is hard. You don't need to make it harder by allowing side effects and causality programming to spin your head even more. 90 | 91 | > [Sergey Aldoukhov](https://stackoverflow.com/q/9299813/5984310): People who frequent the RX forum know that E.Meijer does not like Subjects. While I have a deepest respect to RX creator's opinion, I have been using Subjects quite extensively in multiple projects for a couple of years and haven't had any architectural problem or a bug because of them.
92 | [Ani Betts](https://stackoverflow.com/a/9301923/5984310): Erik Meijer is thinking in a purely functional way - Subjects are the mutable variables of Rx. So, in general usage he's right - using Subjects is sometimes a way to cop out of Thinking Functionally, and if you use them too much, you're trying to row upstream. However! Subject are extremely useful when you're interfacing with the non-Functional world of .NET. Wrapping an event or callback method? Subjects are great for that. Trying to put an Rx "interface" onto some existing code? Use a Subject! 93 | 94 | > [Bart De Smet](https://stackoverflow.com/a/12049997/5984310): Whenever you need to create an observable out of thin air, Observable.Create should be the first thing to think of. in a lot of cases, there's already a built-in primitive in Rx that does exactly what you need. For example, there's From* factory methods to bridge with existing concepts (such as events, tasks, asynchronous methods, enumerable sequence), some of which using a subject under the covers. For multicasting logic, there's the Publish, Replay, etc. family of operators. 95 | 96 | > [James World](https://stackoverflow.com/a/21824601/5984310): It's not so much that the use of Subject is bad - there has to be some way of "entering the monad" - that's the academic way of saying "get an IObservable". You need to start somewhere. The problem with Subject arises more when it's used from a subscription instead of chaining existing observables together. Subjects should just exist at the edges of your Rx machinery. If none of the provided entry points (e.g. FromEvent, FromEventPattern, FromAsync, Return, ToObservable and so on) work for you then using Subject is perfectly valid. And there's no need to add extra complexity just to facilitate using one of the above - most of them use subjects or subject-like constructs under the covers anyway. 97 | 98 | > [Enigmativity](https://stackoverflow.com/a/45979673/5984310): It's usually a good idea to avoid subjects. 99 | 100 | > [Shlomo](https://stackoverflow.com/a/45966519/5984310): Subjects aren't universally bad. 101 | 102 | ## Convenient References 103 | 104 | ### Scheduler Defaults 105 | 106 | [SchedulerDefaults](https://github.com/dotnet/reactive/blob/7ad606b3dcd4bb2c6ae9622f8a59db7f8f52aa85/Rx.NET/Source/src/System.Reactive/Concurrency/SchedulerDefaults.cs) 107 | 108 | AsyncConversions: [Start, ToAsync, FromAsyncPattern](https://github.com/dotnet/reactive/blob/7ad606b3dcd4bb2c6ae9622f8a59db7f8f52aa85/Rx.NET/Source/src/System.Reactive/Linq/QueryLanguage.Async.cs) 109 | 110 | ConstantTimeOperations: [Return, Throw](https://github.com/dotnet/reactive/blob/7ad606b3dcd4bb2c6ae9622f8a59db7f8f52aa85/Rx.NET/Source/src/System.Reactive/Linq/QueryLanguage.Creation.cs), [Append, Prepend, StartWith](https://github.com/dotnet/reactive/blob/7ad606b3dcd4bb2c6ae9622f8a59db7f8f52aa85/Rx.NET/Source/src/System.Reactive/Linq/QueryLanguage.Single.cs) 111 | 112 | SynchronizationContextScheduler/ConstantTimeOperations: [FromEvent, FromEventPattern](https://github.com/dotnet/reactive/blob/7ad606b3dcd4bb2c6ae9622f8a59db7f8f52aa85/Rx.NET/Source/src/System.Reactive/Linq/QueryLanguage.Events.cs) 113 | 114 | Iteration: [Generate, Range, Repeat](https://github.com/dotnet/reactive/blob/7ad606b3dcd4bb2c6ae9622f8a59db7f8f52aa85/Rx.NET/Source/src/System.Reactive/Linq/QueryLanguage.Creation.cs), [TakeLast](https://github.com/dotnet/reactive/blob/7ad606b3dcd4bb2c6ae9622f8a59db7f8f52aa85/Rx.NET/Source/src/System.Reactive/Linq/QueryLanguage.Time.cs#L331), [ToObservable](https://github.com/dotnet/reactive/blob/7ad606b3dcd4bb2c6ae9622f8a59db7f8f52aa85/Rx.NET/Source/src/System.Reactive/Linq/QueryLanguage.Conversions.cs#L81), [ReplaySubject](https://github.com/dotnet/reactive/blob/7ad606b3dcd4bb2c6ae9622f8a59db7f8f52aa85/Rx.NET/Source/src/System.Reactive/Subjects/ReplaySubject.cs) 115 | 116 | TailRecursion: At the time of writing, I don't see this scheduler being used in the source code. 117 | 118 | TimeBasedOperations: [Buffer, Delay, DelaySubscription, Generate, Interval, Sample, Skip, SkipLast, SkipUntil, Take, TakeLast, TakeLastBuffer, TakeUntil, Throttle, TimeInterval, Timeout, Timer, Timestamp, Window](https://github.com/dotnet/reactive/blob/7ad606b3dcd4bb2c6ae9622f8a59db7f8f52aa85/Rx.NET/Source/src/System.Reactive/Linq/QueryLanguage.Time.cs) 119 | 120 | [Useful advice for choosing schedulers](https://stackoverflow.com/a/20636059/5984310) 121 | 122 | ## Notable People to Follow 123 | 124 | _These are basically the names you'll see over and over again on Stack Overflow, answering Rx.Net questions. I've learned a ton from these guys._ 125 | 126 | James World [@jamesw0rld](https://twitter.com/jamesw0rld) 127 | * [Stack Overflow - System.Reactive](https://stackoverflow.com/search?q=user:87427+[system.reactive]) 128 | * [Github](https://github.com/james-world) 129 | * [Blog](http://www.zerobugbuild.com/) 130 | 131 | Lee Campbell [@LeeRyanCampbell](https://twitter.com/leeryancampbell) 132 | * [Stack Overflow - System.Reactive](https://stackoverflow.com/search?q=user:393615+[system.reactive]) 133 | 134 | Shlomo 135 | * [Stack Overflow - System.Reactive](https://stackoverflow.com/search?q=user:415661+[system.reactive]) 136 | 137 | Enigmativity 138 | * [Stack Overflow - System.Reactive](https://stackoverflow.com/search?q=user:259769+[system.reactive]) 139 | 140 | Ani Betts [@anaisbetts](https://twitter.com/anaisbetts) 141 | * [Github](https://github.com/anaisbetts) 142 | * [Book: Programming Reactive Extensions and LINQ](http://disq.us/url?url=http%3A%2F%2Fjliberty.me%2F2lYAZb8%3AI-DqcbqqiSnKie2L8J8U2hBCoS8&cuid=3716375) 143 | * [Stack Overflow - System.Reactive](https://stackoverflow.com/search?q=user:5728+[system.reactive]) 144 | * [Stack Overflow - ReactiveUI](https://stackoverflow.com/search?q=user:5728+[reactiveui]) 145 | 146 | Brandon 147 | * [Stack Overflow - System.Reactive](https://stackoverflow.com/search?q=user:674326+[system.reactive]) 148 | 149 | Dave Sexton [@IDaveSexton](https://twitter.com/idavesexton) 150 | * [Stack Overflow - System.Reactive](https://stackoverflow.com/search?q=user:3970148+[system.reactive]) 151 | 152 | [Stack Overflow Top Users for System.Reactive](https://stackoverflow.com/tags/system.reactive/topusers) 153 | -------------------------------------------------------------------------------- /RxUI.md: -------------------------------------------------------------------------------- 1 | # ReactiveUI CheatSheet 2 | A collection of links and snippets to resources, samples, answers, people, videos, etc. related to ReactiveUI. 3 | 4 | ## Table of Contents 5 | 6 | #### [Sample Projects](#sample-projects-1) 7 | #### [Learning Resources](#learning-resources-1) 8 | #### [Tips & Best Practices](#tips--best-practices-1) 9 | #### [Notable People to Follow](#notable-people-to-follow-1) 10 | #### [ReactiveUI Glossary](#reactiveui-glossary-1) 11 | 12 | ## Sample Projects 13 | 14 | _Most of these are pretty dated but still have a lot of relevance._ 15 | 16 | A lot of these are taken from [this thread](https://github.com/reactiveui/ReactiveUI/issues/687), but I find it easier to browse when it's organized by platform, like this. 17 | 18 | ### Xamarin.Forms 19 | 20 | [WorkoutWotch - Xamarin Forms Video Series](https://github.com/kentcb/WorkoutWotch) - The accompanying video series, referenced in the README, covers Xamarin iOS Native, but the codebase was later converted to Xamarin Forms to support other platforms. 21 | 22 | [Reactive Examples by TheEightBot](https://github.com/TheEightBot/Reactive-Examples) 23 | 24 | [UnofficialGitterApp](https://github.com/flagbug/UnofficialGitterApp) 25 | 26 | [XamarinEvolve2014 - Heavily commented demo app by Ani Betts](https://github.com/anaisbetts/XamarinEvolve2014) 27 | 28 | ### Android 29 | 30 | [Burger ingredient streams demo with blog post](https://github.com/JonDouglas/BeingReactive) 31 | 32 | [Espera.Mobile](https://github.com/flagbug/Espera.Mobile) 33 | 34 | ### iOS 35 | 36 | [WorkoutWotch - Xamarin Forms Video Series](https://github.com/kentcb/WorkoutWotch) - The accompanying video series, referenced in the README, covers Xamarin iOS Native, but the codebase was later converted to Xamarin Forms to support other platforms. 37 | 38 | [CodeHub - uses MVVMCross](https://github.com/CodeHubApp/CodeHub) 39 | 40 | [ReactiveTableView & ReactiveCollectionView Demo](https://github.com/cabauman/ReactiveTableViewSource-Sample) 41 | 42 | ### WPF 43 | 44 | [Sample code for the book "You, I, and ReactiveUI"](https://github.com/kentcb/YouIandReactiveUI) 45 | 46 | [FirstsStepsRUI with blog post](https://github.com/kondaskondas/FirstsStepsRUI) 47 | 48 | ### UWP 49 | 50 | [RxUI-UWP-Sample](https://github.com/moswald/RxUI-UWP-Sample) 51 | 52 | ## Learning Resources 53 | 54 | [Github Repo](https://github.com/reactiveui/ReactiveUI) 55 | 56 | [ReactiveUI Slack](https://reactivex.slack.com/messages/reactiveui/) 57 | 58 | [ReactiveUI Docs](https://reactiveui.net) 59 | 60 | [Book: You, I, and ReactiveUI](https://kent-boogaart.com/you-i-and-reactiveui/) - Love this book. Highly recommended. 61 | 62 | [Custom Routing in ReactiveUI](https://kent-boogaart.com/blog/custom-routing-in-reactiveui) 63 | 64 | ## Videos 65 | 66 | [Reactive Extensions for .NET Developers with Michael Stonis (November 2018)](https://channel9.msdn.com/Shows/On-NET/Reactive-Extensions-for-NET-Developers?WT.mc_id=ondotnet-channel9-cephilli) 67 | 68 | [Why You Should Be Building Better Mobile Apps with Reactive Programming – Michael Stonis](https://www.youtube.com/watch?v=DYEbUF4xs1Q) 69 | 70 | ## Tips & Best Practices 71 | 72 | ### Inject Service Locator interface via constructor 73 | 74 | ``` 75 | public SuspensionHost(ISuspensionDriver driver = null) 76 | { 77 | driver = driver ?? Locator.Current.GetService(); 78 | } 79 | ``` 80 | 81 | **Explanation:** This uses a Service Located interface for the default interface, but only if the caller didn't give an explicit one in the constructor. Far more straightforward to test in a unit test runner than trying to construct a sham IoC container, but still falls back to a default implementation at runtime. 82 | 83 | **Source:** [https://stackoverflow.com/a/26924067/5984310](https://stackoverflow.com/a/26924067/5984310) 84 | 85 | ### Call async operations in the View constructor, rather than the ViewModel constructor. 86 | 87 | ``` 88 | this.WhenAnyValue(x => x.ViewModel.LoadItems) 89 | .SelectMany(x => x.ExecuteAsync()) 90 | .Subscribe(); 91 | ``` 92 | 93 | **Explanation:** Invoking async operations in the ViewModel constructor means that your ViewModel class becomes more difficult to test, because you always have to mock out the effects of calling LoadItems, even if the thing you are testing is unrelated. 94 | 95 | **Source:** [https://codereview.stackexchange.com/a/74793](https://codereview.stackexchange.com/a/74793) 96 | 97 | ### When should I bother disposing of IDisposable objects? 98 | 99 | 1) No need 100 | 101 | ``` 102 | public MyViewModel() 103 | { 104 | MyReactiveCommand 105 | .Execute() 106 | .Subscribe(...); 107 | } 108 | ``` 109 | 110 | Quote by Kent Boogart (one of the ReactiveUI maintainers): 111 | 112 | > When the execution of a ReactiveCommand completes, all observers are auto-unsubscribed anyway. Generally, subscriptions to pipelines that have a finite lifetime (eg. via a timeout) need not be disposed manually. Disposing of such a subscription is about as useful as disposing of a MemoryStream. 113 | 114 | 2) Do dispose 115 | 116 | ``` 117 | public MyView() 118 | { 119 | this.WhenAnyValue(x => x.ViewModel) 120 | .Do(PopulateFromViewModel) 121 | .Subscribe(); 122 | } 123 | ``` 124 | 125 | This one is tricky. Disposing of this subscription is a must _if_ developing for a dependency property-based platform such as WPF or UWP. Quoting Ani Betts, this is because "[there's no non-leaky way to observe a dependency property](https://stackoverflow.com/a/22341350/5984310)," which is exactly what the ViewModel property of a ReactiveUserControl is. However, if you happen to know that your ViewModel won't change for the liftime of the view then you can make ViewModel a normal property, eliminating the need to dispose. For other platforms such as Xamarin.Forms, Xamarin.Android, and Xamarin.iOS there's no need to dispose because you're simply monitoring the property (ViewModel) on the view itself, so the subscription is attaching to PropertyChanged on that view. This means the view has a reference to itself and thus, doesn't prevent the it from being garbage collected. 126 | 127 | 3) Do dispose 128 | 129 | ``` 130 | public MyViewModel() 131 | { 132 | SomeService.SomePipeline 133 | .Subscribe(...); 134 | } 135 | ``` 136 | 137 | Services commonly have a longer lifetime than view models, especially in the case of singletons and global application variables. Therefore, it's vital that these kinds of subscriptions are disposed of. 138 | 139 | 4) No need 140 | 141 | ``` 142 | public MyViewModel() 143 | { 144 | SomeService.SomePipelineModelingAsynchrony 145 | .Subscribe(...); 146 | } 147 | ``` 148 | 149 | Pipelines modeling asynchrony can be relied upon to complete, and thus the subscription will be disposed of automatically via OnComplete (or OnError). 150 | 151 | 5) Do dispose 152 | 153 | ``` 154 | public MyView() 155 | { 156 | this.WhenAnyValue(x => x.ViewModel.SomeProperty) 157 | .Do(AssignValueToViewControl) 158 | .Subscribe(); 159 | } 160 | ``` 161 | 162 | Now you're saying "attach to PropertyChanged on _this_ and tell me when the ViewModel property changes, then attach to PropertyChanged on _that_ (the view model) and tell me when SomeProperty changes." This implies the view model has a reference back to the view, which needs to be cleaned up or else the view model will keep the view alive. 163 | 164 | 6) Performance tip 165 | 166 | ``` 167 | public MyView() 168 | { 169 | // For a dependency property-based platform such as WPF and UWP 170 | this.WhenActivated( 171 | disposables => 172 | { 173 | this.WhenAnyValue(x => x.ViewModel) 174 | .Where(x => x != null) 175 | .Do(PopulateFromViewModel) 176 | .Subscribe() 177 | .DisposeWith(disposables); 178 | }); 179 | 180 | // For other platforms it can be simplified to the following 181 | this.WhenAnyValue(x => x.ViewModel) 182 | .Where(x => x != null) 183 | .Do(PopulateFromViewModel) 184 | .Subscribe() 185 | } 186 | 187 | private void PopulateFromViewModel(MyViewModel vm) 188 | { 189 | // Assign values from vm to controls 190 | } 191 | ``` 192 | 193 | More efficient than binding to properties. If your ViewModel properties don't change over time, definitely use this pattern. The _WhenActivated_ part is important for dependency property-based platforms (as mentioned in case 2) since it will handle disposing of the subscription every time the view is deactivated. 194 | 195 | 7) No need 196 | 197 | ``` 198 | // Should I dispose of the IDisposable that WhenActivated returns? 199 | this.WhenActivated( 200 | disposables => 201 | { 202 | ... 203 | }) 204 | ``` 205 | 206 | Quote by Kent Boogart: 207 | 208 | > If you're using WhenActivated in a view, when do you dispose of the disposable that it returns? You'd have to store it in a local field and make the view disposable. But then who disposes of the view? You'd need platform hooks to know when an appropriate time to dispose it is - not a trivial matter if that view is reused in virtualization scenarios. In addition to this, I have found that reactive code in VMs in particular tends to juggle a lot of disposables. Storing all those disposables away and attempting disposal tends to clutter the code and force the VM itself to be disposable, further confusing matters. Perf is another factor to consider, particularly on Android. 209 | 210 | ## Notable People to Follow 211 | 212 | Ani Betts [@anaisbetts](https://twitter.com/anaisbetts) 213 | * [Github](https://github.com/anaisbetts) 214 | * [Book: Programming Reactive Extensions and LINQ](http://disq.us/url?url=http%3A%2F%2Fjliberty.me%2F2lYAZb8%3AI-DqcbqqiSnKie2L8J8U2hBCoS8&cuid=3716375) 215 | * [Stack Overflow - System.Reactive](https://stackoverflow.com/search?q=user:5728+[system.reactive]) 216 | * [Stack Overflow - ReactiveUI](https://stackoverflow.com/search?q=user:5728+[reactiveui]) 217 | 218 | Kent Boogaart [@kent_boogaart](https://twitter.com/kent_boogaart) 219 | * [Blog](https://kent-boogaart.com/blog) 220 | * [Book: You, I, and ReactiveUI](https://kent-boogaart.com/you-i-and-reactiveui/) 221 | * [Github](https://github.com/kentcb) 222 | * [Stack Overflow - System.Reactive](https://stackoverflow.com/search?q=user:5380+[system.reactive]) 223 | 224 | [ReactiveUI Twitter](https://twitter.com/reactivexui) 225 | 226 | [Stack Overflow Top Users for ReactiveUI](https://stackoverflow.com/tags/reactiveui/topusers) 227 | 228 | ## ReactiveUI Glossary 229 | 230 | _WhenActivated_ 231 | 232 | **WhenActivated:** allows you to specify the things that should occur when a view or view model is activated and deactivated; requries that our view implements IActivatable; Typically, you don't need to worry about disposing of the disposable returned by WhenActivated. Views tend to deactivate naturally as a consequence of users navigating through your application and ReactiveUI's default IActivationForViewFetcher implementations. 233 | 234 | **IActivatable:** think of it as IActivatableView; implemented by IViewFor; tag interface (no methods to implement) 235 | 236 | **ISupportsActivation:** think of it as IActivatableViewModel; requires that the IViewFor invokes WhenActivated; can test view model activation and deactivation by calling Activate and Deactivate; implementing this interface more than once in a view model class hierarchy will result in view model activation failing to work correctly 237 | 238 | **ViewModelActivator:** essentially a sink in which WhenActivated will register the blocks of activation logic provided by your view model 239 | 240 | **IActivationForViewFetcher:** implements GetAffinityForView and GetActivationForView 241 | 242 | **GetAffinityForView:** method of IActivationForViewFetcher; tells ReactiveUI how confident you are that your implementation of IActivationForViewFetcher can provide activation information for a given view; higher numbers returned by this method trump lower numbers returned by other implementations 243 | 244 | **GetActivationForView:** method of IActivationForViewFetcher; returns IObservable that ticks true when the view is activated and false when the view is deactivated 245 | 246 | _NAVIGATION_ 247 | 248 | **RoutingState:** NavigationStack, Navigate (ReactiveCommand), NavigateBack (ReactiveCommand), NavigateAndReset (ReactiveCommand) 249 | 250 | **IScreen:** Router (RoutingState); root of a navigation stack; despite the name, its views don't _have to_ occupy the whole screen 251 | 252 | **IRoutableViewModel:** UrlPathSegment (string), HostScreen (IScreen) 253 | 254 | **RoutedViewHost:** platform-specific; monitors an instance of RoutingState, responding to any changes in the navigation stack by creating and embedding the appropriate view 255 | -------------------------------------------------------------------------------- /Samples/Xamarin.Android/ReactiveRecyclerView.md: -------------------------------------------------------------------------------- 1 | We need 5 classes: 2 | 1. A view model derived from `ReactiveObject` for the Activity 3 | 2. A view model derived from `ReactiveObject` for a list item 4 | 2. An Activity derived from `ReactiveAppCompatActivity` 5 | 3. An adapter derived from `ReactiveRecyclerViewAdapter>` 6 | 4. A view holder derived from `ReactiveRecyclerViewViewHolder` 7 | 8 | And we need 2 XML layout files: 9 | 1. An activity that contains a RecyclerView 10 | 2. A view holder that contains list item controls 11 | 12 | ```csharp 13 | public class MainViewModel : ReactiveObject 14 | { 15 | private IObservable _itemClicked; 16 | private IObservable _itemLongClicked; 17 | 18 | public MainViewModel() 19 | { 20 | MyList = new ObservableCollection(); 21 | MyList.AddRange( 22 | new MyListItemViewModel[] 23 | { 24 | new MyListItemViewModel() { MyText = "1" }, 25 | new MyListItemViewModel() { MyText = "2" }, 26 | new MyListItemViewModel() { MyText = "3" }, 27 | new MyListItemViewModel() { MyText = "4" }, 28 | new MyListItemViewModel() { MyText = "5" }, 29 | }); 30 | 31 | var clickedNoti = this 32 | .WhenAnyObservable(vm => vm.ItemLongClicked) 33 | .Select(index => $"Clicked item # {index}."); 34 | 35 | var longClickedNoti = this 36 | .WhenAnyObservable(vm => vm.ItemLongClicked) 37 | .Select(index => $"Long-clicked item # {index}."); 38 | 39 | DisplayNotification = Observable 40 | .Merge(clickedNoti, longClickedNoti); 41 | } 42 | 43 | public ObservableCollection MyList { get; } 44 | 45 | public IObservable DisplayNotification { get; } 46 | 47 | public IObservable ItemClicked 48 | { 49 | get => _itemClicked; 50 | set => this.RaiseAndSetIfChanged(ref _itemClicked, value); 51 | } 52 | 53 | public IObservable ItemLongClicked 54 | { 55 | get => _itemClicked; 56 | set => this.RaiseAndSetIfChanged(ref _itemLongClicked, value); 57 | } 58 | } 59 | ``` 60 | 61 | .... 62 | 63 | ```csharp 64 | // MyListItemViewModel.cs 65 | public class MyListItemViewModel : ReactiveObject 66 | { 67 | private string _myText; 68 | 69 | public string MyText 70 | { 71 | get { return _myText; } 72 | set { this.RaiseAndSetIfChanged(ref _myText, value); } 73 | } 74 | } 75 | ``` 76 | 77 | ... 78 | 79 | ```csharp 80 | [Activity(Label = "@string/app_name", Theme = "@style/AppTheme.NoActionBar", MainLauncher = true)] 81 | public class MainActivity : ReactiveAppCompatActivity 82 | { 83 | private RecyclerView _recyclerView; 84 | private RecyclerView.LayoutManager _layoutManager; 85 | private MyRecyclerViewAdapter _adapter; 86 | 87 | protected override void OnCreate(Bundle savedInstanceState) 88 | { 89 | base.OnCreate(savedInstanceState); 90 | 91 | ViewModel = new MainViewModel(); 92 | 93 | SetContentView(Resource.Layout.activity_main); 94 | 95 | Android.Support.V7.Widget.Toolbar toolbar = FindViewById(Resource.Id.toolbar); 96 | SetSupportActionBar(toolbar); 97 | 98 | _recyclerView = FindViewById(Resource.Id.RecyclerView); 99 | _layoutManager = new LinearLayoutManager(this); 100 | _recyclerView.SetLayoutManager(_layoutManager); 101 | _adapter = new MyRecyclerViewAdapter(ViewModel.MyList); 102 | _recyclerView.SetAdapter(_adapter); 103 | 104 | ViewModel.ItemClicked = _adapter.ItemClicked; 105 | ViewModel.ItemLongClicked = _adapter.ItemLongClicked; 106 | 107 | this.WhenActivated( 108 | disposables => 109 | { 110 | ViewModel 111 | .DisplayNotification 112 | .Subscribe( 113 | text => 114 | { 115 | Toast.MakeText(this, text, ToastLength.Short).Show(); 116 | }) 117 | .DisposeWith(disposables); 118 | }); 119 | } 120 | } 121 | ``` 122 | 123 | ... 124 | 125 | ```xml 126 | 131 | 132 | 136 | 137 | 138 | ``` 139 | 140 | ... 141 | 142 | ```csharp 143 | public class MyRecyclerViewAdapter : ReactiveRecyclerViewAdapter> 144 | { 145 | public MyRecyclerViewAdapter(ObservableCollection backingList) 146 | : base(backingList) 147 | { 148 | } 149 | 150 | public override ReactiveRecyclerViewViewHolder OnCreateReactiveViewHolder(ViewGroup parent, int viewType) 151 | { 152 | View itemView = LayoutInflater 153 | .From(parent.Context) 154 | .Inflate(Resource.Layout.item_main, parent, false); 155 | 156 | var viewHolder = new MyRecyclerViewHolder(itemView); 157 | 158 | return viewHolder; 159 | } 160 | } 161 | ``` 162 | 163 | ... 164 | 165 | ```csharp 166 | public class MyRecyclerViewHolder : ReactiveRecyclerViewViewHolder 167 | { 168 | public MyRecyclerViewHolder(View view) 169 | : base(view) 170 | { 171 | this.WireUpControls(); 172 | 173 | this.OneWayBind(this.ViewModel, x => x.MyText, x => x.MyTextView.Text); 174 | } 175 | 176 | public TextView MyTextView { get; private set; } 177 | } 178 | ``` 179 | 180 | ... 181 | 182 | ```xml 183 | 184 | 190 | 191 | 199 | 200 | 201 | ``` 202 | --------------------------------------------------------------------------------