Как правильно работать с Combine в Swift – обработка асинхронных данных в 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 делает код более читаемым, декларативным и реактивным. 🚀
Начать дискуссию