Skip to content

Commit 3df3260

Browse files
authored
Merge pull request #1318 from swiftlang/automerge/merge-main-2025-05-26_09-13
Merge `main` into `release/6.2`
2 parents 6c1b547 + 4d74f7a commit 3df3260

17 files changed

+329
-104
lines changed

Proposals/0011-concurrency-safe-notifications.md

Lines changed: 68 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -209,36 +209,37 @@ For `MainActorMessage`:
209209
@available(FoundationPreview 0.5, *)
210210
extension NotificationCenter {
211211
// e.g. addObserver(of: workspace, for: .willLaunchApplication) { message in ... }
212-
public func addObserver<I: MessageIdentifier, M: MainActorMessage>(of subject: M.Subject,
213-
for identifier: I,
214-
using observer: @escaping @MainActor (M) -> Void)
215-
-> ObservationToken where I.MessageType == M,
216-
M.Subject: AnyObject
212+
public func addObserver<Identifier: MessageIdentifier, Message: MainActorMessage>(
213+
of subject: Message.Subject,
214+
for identifier: Identifier,
215+
using observer: @escaping @MainActor (Message) -> Void
216+
) -> ObservationToken where Identifier.MessageType == Message, Message.Subject: AnyObject
217217

218-
public func addObserver<I: MessageIdentifier, M: MainActorMessage>(of subject: M.Subject,
219-
for identifier: I,
220-
using observer: @escaping @MainActor (M) -> Void)
221-
-> ObservationToken where I.MessageType == M,
222-
M.Subject: Identifiable,
223-
M.Subject.ID == ObjectIdentifier
218+
public func addObserver<Identifier: MessageIdentifier, Message: MainActorMessage>(
219+
of subject: Message.Subject,
220+
for identifier: Identifier,
221+
using observer: @escaping @MainActor (Message) -> Void
222+
) -> ObservationToken where Identifier.MessageType == Message, Message.Subject: Identifiable, Message.Subject.ID == ObjectIdentifier
224223

225224
// e.g. addObserver(of: NSWorkspace.self, for: .willLaunchApplication) { message in ... }
226-
public func addObserver<I: MessageIdentifier, M: MainActorMessage>(of subject: M.Subject.Type,
227-
for identifier: I,
228-
using observer: @escaping @MainActor (M) -> Void)
229-
-> ObservationToken where I.MessageType == M
225+
public func addObserver<Identifier: MessageIdentifier, Message: MainActorMessage>(
226+
of subject: Message.Subject.Type,
227+
for identifier: Identifier,
228+
using observer: @escaping @MainActor (Message) -> Void
229+
) -> ObservationToken where Identifier.MessageType == Message
230230

231231
// e.g. addObserver(for: NSWorkspace.WillLaunchApplication.self) { message in ... }
232-
public func addObserver<M: MainActorMessage>(of subject: M.Subject? = nil,
233-
for messageType: M.Type,
234-
using observer: @escaping @MainActor (M) -> Void)
235-
-> ObservationToken where M.Subject: AnyObject
236-
237-
public func addObserver<M: MainActorMessage>(of subject: M.Subject? = nil,
238-
for messageType: M.Type,
239-
using observer: @escaping @MainActor (M) -> Void)
240-
-> ObservationToken where M.Subject: Identifiable,
241-
M.Subject.ID == ObjectIdentifier
232+
public func addObserver<Message: MainActorMessage>(
233+
of subject: Message.Subject? = nil,
234+
for messageType: Message.Type,
235+
using observer: @escaping @MainActor (Message) -> Void
236+
) -> ObservationToken where Message.Subject: AnyObject
237+
238+
public func addObserver<Message: MainActorMessage>(
239+
of subject: Message.Subject? = nil,
240+
for messageType: Message.Type,
241+
using observer: @escaping @MainActor (Message) -> Void
242+
) -> ObservationToken where Message.Subject: Identifiable, Message.Subject.ID == ObjectIdentifier
242243
}
243244
```
244245

@@ -247,34 +248,35 @@ And for `AsyncMessage`:
247248
```swift
248249
@available(FoundationPreview 0.5, *)
249250
extension NotificationCenter {
250-
public func addObserver<I: MessageIdentifier, M: AsyncMessage>(of subject: M.Subject,
251-
for identifier: I,
252-
using observer: @escaping @Sendable (M) async -> Void)
253-
-> ObservationToken where I.MessageType == M,
254-
M.Subject: AnyObject
255-
256-
public func addObserver<I: MessageIdentifier, M: AsyncMessage>(of subject: M.Subject,
257-
for identifier: I,
258-
using observer: @escaping @Sendable (M) async -> Void)
259-
-> ObservationToken where I.MessageType == M,
260-
M.Subject: Identifiable,
261-
M.Subject.ID == ObjectIdentifier
262-
263-
public func addObserver<I: MessageIdentifier, M: AsyncMessage>(of subject: M.Subject.Type,
264-
for identifier: I,
265-
using observer: @escaping @Sendable (M) async -> Void)
266-
-> ObservationToken where I.MessageType == M
251+
public func addObserver<Identifier: MessageIdentifier, Message: AsyncMessage>(
252+
of subject: Message.Subject,
253+
for identifier: Identifier,
254+
using observer: @escaping @Sendable (Message) async -> Void
255+
) -> ObservationToken where Identifier.MessageType == Message, Message.Subject: AnyObject
256+
257+
public func addObserver<Identifier: MessageIdentifier, Message: AsyncMessage>(
258+
of subject: Message.Subject,
259+
for identifier: Identifier,
260+
using observer: @escaping @Sendable (Message) async -> Void
261+
) -> ObservationToken where Identifier.MessageType == Message, Message.Subject: Identifiable, Message.Subject.ID == ObjectIdentifier
262+
263+
public func addObserver<Identifier: MessageIdentifier, Message: AsyncMessage>(
264+
of subject: Message.Subject.Type,
265+
for identifier: Identifier,
266+
using observer: @escaping @Sendable (Message) async -> Void
267+
) -> ObservationToken where Identifier.MessageType == Message
267268

268-
public func addObserver<M: AsyncMessage>(of subject: M.Subject? = nil,
269-
for messageType: M.Type,
270-
using observer: @escaping @Sendable (M) async -> Void)
271-
-> ObservationToken where M.Subject: AnyObject
269+
public func addObserver<Message: AsyncMessage>(
270+
of subject: Message.Subject? = nil,
271+
for messageType: Message.Type,
272+
using observer: @escaping @Sendable (Message) async -> Void
273+
) -> ObservationToken where Message.Subject: AnyObject
272274

273-
public func addObserver<M: AsyncMessage>(of subject: M.Subject? = nil,
274-
for messageType: M.Type,
275-
using observer: @escaping @Sendable (M) async -> Void)
276-
-> ObservationToken where M.Subject: Identifiable,
277-
M.Subject.ID == ObjectIdentifier
275+
public func addObserver<Message: AsyncMessage>(
276+
of subject: Message.Subject? = nil,
277+
for messageType: Message.Type,
278+
using observer: @escaping @Sendable (Message) async -> Void
279+
) -> ObservationToken where Message.Subject: Identifiable, Message.Subject.ID == ObjectIdentifier
278280
}
279281
```
280282

@@ -302,40 +304,31 @@ extension NotificationCenter {
302304
of subject: Message.Subject,
303305
for identifier: Identifier,
304306
bufferSize limit: Int = 10
305-
)
306-
-> some AsyncSequence<Message, Never> where Identifier.MessageType == Message,
307-
Message.Subject: AnyObject
307+
) -> some AsyncSequence<Message, Never> where Identifier.MessageType == Message, Message.Subject: AnyObject
308308

309309
public func messages<Identifier: MessageIdentifier, Message: AsyncMessage>(
310310
of subject: Message.Subject,
311311
for identifier: Identifier,
312312
bufferSize limit: Int = 10
313-
)
314-
-> some AsyncSequence<Message, Never> where Identifier.MessageType == Message,
315-
Message.Subject: Identifiable,
316-
Message.Subject.ID == ObjectIdentifier {}
313+
) -> some AsyncSequence<Message, Never> where Identifier.MessageType == Message, Message.Subject: Identifiable, Message.Subject.ID == ObjectIdentifier {}
317314

318315
public func messages<Identifier: MessageIdentifier, Message: AsyncMessage>(
319316
of subject: Message.Subject.Type,
320317
for identifier: Identifier,
321318
bufferSize limit: Int = 10
322-
)
323-
-> some AsyncSequence<Message, Never> where Identifier.MessageType == Message
319+
) -> some AsyncSequence<Message, Never> where Identifier.MessageType == Message
324320

325321
public func messages<Message: AsyncMessage>(
326322
of subject: Message.Subject? = nil,
327323
for messageType: Message.Type,
328324
bufferSize limit: Int = 10
329-
)
330-
-> some AsyncSequence<Message, Never> where Message.Subject: AnyObject
325+
) -> some AsyncSequence<Message, Never> where Message.Subject: AnyObject
331326

332327
public func messages<Message: AsyncMessage>(
333328
of subject: Message.Subject? = nil,
334329
for messageType: Message.Type,
335330
bufferSize limit: Int = 10
336-
)
337-
-> some AsyncSequence<Message, Never> where Message.Subject: Identifiable,
338-
Message.Subject.ID == ObjectIdentifier
331+
) -> some AsyncSequence<Message, Never> where Message.Subject: Identifiable, Message.Subject.ID == ObjectIdentifier
339332
}
340333
```
341334

