Как организовать локальный кэш в MVVM
Что проверяют
Понимаете ли вы, в каком слое живёт кэш: не во ViewModel, а за репозиторием, который ViewModel видит через протокол. Следом обычно спрашивают про инвалидацию: когда данные в кэше устаревают и как интерфейс узнаёт о свежих.
Как отвечать
Опишите слои. View подписан на состояние ViewModel. ViewModel зовёт ItemsRepository через протокол и не знает, откуда пришли данные. Репозиторий решает, что отдать: локальную копию, ответ сети или оба по очереди.
Выбор хранилища:
NSCache: кэш в памяти, система сама чистит его при нехватке памяти. Годится для картинок и недавних ответов, между запусками не живёт;URLCache: HTTP-кэшURLSession, учитываетCache-ControlиETag, если сервер отдаёт правильные заголовки;- файлы с
Codable: JSON в каталогеCaches, просто для небольших наборов. Система может очистить этот каталог при нехватке места; - Core Data, SwiftData (с iOS 17) или SQLite через GRDB: когда нужны выборки, связи и частичные обновления;
UserDefaults: только настройки и флаги, не данные.
Стратегия чаще всего stale-while-revalidate: репозиторий сразу отдаёт кэш, параллельно идёт в сеть и присылает обновление. На Swift Concurrency это удобно сделать через AsyncStream, на Combine через паблишер. ViewModel держит состояние вроде enum State { case loading, loaded([Item], isStale: Bool), failed(Error) }. Устаревание определяют по TTL или через ETag, pull-to-refresh делает принудительный запрос, при выходе из аккаунта кэш очищается. Чтобы чтение и запись не гонялись, хранилище удобно сделать actor.
Где спотыкаются
Кладут логику кэша во ViewModel. Она разрастается, а два экрана с одними и теми же данными начинают кэшировать их по-разному.
Хранят списки объектов в UserDefaults. Этот plist целиком загружается в память, для больших данных он не предназначен.
Отдают кэш и больше не обновляют его. Пользователь неделями видит старые данные, потому что не продумано, когда и как кэш инвалидируется.