Posted at

有名ライブラリに学ぶSwift実践テクニック

More than 1 year has passed since last update.

以前こちらの記事で、Alamofireを読んでいて気づいたSwiftの便利な書き方についてご紹介しました。

Alamofireから学ぶSwift実践テクニック

今回は他のライブラリから、気になった書き方をご紹介したいと思います。


追加可能なenumのように使えるstaticプロパティ


ライブラリ名

SwiftyUserDefaults


内容

SwiftyUserDefaultsはUserDefaultsを便利に使うためのライブラリです。

キーの定義とsubscript引数の入れ方に特徴があります。


README.md

extension DefaultsKeys {

static let username = DefaultsKey<String?>("username")
static let launchCount = DefaultsKey<Int>("launchCount")
}

let username = Defaults[.username]
Defaults[.hotkeyEnabled] = true


Defaultssubscriptの入力が「.」から始まっており、enumのように見えますがDefaultsKeysのstaticプロパティです。

staticプロパティの型が自身の型である場合、その型を引数に取る関数などで型名を省略して渡すことがでるようです。


説明用コード

class StaticPorpertyClass {

static let p1 = StaticPorpertyClass() // 同じ型であることがポイント
}

func myFunction(_ obj: StaticPorpertyClass) {
// ...
}

myFunction(.p1) // StaticPorpertyClass.p1としなくて良い


SwiftyUserDefaults.swiftのDefaultsKeysの定義をご参照ください。

また、NotificationCenterでも同手法が使われています。


メリット


  • 引数のパターンがDefaultsKeys型で制限できる。

  • extensionで外部からパターンを増やすことができる。(enumで制限してしまうとライブラリ外部からDefaultsKeysを追加できない)

  • 引数のところで「.」を打つだけでDefaultsKeysのstaticプロパティが候補で表示される。


  • DefaultsKeyに好きな情報を詰め込める。


オリジナルextensionの入口プロパティ


ライブラリ名

Kingfisher


内容

KingfisherはUIImageViewなどに画像の非同期ダウンロード機能を拡張するライブラリです。

このライブラリでは拡張対象のクラスに対し、メソッドを直接追加するのではなく、kfというプロパティ経由でメソッドにアクセスさせています。


README.mdより抜粋

let url = URL(string: "url_of_your_image")

imageView.kf.setImage(with: url)

kfKingfisherクラスになります。実装はこうなっています。


Kingfisher.swiftより抜粋

public final class Kingfisher<Base> {

public let base: Base
public init(_ base: Base) {
self.base = base
}
}

/**
A type that has Kingfisher extensions.
*/

public protocol KingfisherCompatible {
associatedtype CompatibleType
var kf: CompatibleType { get }
}

public extension KingfisherCompatible {
public var kf: Kingfisher<Self> {
get { return Kingfisher(self) }
}
}

extension Image: KingfisherCompatible { }
#if !os(watchOS)
extension ImageView: KingfisherCompatible { }
extension Button: KingfisherCompatible { }
#endif


KingfisherCompatibleをつけるだけでkfプロパティが追加されるようになっています。

extensionの実装は、Baseがどのクラスかによって切り替えることができます。


ImageView+Kingfisher.swiftより抜粋

extension Kingfisher where Base: ImageView {



NSButton+Kingfisher.swift

extension Kingfisher where Base: NSButton {


この書き方はRxSwiftでも使われています。


メリット



  • kfがネームスペースの役割を果たすので既存メソッドと混ざらない。

  • 既存メソッドと重複した名前も使用可能。

ライブラリを作るときなどに、拡張対象の型が多い場合、あるいは拡張されるプロパティやメソッドが多い場合に便利そうです。


可変長引数subscript


ライブラリ名

SwiftyJSON


内容

SwiftyJSONはJSONのパーサーですが、要素にアクセスしやすいよういろいろ工夫がされています。その中で、可変長引数を持つsubscriptが定義されています。


SwiftyJSON.swiftより抜粋

public subscript(path: JSONSubscriptType...) -> JSON {

get {
return self[path]
}
set {
self[path] = newValue
}
}

JSONの深い所にある要素にアクセスするために使われています。


README.mdより抜粋

json["list",3,"what"] = "that"


うまく使えば、便利な型が作れそうです。

例として複数の値を取り出すDictionary風の型を作ってみました。


説明用コード

struct MultiKeyDictionary<Key: Hashable, Value> {

private let _dictionary: [Key : Value]

init(_ dictionary: [Key : Value]) {
_dictionary = dictionary
}

subscript(keys: Key...) -> [Value] {
return keys.flatMap { return _dictionary[$0] }
}
}

let source = [
"a" : "hoge",
"b" : "fuga",
"c" : "foo",
"d" : "bar"
]
let mkDic = MultiKeyDictionary(source)

print(mkDic["a"]) // -> ["hoge"]
print(mkDic["a", "d"]) // -> ["hoge", "bar"]


subscriptを使った場合は、可変長も検討してみると面白そうです。


メリット


  • 可変長引数のsubscriptを1引数の延長として実装できれば、直感的でより便利な型になる。


custom operatorのチェーン


ライブラリ名

SwiftState


内容

SwiftStateはcustom operatorがいろいろ定義されていて、状態遷移を定義したり実行したりするのが特徴です。一つの状態遷移を表すのは.state0 => .state1と書けるのですが、連続した複雑な状態遷移も.state0 => .state1 => .state2のように書けるよう工夫しています。


説明用コード

machine = StateMachine<MyState, NoEvent>(state: .state0) { machine in

machine.addRouteChain(.state0 => .state1 => .state2, handler: { _ in
print("0 -> 1 -> 2")
})
}

custom operatorの戻り値の型も、再度同じcustom operatorの引数として受け付けるようにするのがポイントです。実際のコードではいろんなパターンが網羅されています。TransitionChain.swiftをご参照ください。細かい工夫ですが気持ちよく書けるので、custom operatorを実装する場合は考慮すると良さそうです。


メリット


  • より便利で直感的にcustom operatorを使うことができる。


最後に

いくつか自分が気になった例を書いてみました。

特にSwiftyUserDefaultsとKingfisherのパターンは使い所が多そうですので、自分のプロジェクトにもすぐ応用できそうです。

SwiftyJSONとSwiftStateの例は、ライブラリを自作する際に参考にできそうです。