associatedtypeを持つprotocolをクラスのメンバ変数として使う方法
Swiftのprotocolには, protocol版Genericsとでも言うべきassociatedtype
があります.
public protocol Loader {
associatedtype ItemType
func load() -> [ItemType]
}
これを安直に使おうとするとコンパイルエラーになり, どうしたものかと悩まされることになります.
Protocol 'Loader' can only be used as a generic constraint because it has Self or associated type requirements
クラスのメンバ変数に利用する方法
型変数を消すType erasure(型消去)が使われることが多いですが, クラスのメンバ変数に持つだけであればGenericsにすることで対応できます.
class ViewController<LoaderType: Loader>: UIViewController where LoaderType.ItemType == Movie
{
private var loader: LoaderType
// MARK: - Lifecycle
init(loader: LoaderType) {
self.loader = loader
super.init(nibName: "ViewController", bundle: Bundle(for: type(of: self)))
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
このようにすると, ItemType
がMovie
のLoader
であることを指定することができます.
型推論に関するバグへの対処方法
注意点として, Swift 5.6現在, このクラスのメソッド内で以下のコードを書くとコンパイルエラーになります.
@IBAction private func didTapButton(_ sender: UIButton) {
// Failed: Cannot convert value of type 'MovieLoader2' to expected argument type 'LoaderType'
let vc = ViewController(loader: MovieLoader2())
present(vc, animated: true, completion: nil)
}
これを回避するには, 型推論ではなく明示的に型を指定する必用があります.
@IBAction private func didTapButton(_ sender: UIButton) {
let vc = ViewController<MovieLoader2>(loader: MovieLoader2())
present(vc, animated: true, completion: nil)
}
このバグはapple/swift#58413で報告しているので, 最新の状況はこちらを参照してください.
記事が気に入ったらチップを送ることができます!
You can give me a cup of coffee :)
Kyash ID: soranoba
Amazon: Wish List
GitHub Sponsor: github.com/sponsors/soranoba
PayPal.Me: paypal.me/soranoba
(Updated: )