Stanford CS193p: Как превратить модель SwiftUI в персистентную базу данных

Stanford Online 4,1 тыс. 1 ч 10 мин 5 мин 18.12.2025
Главное

В тринадцатой лекции курса Stanford CS193p (весна 2025 года) профессор Пол Хэгарти приступает к одной из важнейших тем современной iOS-разработки — сохранению данных с помощью фреймворка SwiftData. Основная цель занятия — научить студентов делать данные приложения персистентными, чтобы информация не исчезала после закрытия программы или перезагрузки устройства . SwiftData представляет собой мощную надстройку над базой данных SQL, которая позволяет разработчикам работать на уровне привычных конструкций Swift, не погружаясь в сложности запросов к БД .

🛠️ Основы SwiftData: от классов к таблицам SQL 1:15

Переход к персистентной модели начинается с использования макроса @Model. По словам Хэгарти, этот макрос можно применять только к классам, поскольку именно они будут представлять собой таблицы в базе данных . Важной особенностью является то, что @Model автоматически делает класс наблюдаемым (Observable), поэтому использовать оба макроса одновременно не имеет смысла .

Поскольку данные сохраняются в SQL, на типы переменных внутри @Model накладываются определенные ограничения:

Хэгарти подчеркивает, что если тип данных не вписывается в эти рамки, разработчик может использовать вычисляемые свойства для преобразования данных в примитивные типы «на лету» .

🔗 Управление связями и атрибутами схемы 7:38

Структура базы данных описывается понятием «схема». Для настройки поведения полей используются атрибуты схемы. Например, атрибут .unique позволяет гарантировать уникальность значения (как ID сотрудника), а .externalStorage полезен для хранения тяжелых объектов, таких как видео или крупные изображения, вне основного файла базы данных .

Особое внимание Хэгарти уделяет управлению связями через @Relationship:

  1. Правила удаления (deleteRule): профессор рекомендует использовать .cascade, чтобы при удалении основного объекта (например, игры) автоматически удалялись связанные с ним данные (попытки и коды) .
  2. Инверсия (inverse): позволяет SwiftData понимать двусторонние связи, когда дочерний объект имеет ссылку на родительский .

Для данных, которые нужны только во время работы приложения и не должны сохраняться на диск, предусмотрены маркеры .ephemeral или @Transient . Однако, по наблюдению Хэгарти, изменения @Transient переменных могут не обновлять пользовательский интерфейс автоматически, что требует ручной инициализации обновлений .

📑 Модель контекста и контейнера 10:01

Для взаимодействия с базой данных используются два ключевых компонента:

Хэгарти отмечает, что операции со SwiftData являются синхронными, но если приложение настроено на работу с iCloud, синхронизация с облаком происходит асинхронно в фоновом режиме, избавляя разработчика от необходимости писать сложный многопоточный код .

🔍 Искусство выборки данных: Predicates и @Query 14:45

Извлечение данных происходит с помощью FetchDescriptor, который определяет три вещи: тип искомой модели, условия поиска и порядок сортировки . Для задания условий используется макрос #Predicate. Это мощный инструмент, который превращает обычное замыкание Swift в сложный SQL-запрос .

Внутри предиктов можно использовать:

Несмотря на гибкость, предикты имеют ограничения: в них нельзя использовать вычисляемые свойства, так как база данных «видит» только реально хранящиеся поля .

В SwiftUI-представлениях профессор рекомендует использовать макрос @Query вместо ручного вызова fetch(). Это позволяет создать массив, который автоматически обновляется при любых изменениях в базе данных, работая аналогично @State или @Binding .

⚠️ Обработка ошибок в Swift 21:52

Работа с базой данных — это первый случай в курсе, когда функции могут возвращать ошибки (например, из-за повреждения файлов). Хэгарти объясняет стандартную систему Swift do-catch:

👨‍💻 Демо: адаптация модели CodeBreaker под SwiftData 31:30

Практическая часть занятия посвящена конвертации существующей игры CodeBreaker. Основная сложность заключается в том, что типы данных в модели не всегда совместимы с SQL. Например, тип Color не является примитивным и не может быть сохранен напрямую .

Хэгарти демонстрирует «массаж данных» (data massaging):

  1. Превращение структур в классы: Code становится @Model class, что требует добавления инициализатора, так как классы не получают его автоматически, в отличие от структур .
  2. Проблема ссылок: Хэгарти указывает на опасный баг — при переходе от структур к классам простое присваивание перестает копировать данные, создавая лишь вторую ссылку на тот же объект в куче . Это требует явного создания копий объектов.
  3. Использование ИИ: Профессор обращается к ChatGPT, чтобы быстро сгенерировать код для перечисления Kind, которое содержит ассоциативные данные и не может быть автоматически преобразовано в строку . Хэгарти выбирает хранение этого перечисления в виде String в базе данных, чтобы сохранить возможность поиска по типу .
  4. UI-независимость: Хэгарти делает модель полностью независимой от SwiftUI, заменяя Color на String (Hex-коды). Чтобы не ломать UI-код, он добавляет расширения с вычисляемыми свойствами, которые прозрачно конвертируют строки обратно в цвета для представлений .

В завершение лекции в главный файл приложения добавляется .modelContainer(for: CodeBreaker.self) . Профессор демонстрирует, что после всех манипуляций игра продолжает работать, а данные теперь готовы к полноценному сохранению, которое будет реализовано на следующем занятии .

💬 Цитаты

«SwiftData — это механизм, который мы используем, чтобы данные оставались на месте после выключения телефона.»

Пол Хэгарти 00:05

«Мы взаимодействуем со SwiftData на уровне Swift, используя обычные структуры и замыкания, не зная, что происходит в SQL под капотом.»

Пол Хэгарти 00:20

«Нельзя поместить Color в базу данных. Это не примитивный тип.»

Пол Хэгарти 55:56
👥 Спикер
🔗 Упомянутые сайты и проекты
📖 Термины
@Model
Макрос в SwiftData, который помечает класс как персистентную сущность, хранимую в базе данных.
modelContext
Объект-инструмент для выполнения операций добавления, удаления и поиска данных в SwiftData.
#Predicate
Макрос для создания логических условий выборки данных, которые SwiftData транслирует в SQL-запросы.
@Query
Свойство-обертка в SwiftUI для автоматического извлечения и отслеживания данных из SwiftData.
Schema
Описание структуры данных в базе, включая таблицы, поля и связи между ними.
📊 Цифры
🗓 Хронология
  1. 2025 Чтение лекции L13 в рамках курса Stanford CS193p.
⚖️ Другая сторона
Технологии и IT SwiftData iOS Development SwiftUI Stanford CS193p Persistence