Help us understand the problem. What is going on with this article?

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

More than 3 years have 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の例は、ライブラリを自作する際に参考にできそうです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした