Какие основные способы передачи данных между модулями в SwiftUI и Combine?

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

Ответ

SwiftUI и Combine предлагают несколько паттернов для передачи данных, выбор зависит от степени связности и глубины иерархии.

1. @StateObject / @ObservedObject (Для связи родитель-ребенок) Используется, когда владелец данных (родитель) передает их непосредственному потомку.

// Модель данных
class UserSettings: ObservableObject {
    @Published var username = ""
}

// Родительский View создает и владеет объектом
struct ParentView: View {
    @StateObject var settings = UserSettings() // Владение
    var body: some View {
        ChildView(settings: settings) // Передача
    }
}

// Дочерний View наблюдает за объектом
struct ChildView: View {
    @ObservedObject var settings: UserSettings // Наблюдение
    var body: some View {
        TextField("Name", text: $settings.username)
    }
}

2. @EnvironmentObject (Для глубокой иерархии) Позволяет передавать данные через множество уровней без явной передачи каждому View.

// В корне приложения (или сцены) помещаем объект в окружение
@main
struct MyApp: App {
    @StateObject var settings = UserSettings()
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(settings) // Инъекция
        }
    }
}

// Любой View в иерархии может получить доступ
struct DeepChildView: View {
    @EnvironmentObject var settings: UserSettings // Получение
    var body: some View {
        Text(settings.username)
    }
}

3. Прямая передача через Combine Publisher Подходит для более реактивных или одноразовых событий.

// В одном модуле
class EventService {
    static let shared = EventService()
    private init() {}
    let userDidLogin = PassthroughSubject<String, Never>()
}

// Отправка события
EventService.shared.userDidLogin.send("NewUser")

// Подписка в другом модуле
EventService.shared.userDidLogin
    .sink { userName in
        print("User (userName) logged in")
    }
    .store(in: &cancellables)

Рекомендация: Для сложных приложений рассмотрите архитектурные подходы (MVVM, Redux) с централизованным хранилищем состояния.