Sobes.tech
Middle

Czy kiedykolwiek używałeś biblioteki Combine w swoich projektach?

sobes.tech AI

Odpowiedź od AI

Tak, używałem.

Głównym zastosowaniem Combine w moich projektach jest obsługa zdarzeń asynchronicznych, takich jak:

  • Żądania sieciowe: obsługa odpowiedzi API, transformacja danych, obsługa błędów.
  • Zmiany w elementach UI: reakcja na działania użytkownika, zmiany w polach tekstowych, przesuwanie.
  • Przetwarzanie danych lokalnych: śledzenie zmian w bazach danych (np. Core Data), powiadomienia z FileManager.
  • Tworzenie reaktywnych strumieni danych w architekturze MVVM do wiązania View z ViewModel.

Zalety korzystania z Combine:

  1. Zmniejszenie ilości kodu i poprawa czytelności przy pracy z operacjami asynchronicznymi.
  2. Centralne zarządzanie błędami i anulowanie operacji.
  3. Łatwa kompozycja i transformacja strumieni danych za pomocą operatorów.
  4. Naturna integracja z frameworkami Apple (SwiftUI, Core Data, URLSession).

Przykład użycia Combine do żądania sieciowego:

import Combine
import Foundation

struct APIResult: Decodable {
    let value: String
}

func fetchData(from url: URL) -> AnyPublisher<APIResult, Error> {
    URLSession.shared.dataTaskPublisher(for: url)
        .map(\data) // Wyodrębnia dane z krotki (data, response)
        .decode(type: APIResult.self, decoder: JSONDecoder()) // Dekoduje dane
        .receive(on: DispatchQueue.main) // Przełącza na główny wątek do aktualizacji UI
        .eraseToAnyPublisher() // Usuwa specyfikę typu dla uproszczenia
}

// Przykład subskrypcji
let cancellable = fetchData(from: URL(string: "https://api.example.com/data")!)
    .sink(receiveCompletion: { completion in
        switch completion {
        case .finished:
            print("Żądanie zakończone pomyślnie")
        case .failure(let error):
            print("Błąd żądania: \(error.localizedDescription)")
        }
    }, receiveValue: { result in
        print("Odebrane dane: \(result.value)")
    })
// Należy przechowywać referencję do cancellable, aby subskrypcja pozostała aktywna

Używałem również Combine do łączenia właściwości w MVVM:

class ViewModel {
    @Published var text: String = ""
    @Published var isValid: Bool = false

    private var cancellables = Set<AnyCancellable>()

    init() {
        $text
            .map { $0.count > 0 } // Przekształca tekst na wartość boolowską
            .assign(to: &$isValid) // Przypisuje wynik do właściwości isValid
    }
}

class ViewController: UIViewController {
    let textView = UITextField()
    let validationLabel = UILabel()
    let viewModel = ViewModel()

    private var cancellables = Set<AnyCancellable>()

    override func viewDidLoad() {
        super.viewDidLoad()

        // Binding UITextField do ViewModel
        NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: textView)
            .compactMap { ($0.object as? UITextField)?.text }
            .assign(to: \.text, on: viewModel) // Przypisanie tekstu do ViewModel

        // Binding ViewModel do UILabel
        viewModel.$isValid
            .map { $0 ? "Ważne" : "Nie ważne" }
            .assign(to: \.text, on: validationLabel) // Przypisanie tekstu do UILabel
            .store(in: &cancellables) // Zapisanie subskrypcji
    }
}

Doświadczenie z Combine jest pozytywne, znacznie upraszcza pracę z asynchronicznością w porównaniu do starszych podejść (closures, delegates) w skomplikowanych scenariuszach.