Хранение данных со SwiftData

Чтобы данные пережили перезапуск приложения, их нужно сохранять. SwiftData — современный фреймворк хранения от Apple (iOS 17+), пришедший на смену Core Data.
Суть урока: пометьте класс макросом @Model — и он станет сохраняемой моделью. @Query автоматически подтягивает данные из хранилища во вью и обновляет список при любых изменениях.

SwiftData, представленная в iOS 17, делает персистентность почти бесшовной. Модель — это обычный класс с макросом @Model; макрос превращает его в сущность, которую можно сохранять в базу:

import SwiftData

@Model
class TodoItem {
    var title: String
    var isDone: Bool
    var createdAt: Date

    init(title: String) {
        self.title = title
        self.isDone = false
        self.createdAt = .now
    }
}

Чтобы показать сохранённые данные во вью, используют макрос @Query — он загружает объекты и автоматически обновляет интерфейс при изменениях в хранилище. Доступ к операциям записи даёт ModelContext из окружения:

struct TodoListView: View {
    @Query(sort: \.createdAt) private var items: [TodoItem]
    @Environment(\.modelContext) private var context

    var body: some View {
        List {
            ForEach(items) { item in
                Text(item.title)
            }
            .onDelete { offsets in
                for i in offsets { context.delete(items[i]) }
            }
        }
        .toolbar {
            Button("Добавить") {
                context.insert(TodoItem(title: "Новая задача"))
            }
        }
    }
}

Заметьте: вы не пишете SQL и не управляете сохранением вручную. Вставили объект через context.insert — и @Query сам обновит список. SwiftData спроектирована заодно со SwiftUI: изменения в данных мгновенно отражаются на экране. Контейнер хранилища подключается на старте приложения модификатором .modelContainer(for: TodoItem.self).

@Model class TodoItem      (описание сущности)
        |
   ModelContext            (вставка / удаление / сохранение)
        |
   Хранилище на диске      (под капотом — Core Data)
        |
   @Query private var items (живой запрос)
        |
   List / ForEach          (UI обновляется сам)

Попробуй сам ▶ — запусти код прямо в браузере (Pyodide). Здесь нет Swift, но логика та же, что под капотом мобильного кода:

# Имитируем @Query + ModelContext: живой список, реагирующий на изменения.
store = []     # "хранилище на диске"

def insert(title):
    store.append({'title': title, 'done': False})

def delete(index):
    if 0 <= index < len(store):
        store.pop(index)

def query_sorted():                 # как @Query(sort:)
    return sorted(store, key=lambda x: x['title'])

insert('Купить молоко')
insert('Учить SwiftData')
insert('Опубликовать приложение')
print('Список:', [t['title'] for t in query_sorted()])
delete(0)
print('После удаления:', [t['title'] for t in query_sorted()])

Как работает под капотом

Макрос @Model на этапе компиляции добавляет классу инфраструктуру персистентности — фактически SwiftData построена поверх проверенной временем Core Data. ModelContext — это рабочая область: он отслеживает изменения объектов и по команде save (часто автоматической) записывает их в хранилище. @Query — это живой запрос: он подписан на контекст и при любом изменении пересчитывается, заставляя SwiftUI перерисовать список. Так данные, хранилище и интерфейс остаются согласованными.

Частые ошибки

  • Забыть подключить .modelContainer. Без контейнера @Query и context не будут работать.
  • Хранить @Model-объекты как обычные структуры. Это классы со своей идентичностью и жизненным циклом.
  • Целиться в iOS 16 и ниже. SwiftData требует iOS 17+; для более старых версий нужна Core Data.

Best practices

  • Используйте @Query для отображения и ModelContext для вставки/удаления.
  • Подключайте .modelContainer один раз на уровне приложения.
  • Задавайте сортировку и фильтры прямо в @Query, а не в коде вью.

Итоги. SwiftData — современный, тесно интегрированный со SwiftUI способ хранить данные. @Model описывает сущность, ModelContext управляет записью, а @Query живо отражает хранилище в интерфейсе. Этим уроком мы замыкаем путь от языка до готового приложения с данными, навигацией и сетью.

Шире контекста

SwiftData завершает картину современного стека Apple: язык Swift, интерфейс SwiftUI, конкурентность async/await и теперь персистентность, спроектированная заодно со всем остальным. Под капотом она опирается на проверенную годами Core Data, но прячет её многословность за чистыми макросами @Model и @Query. Эта тесная интеграция означает, что данные, хранилище и интерфейс остаются в согласии без ручной синхронизации: вставили объект — список обновился сам. По мере роста приложения вы столкнётесь с более сложными темами — связями между сущностями, миграциями схемы при обновлении модели, фильтрацией и сортировкой через предикаты, синхронизацией через iCloud. Но фундамент останется тем же простым и декларативным. Важная практическая оговорка: SwiftData требует iOS 17 и новее, поэтому для приложений, поддерживающих более старые версии, по-прежнему актуальна Core Data, и часто их используют бок о бок в переходный период. Завершив этот урок, вы прошли полный путь — от первой переменной до приложения, которое загружает, отображает и сохраняет данные.

Проверьте себя
1. Что делает макрос @Model в SwiftData?
AСоздаёт сетевой запрос
BПревращает класс в сохраняемую (персистентную) сущность
CЗапускает асинхронную задачу
DОписывает внешний вид вью
2. Что обеспечивает @Query во вью?
AОднократную загрузку без обновлений
BЖивой запрос: данные из хранилища, автоматически обновляющие интерфейс при изменениях
CСетевое соединение
DНавигацию между экранами