За годы работы разработчиком iOS, я собрал множество инструментов и полезных штук, которые облегчают процесс разработки. В этой статье, я хочу поделиться одним из таких инструментов. Это будет не большая статья. Я покажу, как пользоваться этой утилитой, продемонстрирую её в действии. Надеюсь, что статья окажется полезной для вас.
Мне часто встречалась ситуация когда надо подписать enum со вложенными типами под протокол Equatable и приходилось реализовывать его функции
static func ==(lhs: T, rhs: T) -> Bool
Чтобы упростить жизнь и каждый раз не писать сложные static func ==(lhs: T, rhs: T) -> Bool Можно подписать enum под protocol ComplexEquatable
/// ```swift /// enum SomeState: ComplexEquatable { /// case text(SomeText) /// } /// /// class SomeText { /// var text: String? /// /// init(text: String?) { /// self.text = text /// } /// } /// /// let objectOne = SomeState.text(.init(text: nil)) /// let objectTwo = SomeState.text(.init(text: "Hello, World!")) /// objectOne == objectTwo // false /// /// let objectOne = SomeState.text(.init(text: "Hello, World!")) /// let objectTwo = SomeState.text(.init(text: "Hello, World!")) /// objectOne == objectTwo // true /// /// let objectOne = SomeState.text(.init(text: "Hello")) /// let objectTwo = SomeState.text(.init(text: "Hello, World!")) /// objectOne == objectTwo // false /// ``` public protocol ComplexEquatable: Hashable, Equatable {} extension ComplexEquatable { static func == (lhs: Self, rhs: Self) -> Bool { lhs.hashValue == rhs.hashValue } func hash(into hasher: inout Hasher) { hasher.combine(getObjectInfo(of: self)) } private func getObjectInfo(of instance: Any) -> String { let mirror = Mirror(reflecting: instance) var result = "" for (index, child) in mirror.children.enumerated() { if let propertyName = child.label { result += "\(index == 0 ? "" : ",")\(propertyName):\(child.value)" let childString = getObjectInfo(of: child.value) if !childString.isEmpty { result += "{\(childString)}" } } } if result == "" { return String(describing: instance) } return result } }
Сердцем ComplexEquatable является метод getObjectInfo, использующий Mirror для рефлексии объектов. Этот метод позволяет исследовать структуру объекта и генерировать уникальное хэш-значение, основываясь на всех его свойствах. Это ключ к тому, чтобы два объекта, идентичные по структуре и содержанию, считались одинаковыми.
Пример использования
Рассмотрим несколько примеров для enum.
enum SomeState: ComplexEquatable { case text(SomeText) } class SomeText { var text: String? init(text: String?) { self.text = text } }
Когда мы сравниваем два объекта SomeState, содержащих nil и строку "Hello, World!" соответственно, результатом будет false, так как их содержимое различно.
let objectOne = SomeState.text(.init(text: nil)) let objectTwo = SomeState.text(.init(text: "Hello, World!")) objectOne == objectTwo // false
Однако, если сравнивать два объекта с одинаковыми строками "Hello, World!", результатом будет true. Это демонстрирует гибкость ComplexEquatable в различных сценариях сравнения.
let objectOne = SomeState.text(.init(text: "Hello, World!")) let objectTwo = SomeState.text(.init(text: "Hello, World!")) objectOne == objectTwo // true
