web-dev-qa-db-ja.com

Swiftクラスの拡張は、特定のプロトコルに準拠している場合のみ

こんにちは=)私は(本質的に)次のことをする必要があるデザインの問題に直面しました:

プロトコルに準拠する_viewWillAppear:_UIViewControllerサブクラスに少しコードを挿入したいMyProtocol。コードで説明:

_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拡張機能で2つのことを行う必要があることです。

  1. MyProtocolおよびUIViewController AP​​Iの両方を呼び出す
  2. _viewWillAppear:_をスウィズルできるようにUIViewControllerメソッドinitialize()をオーバーライドします

これらの2つの機能は互換性がないようです(Swift 3以降)。

  1. 私たちは条件付きのクラスを拡張できません(つまり_extension UIViewController where Self: MyProtocol_)
  2. 代わりにプロトコルを拡張する場合、条件_extension MyProtocol where Self: UIViewController_を追加できますが、メソッドをオーバーライドできませんプロトコル拡張のクラスから。つまり、スウィズリングに必要なpublic override class func initialize()を実行できません。

それで、私が直面しているこの問題に対してSwiftyソリューションを提供できる誰かがいるのではないかと思っていましたか? =)

前もって感謝します!!

21
Robertibiris

まあ、これまでのところ、本当に満足のいく方法を見つけることはできませんでしたが、この特定の問題に対して結局何をしたかを投稿することにしました。簡単に言えば、ソリューションは次のようになります(元のサンプルコードを使用)。

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
        }

    }

}

欠点:

  • 物事を行う「Swift way)」ではありません
  • 機密性の高いコード行を実行するためにUIViewControllersプロトコルに準拠するもののみを検証する場合でも、スウィズリングメソッドが呼び出され、ALL MyProtocolで有効になります。

私はそれが同じような状況に直面している他の誰かを助けることを願っています=)

6
Robertibiris

あなたは解決策の近くにいました。それを逆にするだけです。 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
  }
}
13
Klemen