├── 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 |
--------------------------------------------------------------------------------