Как правильно работать с Combine в Swift – обработка асинхронных данных в SwiftUI

SwiftUI отлично интегрируется с фреймворком Combine, который позволяет управлять асинхронными потоками данных и обновлять интерфейс в реактивном стиле. В этой статье мы рассмотрим, как использовать Combine в SwiftUI для работы с асинхронными операциями.
SwiftUI отлично интегрируется с фреймворком Combine, который позволяет управлять асинхронными потоками данных и обновлять интерфейс в реактивном стиле. В этой статье мы рассмотрим, как использовать Combine в SwiftUI для работы с асинхронными операциями.

Основы Combine в SwiftUI

Combine используется для обработки потоков данных, например, сетевых запросов, таймеров или пользовательского ввода. Основные компоненты Combine:

  • Publisher – источник данных, который генерирует значения во времени.
  • Subscriber – подписчик, который получает данные и выполняет действия.
  • Operators – методы для трансформации и фильтрации данных.

Пример 1: Обработка сетевого запроса в SwiftUI с Combine

Допустим, у нас есть API, возвращающее список пользователей. Мы хотим загружать данные и отображать их в SwiftUI.

Создание модели данных:

import SwiftUI import Combine struct User: Codable, Identifiable { let id: Int let name: String }

ViewModel с использованием Combine:

class UsersViewModel: ObservableObject { @Published var users: [User] = [] private var cancellables = Set<AnyCancellable>() func fetchUsers() { let url = URL(string: "https://jsonplaceholder.typicode.com/users")! URLSession.shared.dataTaskPublisher(for: url) .map { $0.data } // Берем только data из URLSession.DataTaskPublisher .decode(type: [User].self, decoder: JSONDecoder()) .receive(on: DispatchQueue.main) // Обновляем UI в главном потоке .sink(receiveCompletion: { completion in switch completion { case .failure(let error): print("Ошибка загрузки: \(error.localizedDescription)") case .finished: break } }, receiveValue: { [weak self] users in self?.users = users }) .store(in: &cancellables) } }

Использование ViewModel в SwiftUI:

struct UsersListView: View { @StateObject private var viewModel = UsersViewModel() var body: some View { List(viewModel.users) { user in Text(user.name) } .onAppear { viewModel.fetchUsers() } } }

Пример 2: Работа с Timer в SwiftUI

Combine позволяет легко создавать таймеры и обновлять UI.

ViewModel с Timer:

class TimerViewModel: ObservableObject { @Published var currentTime: String = "" private var cancellables = Set<AnyCancellable>() init() { Timer.publish(every: 1, on: .main, in: .common) .autoconnect() .map { _ in let formatter = DateFormatter() formatter.timeStyle = .medium return formatter.string(from: Date()) } .assign(to: &$currentTime) } }

Использование в SwiftUI:

struct TimerView: View { @StateObject private var viewModel = TimerViewModel() var body: some View { Text(viewModel.currentTime) .font(.largeTitle) .padding() } }

Пример 3: Поиск с Combine

Если нужно обработать ввод пользователя, например, в поиске, Combine помогает избежать ненужных запросов и уменьшить нагрузку на сеть.

ViewModel для поиска:

class SearchViewModel: ObservableObject { @Published var searchText: String = "" @Published var results: [String] = [] private var cancellables = Set<AnyCancellable>() init() { $searchText .debounce(for: .milliseconds(500), scheduler: RunLoop.main) // Задержка перед обработкой .removeDuplicates() .sink { [weak self] query in self?.performSearch(query) } .store(in: &cancellables) } private func performSearch(_ query: String) { let allItems = ["Swift", "Combine", "SwiftUI", "Async", "Reactive"] results = query.isEmpty ? [] : allItems.filter { $0.lowercased().contains(query.lowercased()) } } }

Использование в SwiftUI:

struct SearchView: View { @StateObject private var viewModel = SearchViewModel() var body: some View { VStack { TextField("Поиск...", text: $viewModel.searchText) .textFieldStyle(RoundedBorderTextFieldStyle()) .padding() List(viewModel.results, id: \ .self) { result in Text(result) } } } }

Итог:

Combine значительно упрощает обработку асинхронных данных в SwiftUI:

  • Работа с сетевыми запросами через URLSession.dataTaskPublisher.
  • Обновление интерфейса в реальном времени с Timer.
  • Фильтрация пользовательского ввода с debounce и removeDuplicates.
  • Гибкость и удобство при работе с потоками данных.

Использование Combine в SwiftUI делает код более читаемым, декларативным и реактивным. 🚀

Начать дискуссию