@@ -366,27 +359,25 @@ extension NotificationCenter {
366359
// MainActorMessage post()
367360

368361
@MainActor
369-
public func post<M: MainActorMessage>(_ message: M, subject: M.Subject)
370-
where M.Subject: AnyObject
362+
public func post<Message: MainActorMessage>(_ message: Message, subject: Message.Subject)
363+
where Message.Subject: AnyObject
371364

372365
@MainActor
373-
public func post<M: MainActorMessage>(_ message: M, subject: M.Subject)
374-
where M.Subject: Identifiable,
375-
M.Subject.ID == ObjectIdentifier
366+
public func post<Message: MainActorMessage>(_ message: Message, subject: Message.Subject)
367+
where Message.Subject: Identifiable, Message.Subject.ID == ObjectIdentifier
376368

377369
@MainActor
378-
public func post<M: MainActorMessage>(_ message: M, subject: M.Subject.Type = M.Subject.self)
370+
public func post<Message: MainActorMessage>(_ message: Message, subject: Message.Subject.Type = Message.Subject.self)
379371

380372
// AsyncMessage post()
381373

382-
public func post<M: AsyncMessage>(_ message: M, subject: M.Subject)
383-
where M.Subject: AnyObject
374+
public func post<Message: AsyncMessage>(_ message: Message, subject: Message.Subject)
375+
where Message.Subject: AnyObject
384376

385-
public func post<M: AsyncMessage>(_ message: M, subject: M.Subject)
386-
where M.Subject: Identifiable,
387-
M.Subject.ID == ObjectIdentifier
377+
public func post<Message: AsyncMessage>(_ message: Message, subject: Message.Subject)
378+
where Message.Subject: Identifiable, Message.Subject.ID == ObjectIdentifier
388379

389-
public func post<M: AsyncMessage>(_ message: M, subject: M.Subject.Type = M.Subject.self)
380+
public func post<Message: AsyncMessage>(_ message: Message, subject: Message.Subject.Type = Message.Subject.self)
390381
}
391382
```
392383

Proposals/0026-preferredLocales.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Locale.preferredLocales
2+
3+
* Proposal: [SF-0026](0026-preferredLocales.md)
4+
* Authors: [करन मिश्र · Karan Miśra](https://github.com/karan-misra)
5+
* Review Manager: TBD
6+
* Status: **Awaiting review**
7+
* Review: ([pitch](https://forums.swift.org/t/pitch-introduce-locale-preferredlocales/79900))
8+
9+
## Introduction
10+
11+
Add `Locale.preferredLocales` as an alternative to `Locale.preferredLanguages` that returns `[Locale]` instead of `[String]`.
12+
13+
## Motivation
14+
15+
Currently, `Locale.preferredLanguages` is the only way to retrieve the list of languages that the user has specified in Language & Region settings. This follows its predecessor `+[NSLocale preferredLanguages]` and returns an array of `String`s instead of `Locale`s. Processing and manipulating strings is complex and errorprone for clients.
16+
17+
This proposal introduces `Locale.preferredLocales` as a way to retrieve the same information, but in the form of an array of `Locale`s which will allow clients to use the information more easily and with fewer errors, specifically when used to customize the presentation of data within their apps such that content in the user’s preferred languages is more prominent.
18+
19+
## Proposed solution
20+
21+
We propose adding `preferredLocales` as a static variable on `Locale`, similarly to `preferredLanguages`. One of the primary use cases is to allow apps to build language selection menus in which the user’s preferred locales are bubbled up to the top. This can be achieved with the proposed `preferredLocales` API as follows:
22+
23+
```swift
24+
// When building a language selection menu, `matchedLocales` would be shown at the top, and `otherLocales` would be shown below, with a visual divider.
25+
var matchedLocales = []
26+
var otherLocales = []
27+
let availableLocales = // ... array of Locale objects ...
28+
for locale in availableLocales {
29+
var foundMatch = false
30+
for preferredLocale in preferredLocales {
31+
if locale.language.isEquivalent(to: preferredLocale.language) {
32+
matchedLocales.append(locale)
33+
foundMatch = true
34+
break
35+
}
36+
}
37+
if !foundMatch {
38+
otherLocales.append(locale)
39+
}
40+
}
41+
```
42+
43+
## Detailed design
44+
45+
```swift
46+
public struct Locale : Hashable, Equatable, Sendable {
47+
48+
/// Returns a list of the user’s preferred locales, as specified in Language & Region settings, taking into account any per-app language overrides.
49+
@available(FoundationPreview 6.2, *)
50+
public static var preferredLocales: [Locale]
51+
}
52+
```
53+
54+
## Source compatibility
55+
56+
There is no impact on source compatibility or existing code.
57+
58+
## Implications on adoption
59+
60+
This feature can be freely adopted and un-adopted in source code with no deployment constraints and without affecting source compatibility.
61+
62+
## Future directions
63+
64+
In order to further support the use case of building language selection UIs, we can consider adding convenience functions on `Locale` that allow sorting and splitting a list of available `Locale`s into `preferred` and `remaining`, which can then be used to directly populate the UI.
65+
66+
```swift
67+
public static func sorted(_ available: [Locale]) -> (preferred: [Locale], remaining: [Locale])
68+
```
69+
70+
We can also consider adding APIs that work with `Locale.Language` in addition to `Locale` since in many use cases, the developer is handling a list of languages does not need the additional functionality in `Locale`.
71+
72+
Lastly, we can choose to deprecate `Locale.preferredLanguages` since it returns the same information but using `String` which is not a good container for a language identifier and leads to incorrect usage.
73+
74+
## Alternatives considered
75+
76+
* Naming-wise, another possibility was `Locale.preferred`. However, following the current naming convention, this would be confused as returning `Locale` and not `[Locale]`. Additionally, it would be best to keep `Locale.preferred` open in case we need a way to get the first, most preferred `Locale` in the future.
77+
* Deprecate `preferredLanguages` and encourage developers to only use `preferredLocales`.

Sources/FoundationEssentials/AttributedString/AttributeContainer.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,9 @@ extension AttributeContainer {
108108

109109
@available(FoundationPreview 6.2, *)
110110
extension AttributeContainer {
111-
/// Returns an attribute container storing only the attributes in `self` with the `inheritedByAddedText` property set to `true`
111+
/// Returns a copy of the attribute container with only attributes that specify the provided inheritance behavior.
112+
/// - Parameter inheritedByAddedText: An `inheritedByAddedText` value to filter. Attributes matching this value are included in the returned container.
113+
/// - Returns: A copy of the attribute container with only attributes whose `inheritedByAddedText` property matches the provided value.
112114
public func filter(inheritedByAddedText: Bool) -> AttributeContainer {
113115
var storage = self.storage
114116
for (key, value) in storage.contents {
@@ -120,9 +122,9 @@ extension AttributeContainer {
120122
return AttributeContainer(storage)
121123
}
122124

123-
/// Returns an attribute container storing only the attributes in `self` with a matching run boundary property
124-
///
125-
/// Note: if `nil` is provided then only attributes not bound to any particular boundary will be returned
125+
/// Returns a copy of the attribute container with only attributes that have the provided run boundaries.
126+
/// - Parameter runBoundaries: The required `runBoundaries` value of the filtered attributes. If `nil` is provided, only attributes not bound to any specific boundary will be returned.
127+
/// - Returns: A copy of the attribute container with only attributes whose `runBoundaries` property matches the provided value.
126128
public func filter(runBoundaries: AttributedString.AttributeRunBoundaries?) -> AttributeContainer {
127129
var storage = self.storage
128130
for (key, value) in storage.contents {

Sources/FoundationEssentials/AttributedString/AttributeScope.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ extension AttributeScope {
206206
Self.scopeDescription.markdownAttributes
207207
}
208208

209+
/// A list of all attribute keys contained within this scope and any sub-scopes.
209210
@available(FoundationPreview 6.2, *)
210211
public static var attributeKeys: some Sequence<any AttributedStringKey.Type> {
211212
Self.scopeDescription.attributes.values

0 commit comments

Comments
 (0)