← Назад к вопросам

Как связаны NetworkManager и StorageManager?

1.8 Middle🔥 121 комментариев
#Архитектура и паттерны

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Связь между 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 или модели данных требуется синхронное обновление формата в хранилище.

Практический пример: Лента новостей

Для ленты новостей типичная реализация выглядит так:

  1. StorageManager проверяет наличие локальных постов.
  2. Если есть — сразу отображаем их.
  3. NetworkManager в фоне загружает свежие посты.
  4. Новые посты сохраняются в StorageManager с отметкой времени.
  5. При достижении лимита хранилища старые записи удаляются по LRU-алгоритму (Least Recently Used).
  6. Токены для пагинации (курсоры) хранятся вместе с данными.

Вывод

Связь между NetworkManager и StorageManager — это фундамент для создания отзывчивых, стабильных и экономных iOS--приложений. Правильная организация их взаимодействия через абстрактные протоколы, сервисный слой и четкие стратегии синхронизации позволяет создавать архитектуру, которая легко тестируется, масштабируется и адаптируется к изменениям бизнес-логики. Ключевой принцип: NetworkManager отвечает за транспортировку данных, а StorageManager — за их жизненный цикл внутри приложения, вместе они реализуют полный цикл «сервер → устройство → представление».