You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
CellKit is a UITableView/UICollectionView data source layer — every one of its protocols is only ever exercised on the main thread. But the protocols are declared nonisolated, so under Swift 6 every consumer conformance reports:
warning: conformance of 'FooCellModel' to protocol 'CellConvertible' crosses into
main actor-isolated code and can cause data races; this is an error in the Swift 6 language mode
In an app I migrated to Swift 6 this accounted for 61 of 498 warnings — the second largest group. The only consumer-side workaround is to annotate every single conformance:
DifferentiableCellModelWrapper needed a small rework: DifferenceKit's Differentiable/Equatable requirements are nonisolated, so differenceIdentifier is now computed once in a @MainActor init and stored, and == uses MainActor.assumeIsolated. Wrappers are only ever created and diffed by DifferentiableCellModelDataSource, which is now main actor-isolated, so the assumption holds.
MainActor.assumeIsolated requires iOS 13, so Package.swift platforms go from .v9 to .v13. DiffableCellKit already required iOS 13 throughout, and current SPM toolchains no longer accept iOS 9 anyway.
This is source-breaking
Any conformance that is not main actor-isolated will stop compiling. In practice these types are all cells, cell models and data sources, so this should be a no-op for real consumers — but it is a breaking change and probably warrants a minor version bump.
Verification
xcodebuild build passes for both the CellKit and DiffableCellKit schemes on generic/platform=iOS. SwiftLint reports the same 9 pre-existing violations as main — none added.
Building CellKit with SWIFT_STRICT_CONCURRENCY=complete succeeds; two warnings remain in SupplementaryElementKind, which reads UICollectionView.elementKindSectionHeader/Footer from a nonisolated RawRepresentable conformance. I left it alone because marking the enum @MainActor would break RawRepresentable, and the values are immutable strings. Happy to address it separately if you'd prefer.
I could not run the app-side end-to-end check (the consumer pins the released tag), so I have not empirically confirmed that all 45 @MainActor annotations become removable — though that is what the change is for.
Thanks for this, @radimvaculik — the analysis in the description (the 61 conformance warnings, the 45 hand-written @MainActor conformances) is exactly what convinced us to fix this at the library level. We went a different route in #55, which is now the PR we intend to land:
swift-tools-version:6.2 with .defaultIsolation(MainActor.self) + Swift 6 language mode on both targets, instead of annotating each declaration. It needs zero @MainActor in Sources/, builds with zero warnings in Swift 6 mode, and a plain Swift 6 consumer like yours needs no annotations on its conformances.
Platforms move to iOS 15 / tvOS 15 and CocoaPods support is dropped (the podspec couldn't have expressed either approach without duplicating it).
For the record, so the information isn't lost — three things we hit while reviewing this branch in the Swift 6 language mode (SWIFT_VERSION=6):
DifferentiableCellModel.swift — the MainActor.assumeIsolated { lhs… rhs… } in == fails with sending 'lhs' risks causing data races (non-Sendable wrappers captured into a @MainActor closure).
DifferentiableCellModelSection.swift:29 — cells.map(DifferentiableCellModelWrapper.init) now passes a @MainActor init as a bare function value: converting … loses global actor 'MainActor'.
DifferentiableCellModelDataSource.swift — the nested Container enum doesn't inherit the class's @MainActor, so Container.reload calls UIKit from a nonisolated context.
Plus CellKit.podspec still says ios.deployment_target 9.0, which can't compile MainActor at all.
With defaultIsolation none of the DifferentiableCellModelWrapper rework is needed — SE-0470 isolated conformances let DifferenceKit's nonisolated generic algorithm accept the main actor-isolated wrapper as long as it's called from the main actor, which it always is.
Leaving this open for you; feel free to close it once #55 is merged, or point out anything you think we missed.
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
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.
Motivation
CellKit is a UITableView/UICollectionView data source layer — every one of its protocols is only ever exercised on the main thread. But the protocols are declared nonisolated, so under Swift 6 every consumer conformance reports:
In an app I migrated to Swift 6 this accounted for 61 of 498 warnings — the second largest group. The only consumer-side workaround is to annotate every single conformance:
which had to be repeated 45 times, plus a
@preconcurrency import CellKitwhere a cell model is built off the main thread.Change
@MainActoron the API that touches UIKit:ReusableView,CellConfigurable,CellModel,SupplementaryViewModel,CellConvertible,CellModelSelectable,CellModelDataSourceDelegate,DataSource,CollectionSupplementaryViewModel,AbstractDataSource,CellModelDataSource,LazyCellProvider,LazySupplementaryViewProviderDifferentiableCellModel,DifferentiableCellModelDataSourceDifferentiableCellModelWrapperneeded a small rework: DifferenceKit'sDifferentiable/Equatablerequirements are nonisolated, sodifferenceIdentifieris now computed once in a@MainActor initand stored, and==usesMainActor.assumeIsolated. Wrappers are only ever created and diffed byDifferentiableCellModelDataSource, which is now main actor-isolated, so the assumption holds.MainActor.assumeIsolatedrequires iOS 13, soPackage.swiftplatforms go from.v9to.v13.DiffableCellKitalready required iOS 13 throughout, and current SPM toolchains no longer accept iOS 9 anyway.This is source-breaking
Any conformance that is not main actor-isolated will stop compiling. In practice these types are all cells, cell models and data sources, so this should be a no-op for real consumers — but it is a breaking change and probably warrants a minor version bump.
Verification
xcodebuild buildpasses for both theCellKitandDiffableCellKitschemes ongeneric/platform=iOS. SwiftLint reports the same 9 pre-existing violations asmain— none added.Building
CellKitwithSWIFT_STRICT_CONCURRENCY=completesucceeds; two warnings remain inSupplementaryElementKind, which readsUICollectionView.elementKindSectionHeader/Footerfrom a nonisolatedRawRepresentableconformance. I left it alone because marking the enum@MainActorwould breakRawRepresentable, and the values are immutable strings. Happy to address it separately if you'd prefer.I could not run the app-side end-to-end check (the consumer pins the released tag), so I have not empirically confirmed that all 45
@MainActorannotations become removable — though that is what the change is for.