Swift class 的扩展仅当它符合特定协议时

Swift extension of a class ONLY when it conforms to a specific protocol

你好 =) 我刚刚遇到一个设计问题,我需要(基本上)执行以下操作:

我想在 viewWillAppear: 上注入一些符合协议 MyProtocol[=47 的 UIViewController subclass 的代码=].代码解释:

protocol MyProtocol
{
    func protocolFunction() {
        //do cool stuff...
    }
}

extension UIViewController where Self: MyProtocol //<-----compilation error
{
    public override class func initialize()
    {
        //swizzling stuff switching viewWillAppear(_: Bool) with xxx_viewWillAppear(animated: Bool)
    }

    //  MARK: - Swizzling

    func xxx_viewWillAppear(animated: Bool)
    {
        self.xxx_viewWillAppear(animated)

        //invoke APIs from  
        self.protocolFunction() // MyProtocol APIs
        let viewLoaded = self.isViewLoaded // UIViewController APIs

    }
}

这里的主要问题是我需要在 UIVIewController 扩展中做两件事:

  1. 同时调用 MyProtocolUIViewController API 的
  2. 覆盖 UIViewController 方法 initialize() 以便能够调配 viewWillAppear:

这 2 个功能似乎不兼容(截至 Swift 3)因为:

  1. 我们无法使用条件 扩展 classes(即 extension UIViewController where Self: MyProtocol
  2. 如果我们 扩展协议 ,我们可以添加条件 extension MyProtocol where Self: UIViewController 我们不能覆盖 class 中的方法在协议扩展 中,这意味着我们不能 public override class func initialize() 这是 swizzling 所需要的。

所以我想知道是否有人可以为我面临的这个问题提供 Swifty 解决方案? =)

提前致谢!!

好吧,到目前为止我还没有找到真正令人满意的方法,但我决定 post 我最终为这个特定问题所做的事情。 简而言之,解决方案是这样的(使用原始示例代码):

protocol MyProtocol
{
    func protocolFunction() {
        //do cool stuff...
    }
}

extension UIViewController //------->simple extension on UIViewController directly
{
    public override class func initialize()
    {
        //swizzling stuff switching viewWillAppear(_: Bool) with xxx_viewWillAppear(animated: Bool)
    }

    //  MARK: - Swizzling

    func xxx_viewWillAppear(animated: Bool)
    {
        self.xxx_viewWillAppear(animated)

        //------->only run when self conforms to MyProtocol
        if let protocolConformingSelf = self as? MyProtocol { 
            //invoke APIs from  
            protocolConformingSelf.protocolFunction() // MyProtocol APIs
            let viewLoaded = protocolConformingSelf.isViewLoaded // UIViewController APIs
        }

    }

}

缺点:

  • 不会"the Swift way"做事
  • 调配方法将被调用并对所有 UIViewControllers 生效,即使我们只验证那些符合 MyProtocol 协议的 运行 敏感代码行.

我非常希望它能帮助其他面临类似情况的人 =)

您已接近解决方案。只需要换一种方式。仅当它是 UIViewController 的一部分时才扩展协议。

protocol MyProtocol
{
  func protocolFunction() {
    //do cool stuff...
  }
}

extension MyProtocol where Self: UIViewController {
  public override class func initialize()
  {
    //swizzling stuff switching viewWillAppear(_: Bool) with xxx_viewWillAppear(animated: Bool)
  }

  //  MARK: - Swizzling

  func xxx_viewWillAppear(animated: Bool)
  {
    self.xxx_viewWillAppear(animated)

    //invoke APIs from  
    self.protocolFunction() // MyProtocol APIs
    let viewLoaded = self.isViewLoaded // UIViewController APIs
  }
}