Как связаны NetworkManager и StorageManager?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Связь между NetworkManager и StorageManager в iOS--приложении
В архитектуре iOS--приложения NetworkManager (менеджер сети) и StorageManager (менеджер хранилища) тесно связаны, образуя критически важную пару для работы с данными. Их взаимодействие реализует паттерн «загрузка — кэширование — предоставление», обеспечивая баланс между актуальностью данных, скоростью работы и автономной функциональностью.
Ключевые аспекты взаимодействия
1. Кэширование сетевых данных
Основная связь — StorageManager выступает в роли персистентного кэша для данных, загруженных через NetworkManager. Это позволяет:
- Ускорить повторное отображение данных (UI загружается мгновенно из кэша)
- Обедоспечить работу приложения без сети (офлайн-режим)
- Снизить нагрузку на сервер и трафик пользователя
class DataService {
let networkManager: NetworkManager
let storageManager: StorageManager
func fetchUserProfile(userId: String) async throws -> UserProfile {
// 1. Проверяем кэш (StorageManager)
if let cachedProfile = storageManager.getUserProfile(id: userId) {
return cachedProfile
}
// 2. Если нет в кэше — загружаем из сети (NetworkManager)
let networkProfile = try await networkManager.requestUserProfile(id: userId)
// 3. Сохраняем загруженные данные в кэш (StorageManager)
storageManager.saveUserProfile(networkProfile)
return networkProfile
}
}
2. Стратегии синхронизации
Взаимодействие часто строится по стратегиям обновления данных:
- Cache-then-network: Сначала показываем кэшированные данные, затем обновляем из сети.
- Network-then-cache: Пытаемся загрузить свежие данные; при неудаче используем кэш.
- Периодическая синхронизация: Фоновая загрузка новых данных и обновление хранилища.
3. Управление состоянием данных
StorageManager помогает NetworkManager эффективно работать с данными:
- Хранение токенов аутентификации: Сетевые запросы требуют токенов, которые обычно хранятся в Keychain (через StorageManager).
- Локальные идентификаторы: Для создания объектов перед отправкой на сервер (оптимистичное создание).
- Дифференциальные обновления: Зная локальное состояние, можно запросить только изменения с сервера.
Архитектурные паттерны для организации связи
Координация через сервисный слой
Наиболее чистый подход — создать отдельный DataService или Repository, который инкапсулирует логику взаимодействия:
protocol UserRepositoryProtocol {
func getCurrentUser() async throws -> User
}
class UserRepository: UserRepositoryProtocol {
private let networkManager: NetworkManagerProtocol
private let storageManager: StorageManagerProtocol
init(network: NetworkManagerProtocol, storage: StorageManagerProtocol) {
self.networkManager = network
self.storageManager = storage
}
func getCurrentUser() async throws -> User {
// Комбинированная логика работы с сетью и хранилищем
}
}
Использование реактивных подходов
При использовании Combine или RxSwift связь становится декларативной:
// Пример на Combine
func fetchData() -> AnyPublisher<Data, Error> {
// Пробуем загрузить из кэша
return storageManager.fetchCachedData()
.catch { _ in
// Если в кэше нет — загружаем из сети и сохраняем
self.networkManager.fetchRemoteData()
.flatMap { remoteData in
self.storageManager.save(data: remoteData)
.map { _ in remoteData }
}
}
.eraseToAnyPublisher()
}
Проблемы и решения при интеграции
- Консистентность данных: При одновременном доступе из нескольких потоков нужна правильная синхронизация. Решение — использование очередей (DispatchQueue) или акторов (Swift Actor).
- Обработка ошибок: Нужно четко различать ошибки сети и ошибки хранилища, предоставляя пользователю понятные сообщения.
- Инвалидация кэша: Реализация механизма устаревания кэша (TTL — time to live) или принудительного обновления при определенных действиях пользователя.
- Миграция данных: При изменении структуры API или модели данных требуется синхронное обновление формата в хранилище.
Практический пример: Лента новостей
Для ленты новостей типичная реализация выглядит так:
- StorageManager проверяет наличие локальных постов.
- Если есть — сразу отображаем их.
- NetworkManager в фоне загружает свежие посты.
- Новые посты сохраняются в StorageManager с отметкой времени.
- При достижении лимита хранилища старые записи удаляются по LRU-алгоритму (Least Recently Used).
- Токены для пагинации (курсоры) хранятся вместе с данными.
Вывод
Связь между NetworkManager и StorageManager — это фундамент для создания отзывчивых, стабильных и экономных iOS--приложений. Правильная организация их взаимодействия через абстрактные протоколы, сервисный слой и четкие стратегии синхронизации позволяет создавать архитектуру, которая легко тестируется, масштабируется и адаптируется к изменениям бизнес-логики. Ключевой принцип: NetworkManager отвечает за транспортировку данных, а StorageManager — за их жизненный цикл внутри приложения, вместе они реализуют полный цикл «сервер → устройство → представление».