This is Understanding Combine, written by Matt Neuburg. It is a work in progress. Corrections and suggestions are greatly appreciated (you can comment here). So are donations; please consider keeping me going by funding this work at Or buy my books: the current editions are iOS 13 Programming Fundamentals with Swift and Programming iOS 13. Thank you!


.removeDuplicates (Publishers.RemoveDuplicates) can be thought of as a specialized form of .filter; it is also related to .scan. Somewhat like .scan, it keeps track of a previous value. Like .filter, it decides whether to stop the current value that arrives from upstream, but it can do this based on something about the previous value.

It works like this. The first value from upstream is always passed through .removeDuplicates without question. That value is also memorized. Each time a new value arrives from upstream, .removeDuplicates compares it to the most recent memorized previous value, and decides whether to allow this new value to pass downstream. If the answer is No, the value from upstream is thrown away. If the answer is Yes, the value from upstream is permitted to pass downstream, and its value is memorized.

Thus, .removeDuplicates is always comparing each value that arrives from upstream with the value that it most recently permitted to pass downstream. The idea is to prevent the downstream from receiving multiple duplicate values in a row.

By default, .removeDuplicates uses a simple equality test: if the current value is the same as the previous passed value, it is blocked. However, you can append a filter function that receives two values — the previous passed value, followed by the current value — and returns a Bool saying whether the current value should be blocked. Note that this is the inverse of the Bool you return in the .filter filter function: with .filter, true means “let it through,” but with .removeDuplicates, true means “block it.”

There is also a .tryRemoveDuplicates (Publishers.TryRemoveDuplicates); it adds to the filter function the ability to throw an error which is passed down the pipeline as a failure.

Table of Contents