Swift в 2025 году: что нового на практике?
2025 год стал знаковым для экосистемы Apple. Вышел Swift 6 — релиз, который обещает не только стабильность, но и фундаментальные изменения в том, как мы пишем код. Если вы разработчик под iOS или macOS, вам необходимо разобраться в нововведениях, чтобы не отставать от индустрии. В этой статье мы разберём ключевые изменения Swift 6 на практических примерах, которые можно сразу применить в проектах.
1. Неисключающие перечисления (Non‑exhaustive enums)
Одно из самых ожидаемых изменений — возможность объявлять перечисления, которые могут расширяться в будущем без нарушения обратной совместимости. Раньше, если библиотека добавляла новый кейс в enum, ваш код ломался. Теперь это в прошлом.
Объявление такого перечисления выглядит так:
public enum NetworkError: Sendable {
case timeout
case serverError(Int)
case unknown
@_nonExhaustive case custom(String)
}
При обработке такого enum компилятор больше не требует, чтобы switch был исчерпывающим. Вы обязаны добавить default:
func handleError(_ error: NetworkError) {
switch error {
case .timeout:
print("Тайм-аут")
case .serverError(let code):
print("Ошибка сервера: \(code)")
default:
print("Неизвестная ошибка")
}
}
Практический совет: Если вы разрабатываете публичную библиотеку или SDK, помечайте свои enum атрибутом @_nonExhaustive — это сохранит совместимость с будущими версиями вашего кода.
2. Макросы: новый уровень метапрограммирования
Макросы, введённые в Swift 5.9, в 2025 году стали по-настоящему мощными. В Swift 6 появились макросы-атрибуты и макросы-члены, которые позволяют генерировать код на этапе компиляции без лишнего бойлерплейта.
Рассмотрим пример: вместо ручного написания init и Codable для модели, вы можете использовать встроенный макрос @MemberwiseInit (новинка Swift 6):
@MemberwiseInit
struct UserProfile: Codable {
let id: Int
var name: String
var email: String
var age: Int
}
// Компилятор автоматически генерирует:
// init(id: Int, name: String, email: String, age: Int)
// и реализацию Codable
Ещё один пример — макрос #Predicate для SwiftData:
let youngUsers = try modelContext.fetch(
FetchDescriptor<UserProfile>(
predicate: #Predicate { $0.age < 25 }
)
)
Макросы значительно сокращают количество шаблонного кода и уменьшают вероятность ошибок. В 2025 году их использование стало стандартом для новых проектов.
3. Наблюдаемые свойства (Observable) и SwiftUI
В Swift 6 появился новый тип Observable, который пришёл на смену ObservableObject из Combine. Теперь не нужно писать @Published и @ObservedObject — всё работает через макрос @Observable.
Пример модели:
import Observation
@Observable
class CounterViewModel {
var count = 0
var isPremium = false
func increment() {
count += 1
if count >= 10 {
isPremium = true
}
}
}
Использование в SwiftUI стало проще:
struct CounterView: View {
@State private var viewModel = CounterViewModel()
var body: some View {
VStack {
Text("Счёт: \(viewModel.count)")
Button("Увеличить") {
viewModel.increment()
}
if viewModel.isPremium {
Text("Премиум доступен!")
}
}
.padding()
}
}
Что изменилось?
- Не нужно явно подписываться на изменения — SwiftUI сам отслеживает доступ к свойствам.
- Улучшена производительность: обновляется только тот View, который читает изменившееся свойство.
- Меньше кода: исчезли
@Published,@ObservedObjectи@StateObject(в новом синтаксисе).
4. Улучшенная параллельность и владение
Swift 6 вводит строгую проверку изоляции данных на уровне компилятора. Теперь Sendable проверяется жёстче, а неявное shared mutable state запрещено. Это делает многопоточный код безопасным без необходимости вручную расставлять блокировки.
Пример с async/await и акторами:
actor DataLoader {
private var cache = [String: Data]()
func loadData(from url: String) async throws -> Data {
if let cached = cache[url] {
return cached
}
let data = try await downloadData(from: url)
cache[url] = data
return data
}
private func downloadData(from url: String) async throws -> Data {
let (data, _) = try await URLSession.shared.data(from: URL(string: url)!)
return data
}
}
// Использование
Task {
let loader = DataLoader()
let data = try await loader.loadData(from: "https://example.com/data.json")
print("Данные получены: \(data.count) байт")
}
Ключевые нововведения:
- Строгая Sendable-проверка: компилятор не даст передать не-Sendable тип между изолированными контекстами.
- Улучшенные акторы: поддержка наследования и глобальных акторов.
- Noncopyable типы: теперь можно создавать типы, которые нельзя копировать (полезно для управления ресурсами, например, файловыми дескрипторами).
Пример noncopyable типа:
struct FileDescriptor: ~Copyable {
private let fd: Int32
init(descriptor: Int32) {
self.fd = descriptor
}
consuming func close() {
Darwin.close(fd)
}
deinit {
Darwin.close(fd)
}
}
Такие типы гарантируют, что ресурс будет освобождён ровно один раз, предотвращая утечки и двойное закрытие.
Заключение
Swift 6 — это не просто очередное обновление, а ша