Skip to content

Add Publishers.AgainAt - #29

Merged
atdrendel merged 4 commits into
mainfrom
again-at
May 3, 2026
Merged

Add Publishers.AgainAt#29
atdrendel merged 4 commits into
mainfrom
again-at

Conversation

@atdrendel

Copy link
Copy Markdown
Contributor
  • Add Publishers.AgainAt
  • Add comprehensive test coverage for existing publishers

Copilot AI review requested due to automatic review settings May 3, 2026 16:34
@atdrendel

Copy link
Copy Markdown
Contributor Author

@copilot Update the README.md to ensure it is up-to-date. The next version will be v6.1.0

Agent-Logs-Url: https://github.com/shareup/combine-extensions/sessions/d063065b-f586-4124-bcd1-4b60b71f3620

Co-authored-by: atdrendel <202402+atdrendel@users.noreply.github.com>

Copilot AI commented May 3, 2026

Copy link
Copy Markdown

@copilot Update the README.md to ensure it is up-to-date. The next version will be v6.1.0

Done in a74f63a. The README now lists all publishers (including AgainAt, Distinct, MulticastLatest), adds an Extensions section (Cancellable.onCancel(), Publisher.sink()), a Schedulers section (TestScheduler, UIScheduler), and updates the version reference to 6.1.0.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Combine operator Publishers.AgainAt / Publisher.againAt(scheduler:options:) that pairs each emitted value with a timer object that can schedule re-publication of the latest value at a requested scheduler time. Also expands unit test coverage across many existing publishers/operators in CombineExtensions and helpers in CombineTestExtensions.

Changes:

  • Introduce Publishers.AgainAt operator and extensive behavioral tests (republish scheduling, demand handling, cancellation/completion behavior).
  • Add/expand test coverage for multiple existing operators (distinct, enumerate, reduceLatest, retryIf, throttleWhile, stream publishers, subscription stores, etc.).
  • Minor cleanup and concurrency annotation updates (e.g., _Expectation Sendable annotation, small formatting updates).

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
Tests/CombineExtensionsTests/UISchedulerTests.swift Adds coverage for UIScheduler.now/minimumTolerance mirroring main queue.
Tests/CombineExtensionsTests/ThrottleWhileTests.swift Adds more edge-case tests (completion/failure while throttled, regulator timing), plus whitespace cleanup.
Tests/CombineExtensionsTests/TestSchedulerTests.swift Adds more behavioral tests around run/advance, ordering, and interval cancellation.
Tests/CombineExtensionsTests/SingleSubscriptionStoreTests.swift Adds tests for initializer behavior and replacement/removal semantics.
Tests/CombineExtensionsTests/RetryIfTests.swift Adds additional tests for retry convenience and cancellation/attempt counting behavior.
Tests/CombineExtensionsTests/ReduceLatestTests.swift Adds demand/backpressure and completion/failure/cancel tests with a manual subscriber helper.
Tests/CombineExtensionsTests/PublisherTestExtensionsTests.swift Adds tests for various expectation helpers and updates call-site formatting.
Tests/CombineExtensionsTests/PublisherSinkTests.swift New tests for sink overloads in CombineExtensions.
Tests/CombineExtensionsTests/OutputStreamPublisherTests.swift Adds tests around demand forwarding, failure behavior, and append-to-URL semantics; minor formatting cleanup.
Tests/CombineExtensionsTests/OnCancelTests.swift Adds tests to validate ordering and “only once” behavior of onCancel, plus a spy cancellable helper.
Tests/CombineExtensionsTests/MulticastLatestTests.swift Adds tests for late subscribers, no initial emission, and completion/failure propagation; minor formatting cleanup.
Tests/CombineExtensionsTests/KeyedSubscriptionStoreTests.swift Adds tests for initializer, replacement cancellation, and removed-subscription lifetime behavior.
Tests/CombineExtensionsTests/InputStreamPublisherTests.swift Adds tests for empty inputs, demand-driven chunking, and empty file behavior with a manual subscriber helper.
Tests/CombineExtensionsTests/EnumeratedTests.swift Adds tests for negative start index, per-subscriber state isolation, and failure propagation.
Tests/CombineExtensionsTests/DistinctTests.swift Adds tests for empty/duplicate dropping and subscriber state behavior; minor loop refactor in concurrency test.
Tests/CombineExtensionsTests/BufferPassthroughSubjectTests.swift Adds tests around buffered demand, buffered completion, post-completion behavior, and repeated completion; adds manual-demand subscriber helper.
Tests/CombineExtensionsTests/AnyConnectablePublisherTests.swift Adds tests ensuring erased connectable publisher forwards, connects, autoconnects, and cancels properly.
Tests/CombineExtensionsTests/AgainAtTests.swift New comprehensive test suite for againAt/Publishers.AgainAt.
Sources/CombineTestExtensions/Publisher+Test.swift Marks internal _Expectation as @unchecked Sendable.
Sources/CombineExtensions/MulticastLatest.swift Minor formatting change to multicast factory closure style.
Sources/CombineExtensions/AgainAt.swift New implementation of Publishers.AgainAt and Publisher.againAt(...).
Comments suppressed due to low confidence (1)

Sources/CombineTestExtensions/Publisher+Test.swift:532

  • _Expectation is now marked @unchecked Sendable, but it has mutable state (token, fulfillmentCount) that is mutated in fulfill(). If fulfill() can be invoked from multiple threads (e.g., when expectations are fulfilled from publishers emitting on different queues), this becomes a potential data race. Consider protecting the mutable state with synchronization (lock/atomic), or ensuring fulfill() and token cancellation always run on a single executor/queue before asserting Sendable conformance.
private class _Expectation: XCTestExpectation, @unchecked Sendable {
    var token: AnyCancellable?

    private var fulfillmentCount: Int = 0

    override func fulfill() {
        super.fulfill()

        fulfillmentCount += 1
        if fulfillmentCount == expectedFulfillmentCount {
            token?.cancel()
            token = nil
        }
    }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Sources/CombineExtensions/AgainAt.swift
@atdrendel
atdrendel merged commit e8e118a into main May 3, 2026
2 checks passed
@atdrendel
atdrendel deleted the again-at branch May 3, 2026 17:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants