· 3 min czytania

Porównywanie obiektów w Swift za pomocą protokołu Equatable

Jak usprawnić porównywanie obiektów w Swift za pomocą protokołu Equatable? Zobacz na konkretnych przykładach.

Jak usprawnić porównywanie obiektów w Swift za pomocą protokołu Equatable? Zobacz na konkretnych przykładach.

Zgodność z protokołem Equatable umożliwia porównywanie obiektów w Swift pod kątem ich równości lub nierówności przy użyciu operatorów (==) i (!=). Dzięki temu możemy na przykład przeprowadzać określone operacje w zależności od tego, czy obiekty są zgodne lub nie.

Equatable jest bardzo łatwy w implementacji, a co lepsze większość podstawowych typów w standardowej bibliotece Swift jest już z nim zgodna. Jednak, w przypadku tworzenia własnych typów, konieczne będzie zaimplementowanie tego protokołu samemu. Podobnie problem możemy napotkać porównując obiekty z zewnętrzych biliotek, które tego protokołu nie zaimplementowały.

Przyjrzyjmy się jak możemy rozwiązać te problemy 👇

Jak zapewnić zgodność z Equatable?

Aby zapewnić typowi porównywalność wystarczy zaimplementować protokół Equatable, jak poniżej:

struct Dog: Equatable {
    let id = UUID()
    let name: String
}

Teraz możemy już porównywać obiekty tej struktury dowolnie:

let dog1 = Dog(name: "Doggo")
let dog2 = Dog(name: "Carly")

// Wynik: false
print(dog1 == dog2) 

To wszystko, co musimy zrobić dzięki automatycznie syntezowanej zgodności w kompilatorze Swift (ang. “automatically synthesized conformance”).

Dzięki czemu? 🤷‍♂️

Automatycznie syntezowana zgodność

Wraz ze Swift 4.1 do kompilatora dodano funkcję syntezowania protokołów Equatable oraz Hashable, a dokładnie tu: SE-0185. Dzięki temu aby porównać ze sobą dwa obiekty tego samego typu wystarczy, że każdy członek tego typu będzie implementował protokół Equatable.

Na przykład, struktura poniżej jest zgodna z tym protokołem, ponieważ id (UUID) oraz name (String) są typami, które domyślnie wspierają ten protokół i są z nim zgodne.

struct Dog: Equatable {
    let id = UUID()
    let name: String
}

Ale weźmy pod uwagę inny przykład 🤔

struct Address {
    let city: String
}

struct Person: Equatable {
    let id = UUID()
    let name: String
    let address: Address
}

Tu już mamy problem, bo o ile struktura Person implementuje protokół Equatable to Address już nie, a wtedy z Xcode dostaniemy błąd o treści:

Type ‘Person’ does not conform to protocol ‘Equatable’

Aby ten błąd naprawić, struktura Address również musi implementować ten protokół.

Bądź na bieżąco z iOS i Swift 📱

Dołącz do newsletter'a i otrzymuj więcej takich treści regularnie na swoją skrzynkę mailową 📬

Zewnętrzne biblioteki

A co jeśli nie mamy bezpośrednio dostępu do kodu struktury czy klasy? Np. jeśli używamy typów dostarczanych przez zewnętrzne biblioteki?

Wtedy z pomocą przychodzą extensiony. Założmy, że chcemy rozszerzyć jakąś strukturę o Equatable. Wystarczy, że napiszemy:

// To jest jakiś typ dostarczony przez zewnętrzną bibliotekę
extension StructFrom3rdPartyLibrary: Equatable { }

Własna logika porównywania

A co jeśli chcemy porównać ze sobą obiekty w pewien specyficzny sposób?

struct Phone: Equatable {
    let id = UUID()
    let model: String
    let vendor: String
}

Założmy, że chcielibyśmy porównywać ze sobą obiekty telefonów tylko i wyłącznie na podstawie producenta, czyli w tym przypadku pola vendor.

let phone1 = Phone(model: "Galaxy S10", vendor: "Samsung")
let phone2 = Phone(model: "Galaxy S20", vendor: "Samsung")

Możemy napisać własną implementację funkcji porównującej aby uzyskać porządany efekt:

extension Phone: Equatable {
    static func == (lhs: Phone, rhs: Phone) -> Bool {
        lhs.vendor == rhs.vendor
    }
}

// Wynik: true
print(phone1 == phone2)

A co z enumami?

Spokojnie, enumy są automatycznie syntezowane, więc nie musimy się o nic martwić. No, o ile ich wszystkie wartości i pola są porównywalne, o czym warto pamiętać.

Podsumowanie

Wnioski? Jeśli chcesz porównywać obiekty tego samego typu, zastosuj protokół Equatable zgodnie z tym, co zaprezentowałem. Możesz opierać się na domyślnej implementacji tego protokołu albo napisać własną logikę porównywania obiektów.

Udostępnij:
Powrót do Bloga