Какими принципами вы руководствуетесь при разделении iOS-приложения на модули (модульность)?

«Какими принципами вы руководствуетесь при разделении iOS-приложения на модули (модульность)?» — вопрос из категории Архитектура, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Ключевой принцип — сильная связность внутри модуля и слабая связанность между модулями. Это достигается через:

  1. Принцип единой ответственности (SRP): Модуль решает одну бизнес-задачу или группу紧密相关 задач (например, Authentication, Payments, NewsFeed).
  2. Инверсия зависимостей (DIP): Модули зависят от абстракций (протоколов), а не от конкретных реализаций других модулей.
  3. Сокрытие реализации: Публичный интерфейс модуля минимален и стабилен.

Пример структуры модуля Networking:

// Модуль: Networking
// Публичный интерфейс модуля (то, что видят другие модули)
public protocol NetworkClient {
    func performRequest(_ endpoint: Endpoint) async throws -> Data
}

public struct Endpoint {
    let path: String
    let method: HTTPMethod
}

// Внутренняя реализация скрыта. Другие модули о ней не знают.
final class DefaultNetworkClient: NetworkClient {
    private let session: URLSession

    public func performRequest(_ endpoint: Endpoint) async throws -> Data {
        // ... реализация сетевого запроса
    }
}

// Модуль: UserProfile (зависит от абстракции Networking)
import Networking // Зависимость на другой модуль

class UserRepository {
    private let networkClient: NetworkClient // Зависимость от протокола

    init(networkClient: NetworkClient) {
        self.networkClient = networkClient
    }

    func fetchUser() async throws -> User {
        let data = try await networkClient.performRequest(Endpoint(path: "/user", method: .get))
        return try JSONDecoder().decode(User.self, from: data)
    }
}

Преимущества модульности:

  • Повторное использование: Модуль Networking можно использовать в разных проектах.
  • Независимая разработка и тестирование: Модули компилируются и тестируются изолированно.
  • Четкие границы ответственности: Упрощает навигацию по коду и онбординг новых разработчиков.
  • Сокращение времени компиляции: При изменении одного модуля пересобирается только он.

Инструменты: Для управления модулями в iOS используются Swift Package Manager (SPM), CocoaPods или тулза от Xcode (разделение на frameworks).