Демонстрация SwiftData: сохранение и управление данными в iOS 0:05
В этой лекции курса Stanford CS193p преподаватель подробно разбирает процесс интеграции SwiftData в существующее приложение CodeBreaker. Основной упор сделан на переход от хранения данных в оперативной памяти (с использованием структуры struct и семантики значений) к постоянному хранилищу на основе SQL, а также на эффективное управление запросами, сортировкой и поиском.
🛠 Перевод модели на SwiftData 1:00
Для реализации полноценного хранилища модель приложения была существенно модифицирована:
- @Model вместо struct: Классы
CodeиCodeBreakerтеперь помечены атрибутом@Model, что превращает их в сущности базы данных. - Примитивные типы: Перечисления (enum), такие как
Kind, были заменены наString, так как только примитивные типы могут храниться в базе напрямую. Для удобства работы добавлены вычисляемые свойства для конвертации обратно в нужные типы. - UI-независимость: Модель избавлена от импорта SwiftUI и типов
Color. Теперь конвертация цветов в hex-строки происходит на уровне UI (в представлениях или через расширения), что делает ядро приложения независимым от графического интерфейса. - @Relationships и @Transient: Установлены связи между
CodeBreakerиCodeс удалением каскадом (при удалении игры удаляются все её коды). СвойствоstartTimeпомечено как@Transient, так как его хранение в БД не имеет смысла; для корректного обновления UI при измененииstartTimeпреподаватель использует «хак» с принудительным обновлениемelapsedTime.
🖼 Изоляция Preview от БД 9:07
Стандартные инструменты Xcode для предварительного просмотра (#Preview) не имеют доступа к контейнеру базы данных, что приводило к сбоям. Преподаватель представил решение через систему Preview Trait:
- Создается
PreviewModifierпод названиемSwiftDataPreview. - Модификатор использует
ModelContainerс конфигурациейisStoredInMemoryOnly: true, что идеально подходит для тестов, так как не засоряет диск. - Для удобства использования создано расширение
PreviewTrait, позволяющее вызывать контейнер одной строкой:#Preview(traits: .swiftData).
🔍 Запросы, фильтрация и ModelContext 21:36
Переход на БД изменил источник правды: вместо локального массива [CodeBreaker] теперь используется @Query.
- @Query: Этот атрибут позволяет автоматически получать данные из БД. Он поддерживает фильтрацию через
#Predicateи сортировку с помощьюSortDescriptor. Запросы являются «живыми» и обновляются при любых изменениях в базе. - ModelContext: Это «хаб» для всех операций с данными. Он используется для вызова методов
insert(),delete()иsave(). - Оптимизация: Хотя
@Queryзакрывает 90% задач, для специальных случаев (например, проверки на пустоту БД) можно использоватьmodelContext.fetchCount(), который эффективнее обычногоfetch(), так как возвращает только число, не загружая объекты в память.
📅 Сортировка и управление состоянием 45:44
Для решения проблемы случайного порядка записей в базе данных была добавлена отметка времени timestamp для каждого Code.
- Сортировка: В
CodeBreakerдобавлен вычисляемый массивattemptsс методом.sorted(), который обеспечивает постоянный порядок отображения в UI вне зависимости от порядка хранения в БД. - Динамическая настройка запросов: Поскольку параметры
@Query(сортировка, фильтры) нельзя менять «на лету» в теле View, преподаватель демонстрирует технику инициализации запроса прямо вinit()представленияGameList. Это позволяет гибко менять логику отображения, например, при переключении пикера «Имя / Недавнее». - Поиск: Использование модификатора
.searchable(text: $searchString)в сочетании с динамическим обновлением предиката вinit()позволяет реализовать полнотекстовый поиск по именам игр.