Можно ли в Swift extension протокола добавить соответствие другому протоколу?

Ответ

Да, можно. Это мощная техника для добавления требований протокола и реализации по умолчанию через условные расширения.

1. Добавление соответствия протоколу с реализацией по умолчанию

protocol Displayable {
    var displayName: String { get }
}

protocol Identifiable {
    var id: UUID { get }
}

// Расширяем Identifiable, добавляя соответствие Displayable
// только для типов, которые уже соответствуют Identifiable
extension Identifiable {
    var displayName: String {
        return "ID: (id.uuidString.prefix(8))"
    }
}

// Теперь любой тип, соответствующий Identifiable,
// автоматически получает реализацию Displayable
struct User: Identifiable {
    let id = UUID()
    // displayName доступен автоматически
}

let user = User()
print(user.displayName) // "ID: A1B2C3D4"

2. Условные расширения с where-ограничениями

protocol Savable {
    func save()
}

// Добавляем соответствие Savable только для Collection,
// элементы которой соответствуют Codable
extension Collection where Element: Codable {
    func save() {
        let encoder = JSONEncoder()
        if let data = try? encoder.encode(self) {
            // Сохранение данных
            UserDefaults.standard.set(data, forKey: "collection")
        }
    }
}

// Теперь массив Codable объектов получает метод save()
struct Item: Codable {}
let items: [Item] = [Item(), Item()]
items.save() // Работает!

Ключевые преимущества:

  • Повышение переиспользуемости кода
  • Избегание дублирования реализации
  • Точечное добавление функциональности только нужным типам
  • Поддержка протокол-ориентированного дизайна

Ответ 18+ 🔞

А, ну ты смотри какой, блядь, умник подъехал! Хочешь в extension хранимое свойство запихнуть, да? Ну, дружок, держи свои штаны — нихуя не выйдет!

Это, блядь, фундаментальное ограничение Swift, ёпта! Расширение — оно как бы дополняет, а не ломает структуру памяти типа. Представь, что ты пришёл в гости и начал в чужой квартире несущие стены ломать. Тебе же по ебалу дадут, правильно? Вот и Swift так же.

Что МОЖНО делать в extension, чтобы не выглядеть полным мудаком:

  1. Вычисляемые свойства (computed properties) — это твой лучший друг, если мозгов хватает.

    extension UIView {
    var isVisible: Bool {
        get { return !isHidden && alpha > 0 }
        set { isHidden = !newValue }
    }
    }

    Вот видишь? Никакой магии, просто логика. Не хранит нихуя, а работает.

  2. Статические свойства — тоже ок.

    extension UIColor {
    static var customRed: UIColor {
        return UIColor(red: 0.9, green: 0.1, blue: 0.1, alpha: 1.0)
    }
    }

    Типа константы, блядь. Красота.

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

1. Ассоциированные объекты — но только для классов, которые от NSObject торчат, как хуй из штанов.

import ObjectiveC

extension UIView {
    private struct AssociatedKeys {
        static var customTag = "customTag"
    }

    var customTag: String? {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.customTag) as? String
        }
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.customTag, newValue, .OBJC_ASSOCIATION_RETAIN)
        }
    }
}

// Использование
let view = UIView()
view.customTag = "ImportantView"

Вот это уже похоже на хранимое, да? Но это чисто Objective-C магия, блядь. Swift так не умеет из коробки.

2. Обёртка через вычисляемое свойство — для тех, кто боится Objective-C как чёрт ладана.

class Storage {
    static let shared = Storage()
    private var values: [ObjectIdentifier: Any] = [:]

    func setValue<T>(_ value: T, for key: ObjectIdentifier) {
        values[key] = value
    }

    func getValue<T>(for key: ObjectIdentifier) -> T? {
        return values[key] as? T
    }
}

extension UIView {
    var persistentData: String? {
        get {
            let key = ObjectIdentifier(self)
            return Storage.shared.getValue(for: key)
        }
        set {
            let key = ObjectIdentifier(self)
            Storage.shared.setValue(newValue, for: key)
        }
    }
}

Ну, типа храним, но на самом деле — нет, блядь. Всё в отдельной словарь засунули. Костыль, конечно, но работает.

Итог, ёпта:

  • Хочешь по-честному — добавляй свойства в основное объявление типа, а не выёбывайся.
  • Расширения — для поведения, а не для изменения скелета.
  • Ассоциированные объекты — это как грязные носки под кроватью: работает, но пахнет.

Вот так вот, блядь. Разобрался? Иди теперь делай, а не вопросы ебаные задавай!