{"id":15029341,"url":"https://github.com/combinecommunity/combineext","last_synced_at":"2025-05-14T23:06:46.957Z","repository":{"id":37952799,"uuid":"216883032","full_name":"CombineCommunity/CombineExt","owner":"CombineCommunity","description":"CombineExt provides a collection of operators, publishers and utilities for Combine, that are not provided by Apple themselves, but are common in other Reactive Frameworks and standards.","archived":false,"fork":false,"pushed_at":"2024-06-19T03:27:42.000Z","size":354,"stargazers_count":1768,"open_issues_count":33,"forks_count":161,"subscribers_count":31,"default_branch":"main","last_synced_at":"2025-05-14T23:06:28.285Z","etag":null,"topics":["combine-framework","reactive-programming","reactive-streams","swift"],"latest_commit_sha":null,"homepage":"https://combine.community","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CombineCommunity.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-10-22T18:29:31.000Z","updated_at":"2025-05-12T16:00:39.000Z","dependencies_parsed_at":"2024-06-18T13:35:08.435Z","dependency_job_id":"5f609f85-1f26-4f84-83f6-8e99ff3f89b8","html_url":"https://github.com/CombineCommunity/CombineExt","commit_stats":{"total_commits":105,"total_committers":33,"mean_commits":"3.1818181818181817","dds":0.6285714285714286,"last_synced_commit":"d7b896fa9ca8b47fa7bcde6b43ef9b70bf8c1f56"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CombineCommunity%2FCombineExt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CombineCommunity%2FCombineExt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CombineCommunity%2FCombineExt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CombineCommunity%2FCombineExt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CombineCommunity","download_url":"https://codeload.github.com/CombineCommunity/CombineExt/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254243362,"owners_count":22038046,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["combine-framework","reactive-programming","reactive-streams","swift"],"created_at":"2024-09-24T20:10:21.598Z","updated_at":"2025-05-14T23:06:41.944Z","avatar_url":"https://github.com/CombineCommunity.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CombineExt\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/CombineCommunity/CombineExt/raw/main/Resources/logo.png\" width=\"45%\"\u003e\n\u003cbr /\u003e\u003cbr /\u003e\n\u003ca href=\"https://actions-badge.atrox.dev/CombineCommunity/CombineExt/goto\" target=\"_blank\" alt=\"Build Status\" title=\"Build Status\"\u003e\u003cimg src=\"https://github.com/CombineCommunity/CombineExt/workflows/CombineExt/badge.svg?branch=main\" alt=\"Build Status\" title=\"Build Status\"\u003e\u003c/a\u003e\n\u003ca href=\"https://codecov.io/gh/CombineCommunity/CombineExt\" target=\"_blank\" alt=\"Code Coverage for CombineExt on codecov\" title=\"Code Coverage for CombineExt on codecov\"\u003e\u003cimg src=\"https://codecov.io/gh/CombineCommunity/CombineExt/branch/main/graph/badge.svg\" alt=\"Code Coverage for CombineExt on codecov\" title=\"Code Coverage for CombineExt on codecov\"/\u003e\u003c/a\u003e\n\u003cbr /\u003e\n\u003cimg src=\"https://img.shields.io/badge/platforms-iOS%2013%20%7C%20macOS 10.15%20%7C%20tvOS%2013%20%7C%20watchOS%206-333333.svg\" /\u003e\n\u003cbr /\u003e\n\u003ca href=\"https://cocoapods.org/pods/CombineExt\" target=\"_blank\"\u003e\u003cimg src=\"https://img.shields.io/cocoapods/v/CombineExt.svg?1\" alt=\"CombineExt supports CocoaPods\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/apple/swift-package-manager\" target=\"_blank\"\u003e\u003cimg src=\"https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg\" alt=\"CombineExt supports Swift Package Manager (SPM)\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/Carthage/Carthage\" target=\"_blank\"\u003e\u003cimg src=\"https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat\" alt=\"CombineExt supports Carthage\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nCombineExt provides a collection of operators, publishers and utilities for Combine, that are not provided by Apple themselves, but are common in other Reactive Frameworks and standards.\n\nThe original inspiration for many of these additions came from my journey investigating Combine after years of RxSwift and ReactiveX usage.\n\nAll operators, utilities and helpers respect Combine's publisher contract, including backpressure.\n\n### Operators\n* [withLatestFrom](#withLatestFrom)\n* [flatMapLatest](#flatMapLatest)\n* [assign](#assign)\n* [amb and Collection.amb](#amb)\n* [materialize](#materialize)\n* [values](#values)\n* [failures](#failures)\n* [dematerialize](#dematerialize)\n* [partition](#partition)\n* [zip(with:) and Collection.zip](#ZipMany)\n* [Collection.merge()](#MergeMany)\n* [combineLatest(with:) and Collection.combineLatest](#CombineLatestMany)\n* [mapMany(_:)](#MapMany)\n* [filterMany(_:)](#FilterMany)\n* [setOutputType(to:)](#setOutputType)\n* [removeAllDuplicates and removeAllDuplicates(by:) ](#removeAllDuplicates)\n* [share(replay:)](#sharereplay)\n* [prefix(duration:tolerance:​on:options:)](#prefixduration)\n* [prefix(while:behavior:​)](#prefixwhilebehavior)\n* [toggle()](#toggle)   \n* [nwise(_:) and pairwise()](#nwise)\n* [ignoreOutput(setOutputType:)](#ignoreOutputsetOutputType)\n* [ignoreFailure](#ignoreFailure)\n* [mapToResult](#mapToResult)\n* [flatMapBatches(of:)](#flatMapBatchesof)\n\n### Publishers\n* [AnyPublisher.create](#AnypublisherCreate)\n* [CurrentValueRelay](#CurrentValueRelay)\n* [PassthroughRelay](#PassthroughRelay)\n\n### Subjects\n* [ReplaySubject](#ReplaySubject)\n\n\u003e **Note**: This is still a relatively early version of CombineExt, with much more to be desired. I gladly accept PRs, ideas, opinions, or improvements. Thank you! :)\n\n## Installation\n\n### CocoaPods\n\nAdd the following line to your **Podfile**:\n\n```rb\npod 'CombineExt'\n```\n\n### Swift Package Manager\n\nAdd the following dependency to your **Package.swift** file:\n\n```swift\n.package(url: \"https://github.com/CombineCommunity/CombineExt.git\", from: \"1.0.0\")\n```\n\n### Carthage\n\nCarthage support is offered as a prebuilt binary.\n\nAdd the following to your **Cartfile**:\n\n```\ngithub \"CombineCommunity/CombineExt\"\n```\n\n## Operators\n\nThis section outlines some of the custom operators CombineExt provides.\n\n### withLatestFrom\n\nMerges up to four publishers into a single publisher by combining each value from `self` with the _latest_ value from the other publishers, if any.\n\n```swift\nlet taps = PassthroughSubject\u003cVoid, Never\u003e()\nlet values = CurrentValueSubject\u003cString, Never\u003e(\"Hello\")\n\ntaps\n  .withLatestFrom(values)\n  .sink(receiveValue: { print(\"withLatestFrom: \\($0)\") })\n\ntaps.send()\ntaps.send()\nvalues.send(\"World!\")\ntaps.send()\n```\n\n#### Output:\n\n```none\nwithLatestFrom: Hello\nwithLatestFrom: Hello\nwithLatestFrom: World!\n```\n\n------\n\n### flatMapLatest\n\nTransforms an output value into a new publisher, and flattens the stream of events from these multiple upstream publishers to appear as if they were coming from a single stream of events.\n\nMapping to a new publisher will cancel the subscription to the previous one, keeping only a single subscription active along with its event emissions.\n\n**Note**: `flatMapLatest` is a combination of `map` and `switchToLatest`.\n\n```swift\nlet trigger = PassthroughSubject\u003cVoid, Never\u003e()\ntrigger\n    .flatMapLatest { performNetworkRequest() }\n\ntrigger.send()\ntrigger.send() // cancels previous request\ntrigger.send() // cancels previous request\n```\n\n------\n\n### assign\n\nCombineExt provides custom overloads of `assign(to:on:)` that let you bind a publisher to multiple keypath targets simultaneously.\n\n```swift\nvar label1: UILabel\nvar label2: UILabel\nvar text: UITextField\n\n[\"hey\", \"there\", \"friend\"]\n    .publisher\n    .assign(to: \\.text, on: label1,\n            and: \\.text, on: label2,\n            and: \\.text, on: text)\n```\n\nCombineExt provides an additional overload — `assign(to:on​:ownership)` — which lets you specify the kind of ownersip you want for your assign operation: `strong`, `weak` or `unowned`.\n\n```swift\n// Retain `self` strongly\nsubscription = subject.assign(to: \\.value, on: self)\nsubscription = subject.assign(to: \\.value, on: self, ownership: .strong)\n\n// Use a `weak` reference to `self`\nsubscription = subject.assign(to: \\.value, on: self, ownership: .weak)\n\n// Use an `unowned` reference to `self`\nsubscription = subject.assign(to: \\.value, on: self, ownership: .unowned)\n```\n\n------\n\n### amb\n\nAmb takes multiple publishers and mirrors the first one to emit an event. You can think of it as a race of publishers, where the first one to emit passes its events, while the others are ignored (there’s also a `Collection.amb` method to ease working with multiple publishers).\n\nThe name `amb` comes from the [Reactive Extensions operator](http://reactivex.io/documentation/operators/amb.html), also known in RxJS as `race`.\n\n```swift\nlet subject1 = PassthroughSubject\u003cInt, Never\u003e()\nlet subject2 = PassthroughSubject\u003cInt, Never\u003e()\n\nsubject1\n  .amb(subject2)\n  .sink(receiveCompletion: { print(\"amb: completed with \\($0)\") },\n        receiveValue: { print(\"amb: \\($0)\") })\n\nsubject2.send(3) // Since this subject emit first, it becomes the active publisher\nsubject1.send(1)\nsubject2.send(6)\nsubject1.send(8)\nsubject1.send(7)\n\nsubject1.send(completion: .finished)\n// Only when subject2 finishes, amb itself finishes as well, since it's the active publisher\nsubject2.send(completion: .finished)\n```\n\n#### Output:\n\n```none\namb: 3\namb: 6\namb: completed with .finished\n```\n\n------\n\n### materialize\n\nConvert any publisher to a publisher of its events. Given a `Publisher\u003cOutput, MyError\u003e`, this operator will return a `Publisher\u003cEvent\u003cOutput, MyError\u003e, Never\u003e`, which means your failure will actually be a regular value, which makes error handling much simpler in many use cases.\n\n```swift\nlet values = PassthroughSubject\u003cString, MyError\u003e()\nenum MyError: Swift.Error {\n  case ohNo\n}\n\nvalues\n  .materialize()\n  .sink(receiveCompletion: { print(\"materialized: completed with \\($0)\") },\n        receiveValue: { print(\"materialized: \\($0)\") })\n\nvalues.send(\"Hello\")\nvalues.send(\"World\")\nvalues.send(\"What's up?\")\nvalues.send(completion: .failure(.ohNo))\n```\n\n#### Output:\n\n```none\nmaterialize: .value(\"Hello\")\nmaterialize: .value(\"World\")\nmaterialize: .value(\"What's up?\")\nmaterialize: .failure(.ohNo)\nmaterialize: completed with .finished\n```\n\n------\n\n### values\n\nGiven a materialized publisher, publish only the emitted upstream values, omitting failures. Given a `Publisher\u003cEvent\u003cString, MyError\u003e, Never\u003e`, this operator will return a `Publisher\u003cString, Never\u003e`.\n\n**Note**: This operator only works on publishers that were materialized with the `materialize()` operator.\n\n```swift\nlet values = PassthroughSubject\u003cString, MyError\u003e()\nenum MyError: Swift.Error {\n  case ohNo\n}\n\nvalues\n  .materialize()\n  .values()\n  .sink(receiveValue: { print(\"values: \\($0)\") })\n\nvalues.send(\"Hello\")\nvalues.send(\"World\")\nvalues.send(\"What's up?\")\nvalues.send(completion: .failure(.ohNo))\n```\n\n#### Output:\n\n```none\nvalues: \"Hello\"\nvalues: \"World\"\nvalues: \"What's up?\"\n```\n\n------\n\n### failures\n\nGiven a materialized publisher, publish only the emitted upstream failure, omitting values. Given a `Publisher\u003cEvent\u003cString, MyError\u003e, Never\u003e`, this operator will return a `Publisher\u003cMyError, Never\u003e`.\n\n**Note**: This operator only works on publishers that were materialized with the `materialize()` operator.\n\n```swift\nlet values = PassthroughSubject\u003cString, MyError\u003e()\nenum MyError: Swift.Error {\n  case ohNo\n}\n\nvalues\n  .materialize()\n  .failures()\n  .sink(receiveValue: { print(\"failures: \\($0)\") })\n\nvalues.send(\"Hello\")\nvalues.send(\"World\")\nvalues.send(\"What's up?\")\nvalues.send(completion: .failure(.ohNo))\n```\n\n#### Output:\n\n```none\nfailure: MyError.ohNo\n```\n\n------\n\n### dematerialize\n\nConverts a previously-materialized publisher into its original form. Given a `Publisher\u003cEvent\u003cString, MyError\u003e, Never\u003e`, this operator will return a `Publisher\u003cString, MyError\u003e`\n\n**Note**: This operator only works on publishers that were materialized with the `materialize()` operator.\n\n------\n\n### partition\n\nPartition a publisher's values into two separate publishers of values that match, and don't match, the provided predicate.\n\n```swift\nlet source = PassthroughSubject\u003cInt, Never\u003e()\n\nlet (even, odd) = source.partition { $0 % 2 == 0 }\n\neven.sink(receiveValue: { print(\"even: \\($0)\") })\nodd.sink(receiveValue: { print(\"odd: \\($0)\") })\n\nsource.send(1)\nsource.send(2)\nsource.send(3)\nsource.send(4)\nsource.send(5)\n```\n\n#### Output:\n\n```none\nodd: 1\neven: 2\nodd: 3\neven: 4\nodd: 5\n```\n\n------\n\n### ZipMany\n\nThis repo includes two overloads on Combine’s `Publisher.zip` methods (which, at the time of writing only go up to arity three).\n\nThis lets you arbitrarily zip many publishers and receive an array of inner publisher outputs back.\n\n```swift\nlet first = PassthroughSubject\u003cInt, Never\u003e()\nlet second = PassthroughSubject\u003cInt, Never\u003e()\nlet third = PassthroughSubject\u003cInt, Never\u003e()\nlet fourth = PassthroughSubject\u003cInt, Never\u003e()\n\nsubscription = first\n  .zip(with: second, third, fourth)\n  .map { $0.reduce(0, +) }\n  .sink(receiveValue: { print(\"zipped: \\($0)\") })\n\nfirst.send(1)\nsecond.send(2)\nthird.send(3)\nfourth.send(4)\n```\n\nYou may also use `.zip()` directly on a collection of publishers with the same output and failure types, e.g.\n\n```swift\n[first, second, third, fourth]\n  .zip()\n  .map { $0.reduce(0, +) }\n  .sink(receiveValue: { print(\"zipped: \\($0)\") })\n```\n\n#### Output:\n\n```none\nzipped: 10\n```\n\n------\n\n### MergeMany\n\nThis repo includes an extension for Collection that allows you to call `.merge()` directly on a collection of publishers with the same output and failure types.\n\nThis lets you arbitrarily merge many publishers and receive inner publisher outputs back from a single publisher.\n\n```swift\nlet first = PassthroughSubject\u003cInt, Never\u003e()\nlet second = PassthroughSubject\u003cInt, Never\u003e()\nlet third = PassthroughSubject\u003cInt, Never\u003e()\nlet fourth = PassthroughSubject\u003cInt, Never\u003e()\n\nsubscription = [first, second, third, fourth]\n  .merge()\n  .sink(receiveValue: { print(\"output: \\($0)\") })\n\nfirst.send(1)\nsecond.send(2)\nthird.send(3)\nfourth.send(4)\n```\n\n#### Output:\n\n```none\noutput: 1\noutput: 2\noutput: 3\noutput: 4\n```\n\n------\n\n### CombineLatestMany\n\nThis repo includes two overloads on Combine’s `Publisher.combineLatest` methods (which, at the time of writing only go up to arity three) and an `Collection.combineLatest` constrained extension.\n\nThis lets you arbitrarily combine many publishers and receive an array of inner publisher outputs back.\n\n```swift\nlet first = PassthroughSubject\u003cBool, Never\u003e()\nlet second = PassthroughSubject\u003cBool, Never\u003e()\nlet third = PassthroughSubject\u003cBool, Never\u003e()\nlet fourth = PassthroughSubject\u003cBool, Never\u003e()\n\nsubscription = [first, second, third, fourth]\n  .combineLatest()\n  .sink(receiveValue: { print(\"combineLatest: \\($0)\") })\n\nfirst.send(true)\nsecond.send(true)\nthird.send(true)\nfourth.send(true)\n\nfirst.send(false)\n```\n\n#### Output:\n\n```none\ncombineLatest: [true, true, true, true]\ncombineLatest: [false, true, true, true]\n```\n\n------\n\n### FilterMany\nFilters element of a publisher collection into a new publisher collection.\n```swift\nlet intArrayPublisher = PassthroughSubject\u003c[Int], Never\u003e()\n\nintArrayPublisher\n  .filterMany { $0.isMultiple(of: 2) }\n  .sink(receiveValue: { print($0) })\n\nintArrayPublisher.send([10, 2, 4, 3, 8])\n```\n\n#### Output:\n\n```\nnone\n[10, 2, 4, 8]\n```\n\n------\n\n### MapMany\n\nProjects each element of a publisher collection into a new publisher collection form.\n\n```swift\nlet intArrayPublisher = PassthroughSubject\u003c[Int], Never\u003e()\n    \nintArrayPublisher\n  .mapMany(String.init)\n  .sink(receiveValue: { print($0) })\n    \nintArrayPublisher.send([10, 2, 2, 4, 3, 8])\n```\n\n#### Output:\n\n```none\n[\"10\", \"2\", \"2\", \"4\", \"3\", \"8\"]\n```\n\n------\n\n### setOutputType\n\n`Publisher.setOutputType(to:)` is an analog to [`.setFailureType(to:)`](https://developer.apple.com/documentation/combine/publisher/3204753-setfailuretype) for when `Output` is constrained to `Never`. This is especially helpful when chaining operators after an [`.ignoreOutput()`](https://developer.apple.com/documentation/combine/publisher/3204714-ignoreoutput) call.\n\n------\n\n### removeAllDuplicates\n\n`Publisher.removeAllDuplicates` and `.removeAllDuplicates(by:)` are stricter forms of Apple’s [`Publisher.removeDuplicates`](https://developer.apple.com/documentation/combine/publisher/3204745-removeduplicates) and [`.removeDuplicates(by:)`](https://developer.apple.com/documentation/combine/publisher/3204746-removeduplicates)—the operators de-duplicate across _all_ previous value events, instead of pairwise.\n\nIf your `Output` doesn‘t conform to `Hashable` or `Equatable`, you may instead use the comparator-based version of this operator to decide whether two elements are equal.\n\n```swift\nsubscription = [1, 1, 2, 1, 3, 3, 4].publisher\n  .removeAllDuplicates()\n  .sink(receiveValue: { print(\"removeAllDuplicates: \\($0)\") })\n```\n\n#### Output:\n\n```none\nremoveAllDuplicates: 1\nremoveAllDuplicates: 2\nremoveAllDuplicates: 3\nremoveAllDuplicates: 4\n```\n\n------\n\n### share(replay:)\n\nSimilar to [`Publisher.share`](https://developer.apple.com/documentation/combine/publisher/3204754-share), `.share(replay:)` can be used to create a publisher instance with reference semantics which replays a pre-defined amount of value events to further subscribers.\n\n```swift\nlet subject = PassthroughSubject\u003cInt, Never\u003e()\n\nlet replayedPublisher = subject\n  .share(replay: 3)\n\nsubscription1 = replayedPublisher\n  .sink(receiveValue: { print(\"first subscriber: \\($0)\") })\n  \nsubject.send(1)\nsubject.send(2)\nsubject.send(3)\nsubject.send(4)\n\nsubscription2 = replayedPublisher\n  .sink(receiveValue: { print(\"second subscriber: \\($0)\") })\n```\n\n#### Output:\n\n```none\nfirst subscriber: 1\nfirst subscriber: 2\nfirst subscriber: 3\nfirst subscriber: 4\nsecond subscriber: 2\nsecond subscriber: 3\nsecond subscriber: 4\n```\n\n### prefix(duration:)\n\nAn overload on `Publisher.prefix` that republishes values for a provided `duration` (in seconds), and then completes.\n\n```swift\nlet subject = PassthroughSubject\u003cInt, Never\u003e()\n\nsubscription = subject\n  .prefix(duration: 0.5, on: DispatchQueue.main)\n  .sink(receiveValue: { print($0) })\n  \nsubject.send(1)\nsubject.send(2)\nsubject.send(3)\n\nDispatchQueue.main.asyncAfter(deadline: .now() + 1) {\n  subject.send(4)\n}\n```\n\n#### Output:\n\n```none\n1\n2\n3\n```\n\n### prefix(while:behavior:)\n\nAn overload on `Publisher.prefix(while:)` that allows for inclusion of the first element that doesn’t pass the `while` predicate.\n\n```swift\nlet subject = PassthroughSubject\u003cInt, Never\u003e()\n\nsubscription = subject\n  .prefix(\n    while: { $0 % 2 == 0 },\n    behavior: .inclusive\n  )\n  .sink(\n    receivecompletion: { print($0) },\n    receiveValue: { print($0) }\n  )\n  \nsubject.send(0)\nsubject.send(2)\nsubject.send(4)\nsubject.send(5)\n```\n\n```none\n0\n2\n4\n5\nfinished\n```\n\n### toggle()\n\nToggle each boolean element of a publisher collection.\n\n```swift\nlet subject = PassthroughSubject\u003cBool, Never\u003e()\n\nsubscription = subject\n  .toggle()\n  .sink(receiveValue: { print($0) })\n  \nsubject.send(true)\nsubject.send(false)\nsubject.send(true)\n```\n\n#### Output:\n\n```none\nfalse\ntrue\nfalse\n```\n\n### nwise\n\nGroups the elements of the source publisher into arrays of N consecutive elements.\n\n```swift\nlet subject = PassthroughSubject\u003cInt, Never\u003e()\n\nsubscription = subject\n  .nwise(3)\n  .sink(receiveValue: { print($0) })\n  \nsubject.send(1)\nsubject.send(2)\nsubject.send(3)\nsubject.send(4)\nsubject.send(5)\n```\n\n#### Output:\n\n```none\n[1, 2, 3]\n[2, 3, 4]\n[3, 4, 5]\n```\n\n### pairwise\n\nGroups the elements of the source publisher into tuples of the previous and current elements\n\n```swift\nlet subject = PassthroughSubject\u003cInt, Never\u003e()\n\nsubscription = subject\n  .pairwise()\n  .sink(receiveValue: { print(\"\\($0.0) -\u003e \\($0.1)\") })\n\nsubject.send(1)\nsubject.send(2)\nsubject.send(3)\nsubject.send(4)\nsubject.send(5)\n```\n\n#### Output:\n\n```none\n1 -\u003e 2\n2 -\u003e 3\n3 -\u003e 4\n4 -\u003e 5\n```\n\n### ignoreOutput(setOutputType:)\n\nShorthand for both ignoring a publisher’s value events and re-writing its `Output` generic.\n\n```swift\nlet onlyAFour = [\"1\", \"2\", \"3\"].publisher\n  .ignoreOutput(setOutputType: Int.self)\n  .append(4)\n```\n\n### ignoreFailure\n\nCombineExt provides a couple of overloads to ignore errors and optionally specify a new error type and whether to trigger completions in such cases.\n\n- `ignoreFailure(completeImmediately:)`\n- `ignoreFailure(setFailureType:completeImmediately:)`\n\n```swift\nenum AnError {\n  case someError \n}\n\nlet subject = PassthroughSubject\u003cInt, AnError\u003e()\n\nsubscription = subject\n  .ignoreFailure() // The `completeImmediately` parameter defaults to `true`.\n  .sink(receiveValue: { print($0) }, receiveCompletion: { print($0) })\n\nsubject.send(1)\nsubject.send(2)\nsubject.send(3)\nsubject.send(completion: .failure(.someError))\n```\n\n#### Output:\n\n```none\n1\n2\n3\n.finished\n```\n------\n\n### mapToResult\n\nTransforms a publisher of type `AnyPublisher\u003cOutput, Failure\u003e` to `AnyPublisher\u003cResult\u003cOutput, Failure\u003e, Never\u003e`\n\n```swift\nenum AnError: Error {\n    case someError\n}\n\nlet subject = PassthroughSubject\u003cInt, AnError\u003e()\n\nlet subscription = subject\n    .mapToResult()\n    .sink(receiveCompletion: { print(\"completion: \\($0)\") },\n          receiveValue: { print(\"value: \\($0)\") })\n\nsubject.send(1)\nsubject.send(2)\nsubject.send(3)\nsubject.send(completion: .failure(.someError))\n```\n\n#### Output\n\n```none\nvalue: success(1)\nvalue: success(2)\nvalue: success(3)\nvalue: failure(AnError.someError)\ncompletion: finished\n```\n\n------\n\n### flatMapBatches(of:)\n\n`Collection.flatMapBatches(of:)` subscribes to the receiver’s contained publishers in batches and returns their outputs in batches, too (while maintaining order). Subsequent batches of publishers are only subscribed to when prior batches successfully complete — any one failure is forwarded downstream.\n\n```swift\nlet ints = (1...6).map(Just.init)\n\nsubscription = ints\n  .flatMapBatches(of: 2)\n  .sink(receiveCompletion: { print($0) }, receiveValue: { print($0) })\n```\n\n#### Output:\n\n```none\n[1, 2]\n[3, 4]\n[5, 6]\n.finished\n```\n\n## Publishers\n\nThis section outlines some of the custom Combine publishers CombineExt provides\n\n### AnyPublisher.create\n\nA publisher which accepts a closure with a subscriber argument, to which you can dynamically send value or completion events.\n\nThis lets you easily create custom publishers to wrap any non-publisher asynchronous work, while still respecting the downstream consumer's backpressure demand.\n\nYou should return a `Cancellable`-conforming object from the closure in which you can define any cleanup actions to execute when the pubilsher completes or the subscription to the publisher is canceled.\n\n```swift\nAnyPublisher\u003cString, MyError\u003e.create { subscriber in\n  // Values\n  subscriber.send(\"Hello\")\n  subscriber.send(\"World!\")\n  \n  // Complete with error\n  subscriber.send(completion: .failure(MyError.someError))\n  \n  // Or, complete successfully\n  subscriber.send(completion: .finished)\n\n  return AnyCancellable { \n    // Perform cleanup\n  }\n}\n```\n\nYou can also use an `AnyPublisher` initializer with the same signature:\n\n```swift\nAnyPublisher\u003cString, MyError\u003e { subscriber in \n    /// ...\n    return AnyCancellable { }\n```\n\n------\n\n### CurrentValueRelay\n\nA `CurrentValueRelay` is identical to a `CurrentValueSubject` with two main differences:\n\n* It only accepts values, but not completion events, which means it cannot fail.\n* It only publishes a `.finished` event upon deallocation.\n\n```swift\nlet relay = CurrentValueRelay\u003cString\u003e(\"well...\")\n\nrelay.sink(receiveValue: { print($0) }) // replays current value, e.g. \"well...\"\n\nrelay.accept(\"values\")\nrelay.accept(\"only\")\nrelay.accept(\"provide\")\nrelay.accept(\"great\")\nrelay.accept(\"guarantees\")\n```\n\n#### Output:\n\n```none\nwell...\nvalues\nonly\nprovide\ngreat\nguarantees\n```\n\n------\n\n### PassthroughRelay\n\nA `PassthroughRelay` is identical to a `PassthroughSubject` with two main differences:\n\n* It only accepts values, but not completion events, which means it cannot fail.\n* It only publishes a `.finished` event upon deallocation.\n\n```swift\nlet relay = PassthroughRelay\u003cString\u003e()\nrelay.accept(\"well...\")\n\nrelay.sink(receiveValue: { print($0) }) // does not replay past value(s)\n\nrelay.accept(\"values\")\nrelay.accept(\"only\")\nrelay.accept(\"provide\")\nrelay.accept(\"great\")\nrelay.accept(\"guarantees\")\n```\n\n#### Output:\n\n```none\nvalues\nonly\nprovide\ngreat\nguarantees\n```\n\n## Subjects\n\n### ReplaySubject\n\nA Combine analog to Rx’s [`ReplaySubject` type](http://reactivex.io/documentation/subject.html). It’s similar to a [`CurrentValueSubject`](https://developer.apple.com/documentation/combine/currentvaluesubject) in that it buffers values, but, it takes it a step further in allowing consumers to specify the number of values to buffer and replay to future subscribers. Also, it will handle forwarding any completion events after the buffer is cleared upon subscription.\n\n```swift\nlet subject = ReplaySubject\u003cInt, Never\u003e(bufferSize: 3)\n\nsubject.send(1)\nsubject.send(2)\nsubject.send(3)\nsubject.send(4)\n\nsubject\n  .sink(receiveValue: { print($0) })\n\nsubject.send(5)\n```\n\n#### Output:\n\n```none\n2\n3\n4\n5\n```\n\n## License\n\nMIT, of course ;-) See the [LICENSE](LICENSE) file. \n\nThe Apple logo and the Combine framework are property of Apple Inc.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcombinecommunity%2Fcombineext","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcombinecommunity%2Fcombineext","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcombinecommunity%2Fcombineext/lists"}