Posted at

Swiftのprotocol extensionでデフォルト引数を使いたいとき


はじめに

protocol extensionを使ってデフォルト引数を設定したい場合どうすればわからなくなったので調べたメモ


やりたいこと

下記のようなプロトコルがある。

public protocol Test {  

func getPage(_ url: URL)
}

この関数getPageの第二引数に新しくnameという引数を設定したい

しかしこの引数、必ず必要というわけではない。

かつ

色んな所からこの関数が呼ばれているため、第二引数をすべての使っている場所で追加したくない。

デフォルト引数を設定したいと考えた


プロトコルが適合された使用例

class hogeClass: Test {

func getPage(_ url: URL){
//何かの処理
}
}


私のよくない例(その1)

プロトコルの定義にはデフォルト引数を設定できないので下記のようには書けない


これはダメ

public protocol Test {  

func getPage(_ url: URL,name: String? = nil)
}


私のよくない例(その2)


じゃあプロトコルを増やしてやるかと思った

public protocol Test {  

func getPage(_ url: URL)
          func getPage(_ url: URL, name: String?)
}


protocol extensionを使ってデフォルト実装をする

詳細Swift3より引用

プロトコル拡張にはメソッドやプロパティの設定(デフォルト)の実装が記述出来るといえます。

プロトコル拡張で定義された実装を既定実装(default implementation),
あるいはデフォルト実装と呼びます(252p 詳細Swift3)

とのことなので第二引数のデフォルトをnilに設定してデフォルト実装をした

extension Test {

func getPage(_ url: URL, name: String? = nil){}
}


問題点

このままだと適合されたクラスはどうなるかというと、下記のように適合されたプロトコルに設定されている関数を呼ぶ必要があるため、同じ名前だが、引数数が異なる関数を設定する必要がある・・・

class hogeClass: Test {

func getPage(_ url: URL){
//何かの処理
}
func getPage(_ url: URL, name: String?){
//何かの処理
}
}

うーんもっといい方法があるはず・・・・


解決策

protocol extensionでよりよくする


protocolの定義

public protocol Test {  

func getPage(_ url: URL)
          func getPage(_ url: URL, name: String?)
}


protocol extension

extension Test {

///ここがポイント
func getPage(_ url: URL){
self.getPage(_ url: URL, name: nil)
        }
}


protocolが適合されているクラス

class hogeClass: Test {

// ここのgetPage(url:)関数はprotocol extensionですでに定義されているため
//  適合されているクラスでこの関数を実行する必要はない
// func getPage(_ url: URL){
// //何かの処理
// }

func getPage(_ url: URL, name: String?){
//何かの処理
}
}

これで大丈夫なはず

なにか間違いもっとこうしたほうがいいなどありましたら教えて下さい 🙏