94. Чиним ошибки после перехода на Swift 6
Oleg991Поднял версию Swift до 6.0 в проекте с кодом для статей и в этой статье расскажу про исправление появившихся ошибок.
Какие ошибки вылезли
Ниже перечислил все виды ошибок, которые появились при попытке собрать проект.
Кстати, чтобы сборка не прерывалась при возникновении ошибок, нужно зайти в настройки Xcode и на вкладке "General" поставить галку "Continue building after Errors".
Все ошибки можно посмотреть в коммите с SHA e3e383aed5c43037063e1b1ec0e9adb5d0d5f6ef:
- Capture of 'bindingOptional' with non-sendable type 'Binding<Wrapped?>' in a @Sendable' closure
- Static property 'defaultValue' is not concurrency-safe because it is nonisolated global shared mutable state
- Main actor-isolated class property 'shared' can not be referenced from a nonisolated context
- Main actor-isolated property 'connectedScenes' can not be referenced from a nonisolated context
- Cannot form key path to main actor-isolated property 'windows'
- Cannot form key path to main actor-isolated property 'isKeyWindow'
- Main actor-isolated property 'overrideUserInterfaceStyle' can not be mutated from a nonisolated context
- Main actor-isolated property 'safeAreaInsets' can not be referenced from a nonisolated context
- Call can throw, but it is not marked with 'try' and the error is not handled
- Main actor-isolated operator function '==' cannot be used to satisfy nonisolated protocol requirement
- Passing closure as a 'sending' parameter risks causing data races between code in the current task and concurrent execution of the closure
- Sending 'self.vm' risks causing data races
Исправление ошибок
Поскольку некоторые ошибки дублировались, а исправление некоторых приводило к исправлению других, было сложно как-то сгруппировать эти кейсы, поэтому делю все случаи просто на абзацы и для наглядности привожу примеры кода.
init<Wrapped: Sendable>(bindingOptional: Binding<Wrapped?>) - так исправляем первую ошибку: Capture of 'bindingOptional' with non-sendable type 'Binding<Wrapped?>' in a @Sendable' closure.
Все кастомные PreferenceKey содержали статичное свойство var, которое нужно было поменять на let, чтобы ушла ошибка: Static property 'defaultValue' is not concurrency-safe because it is nonisolated global shared mutable state.
@MainActor static func set(_ newValue: Theme) - добавили @MainActor, чтобы поправить ошибки внутри enum ColorThemeService - это было очень просто.
Main actor-isolated property 'safeAreaInsets' can not be referenced from a nonisolated context - эту ошибку починить было чуть сложнее, потому что у меня был EnvironmentKey, содержащий в дефолтном значении данные безопасной зоны, полученные из UIApplication.shared, что уже не прокатывает в Swift 6. В качестве решения я сделал такую замену (теперь нужно сохранять в стейт-свойство отступы безопасной зоны):
struct SafeAreaInsetsKey: PreferenceKey {
static let defaultValue: EdgeInsets = .init()
static func reduce(value: inout EdgeInsets, nextValue: () -> EdgeInsets) {}
}
extension View {
/// Возвращает безопасную зону в замыкании
func readSafeAreaInsets(onChange: @escaping (EdgeInsets) -> Void) -> some View {
background(
GeometryReader { geometry in
Color.clear
.preference(key: SafeAreaInsetsKey.self, value: geometry.safeAreaInsets)
}
)
.onPreferenceChange(SafeAreaInsetsKey.self, perform: onChange)
}
}
Неожиданная ошибка Call can throw, but it is not marked with 'try' and the error is not handled решается очень просто:
// ошибка
demoItems.forEach(stackView.addArrangedSubview)
// все ок
demoItems.forEach {
stackView.addArrangedSubview($0)
}
Ошибки реализации протокола Equatable решаются словом nonisolated:
// ошибка static func == (lhs: BrokenView1, rhs: BrokenView1) -> Bool // все ок nonisolated static func == (lhs: BrokenView1, rhs: BrokenView1) -> Bool
Две последние ошибки я поправил обходным решением, которое в проде применять нужно только со знанием дела, речь про @unchecked Sendable. Настоятельно рекомендую прочитать официальную статью, потому что она небольшая и может пригодиться, если вы еще не разбирались в теме. Вкратце: @unchecked Sendable выключает для соответствующего типа проверки Swift Concurrency, но потенциальные ошибки никуда не деваются - нужно либо делать свою кастомную синхронизацию, либо не использовать это решение в проде.
Заключение
Мой проект небольшой, но повозиться с ошибками все же пришлось прилично. Зато теперь вам может быть проще справиться с подобными ошибками 😉
Другие мои статьи по разработке можно посмотреть тут, а про инвестиции - тут.