Merge pull request #1 from samsonjs/xcode26

Fixes for Swift 6.2 on iOS 26 and macOS 26
This commit is contained in:
Sami Samhuri 2025-06-13 11:34:50 -07:00 committed by GitHub
commit 2d4c37d9da
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 25 additions and 18 deletions

View file

@ -32,8 +32,8 @@ class SimplestVersion {
This example uses the context parameter to avoid reference cycles with `self`.
```swift
class WithContext {
var cancellables = Set<AnyAsyncCancellable>()
final class WithContext: Sendable {
nonisolated(unsafe) var cancellables = Set<AnyAsyncCancellable>()
init() {
NotificationCenter.default
@ -55,7 +55,7 @@ class WithContext {
Working with Combine publishers is trivial thanks to [`AnyPublisher.values`][values].
```swift
import Combine
@preconcurrency import Combine
class CombineExample {
var cancellables = Set<AnyAsyncCancellable>()

View file

@ -20,7 +20,7 @@ public final class AsyncMonitor: Hashable, AsyncCancellable {
/// Defaults to `#isolation`, preserving the caller's actor isolation.
/// - sequence: The asynchronous sequence of elements to observe.
/// - block: A closure to execute for each element yielded by the sequence.
@available(iOS 18, *)
@available(iOS 18, macOS 15, *)
public init<Element: Sendable>(
isolation: isolated (any Actor)? = #isolation,
sequence: any AsyncSequence<Element, Never>,
@ -41,6 +41,7 @@ public final class AsyncMonitor: Hashable, AsyncCancellable {
/// - sequence: The asynchronous sequence of elements to observe.
/// - block: A closure to execute for each element yielded by the sequence.
@available(iOS, introduced: 17, obsoleted: 18)
@available(macOS, introduced: 14, obsoleted: 15)
public init<Element: Sendable, Sequence>(
sequence: sending Sequence,
@_inheritActorContext performing block: @escaping @Sendable (Element) async -> Void

View file

@ -1,4 +1,4 @@
@available(iOS 18, *)
@available(iOS 18, macOS 15, *)
public extension AsyncSequence where Element: Sendable, Failure == Never {
/// Observes the elements yielded by this sequence and executes the given closure with each element.
///
@ -36,6 +36,7 @@ public extension AsyncSequence where Element: Sendable, Failure == Never {
}
@available(iOS, introduced: 17, obsoleted: 18)
@available(macOS, introduced: 14, obsoleted: 15)
public extension AsyncSequence where Self: Sendable, Element: Sendable {
/// Observes the elements yielded by this sequence and executes the given closure with each element.
///

View file

@ -25,7 +25,7 @@ public extension NSObjectProtocol where Self: NSObject {
}
}
@available(iOS 18, *)
@available(iOS 18, macOS 15, *)
public extension NSObjectProtocol where Self: NSObject {
/// Observes changes to the specified key path on the object and asynchronously yields each value. Values must be `Sendable`.
///
@ -44,6 +44,7 @@ public extension NSObjectProtocol where Self: NSObject {
}
@available(iOS, introduced: 17, obsoleted: 18)
@available(macOS, introduced: 14, obsoleted: 15)
public extension NSObjectProtocol where Self: NSObject {
/// Observes changes to the specified key path on the object and asynchronously yields each value. Values must be `Sendable`.
///

View file

@ -49,12 +49,12 @@ class AsyncMonitorTests {
try await Task.sleep(for: .milliseconds(10))
}
class Owner {
let deinitHook: () -> Void
final class Owner: Sendable {
let deinitHook: @Sendable () -> Void
private var cancellable: (any AsyncCancellable)?
nonisolated(unsafe) private var cancellable: (any AsyncCancellable)?
init(center: NotificationCenter, deinitHook: @escaping () -> Void) {
init(center: NotificationCenter, deinitHook: @escaping @Sendable () -> Void) {
self.deinitHook = deinitHook
let name = Notification.Name("irrelevant name")
cancellable = center.notifications(named: name)
@ -78,8 +78,10 @@ class AsyncMonitorTests {
}
}
final class SendableObject: NSObject, Sendable {}
@Test func stopsCallingBlockWhenContextIsDeallocated() async throws {
var context: NSObject? = NSObject()
var context: SendableObject? = SendableObject()
subject = center.notifications(named: name)
.map(\.name)
.monitor(context: context!) { context, receivedName in

View file

@ -9,22 +9,22 @@ class AsyncKVOTests {
@Test(.timeLimit(.minutes(1)))
func monitorValuesYieldsChanges() async throws {
let subject = try #require(subject)
var values = [Double]()
let values = ValueLocker(value: [Double]())
let total = 3
cancellable = subject.values(for: \.fractionCompleted)
.prefix(total)
.monitor { progress in
values.append(progress)
values.modify { $0.append(progress) }
}
for n in 1...total {
subject.completedUnitCount += 1
while values.count < n {
while values.value.count < n {
try await Task.sleep(for: .microseconds(2))
}
}
#expect(values.count == total)
#expect(values.value.count == total)
}
// It's important that the test and the progress-observing task are not on the same actor, so

View file

@ -3,6 +3,8 @@ import Foundation
// MARK: Basics
extension Notification: @unchecked @retroactive Sendable {}
class SimplestVersion {
let cancellable = NotificationCenter.default
.notifications(named: .NSCalendarDayChanged)
@ -12,8 +14,8 @@ class SimplestVersion {
}
}
class WithContext {
var cancellables = Set<AnyAsyncCancellable>()
final class WithContext: Sendable {
nonisolated(unsafe) var cancellables = Set<AnyAsyncCancellable>()
init() {
NotificationCenter.default
@ -31,7 +33,7 @@ class WithContext {
// MARK: - Combine
import Combine
@preconcurrency import Combine
class CombineExample {
var cancellables = Set<AnyAsyncCancellable>()