Skill

Master Combine Publishers in Swift

A Combine publisher expert that builds custom Swift publishers with backpressure handling, exponential-backoff retry, and resource lifecycle management.


73
Spark score
out of 100
Updated 11 days ago
Version 1.0.0
Models

Add to Favorites

Why it matters

Become an expert in Apple's Combine framework for reactive Swift development. This asset provides deep knowledge and practical examples for creating custom publishers, managing memory, handling backpressure, and optimizing performance.

Outcomes

What it gets done

01

Create custom Combine publishers from scratch.

02

Implement advanced publisher patterns like backpressure handling and merging.

03

Optimize Combine publisher performance and resource management.

04

Write effective tests for Combine publishers using Test Scheduler.

Install

Add it to your toolbox

Run in your project directory:

curl -fsSL https://spark.entire.vc/get/vb-combine-publisher | bash

Overview

Combine Publisher агент

A Combine publisher expert that builds custom Swift publishers: backpressure-aware buffering, priority-based merging, exponential-backoff retry, and guaranteed resource cleanup on completion or cancellation. Use it when Swift/Combine code needs a custom publisher beyond the built-in operators - retry, backpressure, priority merging, or resource cleanup.

What it does

Builds custom Combine publishers for Swift's reactive programming framework, grounded in two core principles: publisher lifecycle (publishers are declarative and lazy, don't execute until subscribed, must handle .finished/.failure completion properly, use the right scheduler for UI vs. background work, and support cancellation for resource cleanup) and memory management (store cancellables in Set<AnyCancellable> or via .store(in:), avoid retain cycles with [weak self], cancel subscriptions in deinit when storing manually). It implements custom publishers from scratch - a Publisher/Subscription conforming type pair, such as a timer publisher that starts a repeating Timer on request(_:) and invalidates it on cancel() - and publisher extensions like exponential-backoff retry, which recursively retries a failed publisher with a delay that grows by a configurable multiplier each attempt before finally failing. Advanced patterns cover backpressure (a buffered publisher wrapping Combine's .buffer(size:prefetch:whenFull:) with drop-oldest, drop-newest, or error strategies) and priority-aware merging of two publishers (tagging values by priority and suppressing a low-priority value that arrives too soon after a high-priority one). Performance patterns cover lazy transformation (deferring a map operation via .handleEvents) and resource lifecycle management (a publisher that creates a resource, runs an operation against it, and guarantees cleanup on both completion and cancellation). Testing uses XCTest with XCTestExpectation and .store(in:) to assert the exact sequence of values a custom publisher emits.

When to use - and when NOT to

Use it when Swift/Combine code needs a custom publisher beyond what the built-in operators provide - a timer source, retry-with-backoff, backpressure-aware buffering, priority merging, or guaranteed resource cleanup on cancellation - rather than composing only stock Combine operators.

Inputs and outputs

Input is the reactive data source or transformation to model as a Combine publisher. Output is a custom Publisher/Subscription conformance, a publisher extension such as retry-with-backoff, a backpressure or merge strategy, or an XCTest suite verifying the publisher's emitted value sequence.

Integrations

Built entirely on Apple's Combine framework (Publisher, Subscriber, Subscription, AnyCancellable, Publishers.Merge, DispatchQueue, Timer) and XCTest for verification, targeting Swift application code that already uses Combine for reactive state.

Who it's for

For Swift developers writing custom Combine publishers rather than just chaining built-in operators. Eight practices apply throughout: erase publisher types at API boundaries with eraseToAnyPublisher(), favor composition over inheritance, implement demand handling correctly in custom subscriptions, pick the right scheduler (.main for UI, .global() for computation), handle errors with .catch/.retry/.replaceError rather than crashing, avoid blocking operations inside a publisher chain, use .share() when multiple subscribers need one expensive upstream operation, and always implement cancellation for resource cleanup.

extension Publisher {
    func retryWithExponentialBackoff(
        retries: Int,
        initialDelay: TimeInterval = 1.0,
        multiplier: Double = 2.0
    ) -> AnyPublisher<Output, Failure> {
        self.catch { error -> AnyPublisher<Output, Failure> in
            if retries > 0 {
                let delay = initialDelay * pow(multiplier, Double(retries))
                return Just(())
                    .delay(for: .seconds(delay), scheduler: DispatchQueue.global())
                    .flatMap { _ in
                        self.retryWithExponentialBackoff(
                            retries: retries - 1,
                            initialDelay: initialDelay,
                            multiplier: multiplier
                        )
                    }
                    .eraseToAnyPublisher()
            } else {
                return Fail(error: error).eraseToAnyPublisher()
            }
        }
        .eraseToAnyPublisher()
    }
}

FAQ

Common questions

Discussion

Questions & comments · 0

Sign In Sign in to leave a comment.