Build the example app in the Swift 6 language mode - #1915
Merged
martinpucik merged 2 commits intoAug 29, 2026
Conversation
The package declares `swiftLanguageModes: [.v6]`, but the example app project still set `SWIFT_VERSION = 5.0`. CI therefore built the example without strict concurrency checking, so it validated none of the surface that consumers migrating to Swift 6 actually meet. Raising the example to Swift 6 found a defect in the library itself. `MessagesDataSource`, `MessagesLayoutDelegate` and `MessagesDisplayDelegate` all carry `@MainActor`, but `MessageCellDelegate` and `MessageLabelDelegate` were missed, even though both exist to handle taps inside cells and the library only ever calls them from UIKit classes that are already main actor isolated. Annotate them, rather than papering over the gap in the example, which would have hidden the very problem that building the example in Swift 6 is meant to surface. The rest is the example app's own migration: - `AlertService` and `SampleData` become `@MainActor`. Both touch UIKit or hold mutable state that only view controllers read. - Conformances to `InputBarAccessoryView` protocols are marked `@preconcurrency`, matching the existing treatment of `AVAudioPlayerDelegate` in `BasicAudioController`. Those protocols carry no isolation of their own. - The `DispatchQueue.global` to `DispatchQueue.main` hops in the three example view controllers become `Task`. They passed non-Sendable arrays between queues for no reason: the mock data generator is synchronous, and the hops existed only to simulate latency, which `Task.sleep` expresses without crossing an isolation boundary. - `ChatExampleTests` becomes `@MainActor`, since it reads `SampleData`. The deployment target is left at 15.0. Lowering it to 14.0 to match the package fails: the current Xcode supports deployment targets from 15.0 upward, so the example cannot be built against the iOS 14 minimum that `Package.swift` declares.
`SWIFT_VERSION` is set once at the project level, so raising it to 6.0 also applies to `ChatExampleUITests`. XCUITest's API is main actor isolated, so every `XCUIApplication` call in the test failed to compile. Mark the test case `@MainActor`, matching `ChatExampleTests`. This target is built by the `ChatExampleUITests` scheme, which `make test_example` runs. The `ChatExample` scheme covers only the app and its unit tests, so a build of that scheme alone does not reach this code.
Contributor
Author
|
Pushed
My earlier "builds clean" claim was scoped too narrowly: I built the |
This was referenced Aug 28, 2026
Kaspik
approved these changes
Aug 29, 2026
Base automatically changed from
chore/remove-dead-files
to
docs/manual-installation
August 29, 2026 10:17
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The package declares
swiftLanguageModes: [.v6], but the example project still setSWIFT_VERSION = 5.0. CI built the example with no strict concurrency checking, so it validated none of the surface a consumer migrating to Swift 6 actually meets.It found a real library defect
Three of MessageKit's delegate protocols carry
@MainActor:@MainActorbeforeMessagesDataSourceMessagesLayoutDelegateMessagesDisplayDelegateMessageCellDelegateMessageLabelDelegateBoth missing ones exist purely to handle taps inside cells, and the library only ever calls them from UIKit classes that are already main-actor isolated (
MessageLabel,MessagesCollectionView,MessageContentCell,TextMessageCell). Every consumer building in Swift 6 had to work around this with@preconcurrency.I annotated the protocols rather than papering over it in the example — a
@preconcurrencythere would have hidden the exact problem that building the example in Swift 6 is meant to surface.The example app's own migration
AlertServiceandSampleDatabecome@MainActor. One presents alerts, the other holdslazy varstate that only view controllers read.@preconcurrency, matching the existing treatment ofAVAudioPlayerDelegateinBasicAudioController. Those protocols carry no isolation of their own, so this is the right tool there — unlike MessageKit's own protocols, which I fixed at the source.DispatchQueue.global→DispatchQueue.mainhops becomeTaskin the three example view controllers. They were passing non-Sendable arrays between queues for no benefit: the mock data generator is synchronous, and the hops existed only to fake latency, whichTask.sleepexpresses without crossing an isolation boundary.ChatExampleTestsbecomes@MainActor, since it readsSampleData.The deployment target is deliberately left at 15.0
I tried lowering it to 14.0 to match
Package.swift. It does not build:Dependencies are not the blocker — Kingfisher 8.11.0 supports iOS 13 and InputBarAccessoryView 7.0.6 supports iOS 14. The current Xcode simply will not build an app below iOS 15.
This raises a question worth deciding separately:
Package.swiftadvertisesplatforms: [.iOS(.v14)], but no app on a current toolchain can target iOS 14, so that claim cannot be exercised by the example or by CI. Whether to raise the package minimum to iOS 15 is a breaking change and your call — I have not touched it.Verification
Builds clean for testing, zero errors. The only warnings are two pre-existing XCTest linkage notes about iOS 15 vs 17, unrelated to this change and present before it.
make lintpasses.One cosmetic note
SwiftFormat's
markTypesrule regenerates// MARK:headers from the conformance list, so@preconcurrencynow leaks into four section comments, e.g.// MARK: @preconcurrency AttachmentManagerDelegate. That is the formatter's canonical output — hand-editing failsmake lint. Suppressing it means changing the SwiftFormat config, which I left alone.