Array や Dictionary にもモダンでオシャレな extension を実装する
前書き
RxSwift や Kingfisher、そして自分の Danbo などのライブラリーに採用されている .ex
とかで拡張を明確に区別した extension
の実装法、やり方についてはこちらの記事やこちらの記事に詳しい実装方法を書かれていますが、それらの方法では Array
や Dictionary
といった自分自身の下位型縛りがある型にはそのまま適用できませんでした。where Base == Array
をかくとコンパイラに怒られます。
昨日の投稿に引き続き、どうしても Eltaso で [1, 2, 3].eltaso.random
みたいな書き方をできるようにしたいと思い、昨日色々と四苦八苦した結果、どうにか無事成功したようで、やり方を共有しつつマサカリをお待ちしております。
目標
とりあえずこの記事ではより一般受けな書き方を共有したいと思いますので上記のとちょっと違い [1, 2, 3].ex.random
を目標にしたいと思います。
前準備
一応こちらの記事をベースにしてますので、そちらの記事を参照しながらこの記事を読むとわかりやすいかもしれません。また、できればまず Array
じゃない普通の型にこの実装をする方法を覚えておくことをオススメします。
実装
ExampleCompatible.swift
public protocol ExampleCompatible {
associatedtype CompatibleType
var ex: CompatibleType { get }
}
public struct Example<Base> {
let base: Base
}
public struct ExampleWithSingleAssociatedType<Base, AssociatedType> {
let base: Base
}
Array.swift
extension Array: ExampleCompatible {
public var ex: ExampleWithSingleAssociatedType<Array<Element>, Element> {
return ExampleWithSingleAssociatedType(base: self)
}
}
extension ExampleWithSingleAssociatedType where Base == Array<AssociatedType> {
public var random: AssociatedType? {
guard !self.base.isEmpty else {
return nil
}
let index = Int(arc4random_uniform(UInt32(self.base.count)))
return self.base[index]
}
}
Playground.swift
[1, 2, 3].ex.random // 2
解説
ExampleCompatible.swift
public protocol ExampleCompatible {
associatedtype CompatibleType
var ex: CompatibleType { get }
}
public struct Example<Base> {
let base: Base
}
public struct ExampleWithSingleAssociatedType<Base, AssociatedType> {
let base: Base
}
extension Array: ExampleCompatible {
public var ex: ExampleWithSingleAssociatedType<Array<Element>, Element> {
return ExampleWithSingleAssociatedType(base: self)
}
}
extension ExampleWithSingleAssociatedType where Base == Array<AssociatedType> {
public var random: AssociatedType? {
guard !self.base.isEmpty else {
return nil
}
let index = Int(arc4random_uniform(UInt32(self.base.count)))
return self.base[index]
}
}
[1, 2, 3].ex.random // 2
ExampleCompatible.swift
まず ExampleCompatible
の部分についてみてわかる通り、Example<Base>
の他に、ExampleWithSingleAssociatedType<Base, AssociatedType>
というやつを新たに追加しました。通常の Example
と違い、こちらは Base
と AssociatedType
の2つの型縛りがあります。それ以外は Example
と同じです
また、ここであえて ExampleCompatible
の extension
を削除しました。なぜかというと Array
は ExampleWithSingleAssociatedType
に入れますが、通常の型は Example
に入れるので、直接 extension ExampleCompatible
の中に ex
を定義してあげると物によっては違う型の ex
が返されてくる危険性がありますので、次の Array.swift
の部分と同じように、直接型拡張の中に定義することにしました。
Array.swift
次に Array.swift
の中ですが、まずは Array
を拡張して ExampleCompatible
を適用させます。これは通常の型の拡張を作る時と同じです。ただしここでは直接 ex
の戻り値を定義する必要があります。戻り値の型はご覧の通り ExampleWithSingleAssociatedType<Array<Element>, Element>
です。つまり ExampleWithSingleAssociatedType
の Base
は Array<Element>
、AssociatedType
は Array.Element
になります。
ここまで定義できたらあとは通常の型の時の Example
の拡張と同じように、ExampleWithSingleAssociatedType
を拡張します。制約は Base == Array<AssociatedType>
になります。
拡張の中に random: AssociatedType?
というプロパティーを定義します。中身は単純な自分のランダムな要素を返す(ただし空配列の場合は nil
を返す)だけです。
Playground.swift
ここまでできたらもう Playground
開いて [1, 2, 3].ex.random
を書けば自動で 1
、2
、3
の中からランダムな数を返してくれる命令が使えます。ね?簡単でしょ?
Array
以外への適用
上記の ExampleWithSingleAssociatedType
の定義からわかる通り、Base
と AssociatedType
の二つの型縛りがあるので、Array
だけでなく、自分自身に下位の型縛りが一つある型なら基本どれにでも適用できます。例えば Range<Bound>
とかですね。書き方は単純に Array.swift
の中身の Array
を Range
、Element
を Bound
に書き換えればできるはずです。
では、下位型縛りが二つある物、例えば Dictionary<Key, Value>
はどうすればいいかというと、これも非常に単純で、さっき ExampleWithSingleAssociatedType<Base, AssociatedType>
を追加したのと同じように、もう一つ ExampleWithDualAssociatedType<Base, AssociatedType1, AssociatedType2>
という struct
を定義して、Dictionary
の ex
を ExampleWithDualAssociatedType<Dictionary<Key, Value>, Key, Value>
で返せば OK です。まあ型縛りが増えてくると書くのがだるいですねw
余談
上記はより一般向け(?)な書き方で書きましたが、自分の Eltaso では Example
ではなく EltasoContainer
で名付けられてたり、拡張は eltaso
の名前にしてます。せっかくわざわざ名前衝突を回避しようとこんな書き方導入したっていうのに、他の同様な書き方をするライブラリー導入して ex
で名前衝突したらシャレにならないですもんね。というわけで皆さんも自分でこの書き方を導入する際は ex
ではなく他の名前で入れた方が無難かと思います。
ちなみにこの拡張方法は Eltaso の 4.0 で一般公開する予定ですが、このバージョンではこれと別にメソッドチェーンの対応も実装しようと考えております(どれほどの実用性があるかはさておき)。仕事量がかなり多いのでもしかすると 4.1 で実装する可能性もありますが、一応現在の対応状況をチョイ見せするとこんな感じです:
たーのしー
Author And Source
この問題について(Array や Dictionary にもモダンでオシャレな extension を実装する), 我々は、より多くの情報をここで見つけました https://qiita.com/lovee/items/0ef70534375ecb19e441著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .