swift api design guidelines (dec 3, 2015)
TRANSCRIPT
About Me
• 田中 孝明 (Takaaki Tanaka)
• @kongmingtrap
• iOS Developer (Swift / Objective-C)
• GyazSquare / GitHub
About Me• 平川 剛一 (Goichi Hirakawa)
• @gooichi
• OS X / iOSソフトウェアエンジニア(フリー)
• GyazSquare / GitHub
• Mailer、デバイス制御、MDM系など
• Objective-C歴XX年、最近はSwiftにはまる…
Swift is Open Source• 2015/12/03にオープンソースとして公開
• 2.2リリース:2016年春頃
• 3.0リリース:2016年秋頃
• 3.0の目標の一つ:
API design guidelines: 定義と適用
Agenda• 2015-12-03版APIガイドラインの紹介
• Fundamentals: 基礎
• Naming: 命名
• Conventions: 規約
• Special instructions: 特記事項
Agenda• 2015-12-03版APIガイドラインの紹介
• Fundamentals: 基礎
• Naming: 命名
• Conventions: 規約
• Special instructions: 特記事項
Write a Document Comment
• すべてのメソッドとプロパティ
• MarkDownのSwift方言を使う /// Returns the first index where `element` appears in `self`, /// or `nil` if `element` is not found. /// /// - Complexity: O(`self.count`). public func indexOf(element: Generator.Element) -> Index? {
Agenda• 2015-12-03版APIガイドラインの紹介
• Fundamentals: 基礎
• Naming: 命名
• Conventions: 規約
• Special instructions: 特記事項
Promote Clear Usage• 曖昧さを避けること例えば、以下のようなポジションを指定して削除するメソッドがあった場合、 public mutating func removeAt(position: Index) -> Element
employees.removeAt(x)
Atを省略すると位置を指定するのか、employeesの 要素を指定するのか曖昧になる: employees.remove(x) // unclear: are we removing x?
Promote Clear Usage• 不要な単語は省略する 余剰な情報は省略すべき。Elementを管理するオブジェクトのElementを削除するメソッドに対して、メソッド名にElementを含めるのは余剰な情報である:
public mutating func removeElement(member: Element) -> Element?
allViews.removeElement(cancelButton)
以下のようにするのがよい: public mutating func remove(member: Element) -> Element?
allViews.remove(cancelButton) // clearer
Promote Clear Usage• パラメータには役割を明確にするための補足情報を追加するNSObject、Any、AnyObjectや基本的な型(IntやString)のパラメータは役割を明確にした名前をつける:
func add(observer: NSObject, for keyPath: String)
grid.add(self, for: graphics) // vague
以下のように、selfはObserver、StringはkeyPathとして使用することを明示する: func addObserver(_ observer: NSObject, forKeyPath path: String)
grid.addObserver(self, forKeyPath: graphics) // clear
Be Grammatical
• mutatingメソッドには動詞句を使用する
• non-mutatingメソッドには名詞句を使用する
• mutatingメソッドが動詞句の場合、対応する non-mutatingメソッドには「ed/ing」をつける
Be Grammatical• non-mutatingなBooleanメソッド、プロパティは以下のようにする x.isEmpty, line1.intersects(line2)
• Protocolは特徴を名詞で記述し、接尾語に「able」、「ible」、「ing」をつける
• その他の変数、プロパティ、定数は名詞で記述する
Use Terminology Well
• 略語は避ける
• 先例を受け入れる連続したデータ構造ではListよりもArrayと名前につける(Arrayはモダン・コンピューティングの基礎であり、すべてのプログラマが知っている)
Agenda• 2015-12-03版APIガイドラインの紹介
• Fundamentals: 基礎
• Naming: 命名
• Conventions: 規約
• Special instructions: 特記事項
General Conventions• O(1)ではない計算型プロパティの複雑さは文章化
• フリー関数よりメソッドやプロパティを選択する
• ケース(大文字・小文字)の規約に従う
• 同じ基本的な意味を共有するとき、メソッドはベース名を共有できる
Free Functions• フリー関数は特別な場合のみ使用される:
1.明白な“self”がないとき min(x, y, z)
2.関数が制約なく汎用的なとき print(x)
3.関数の構文が確立されたドメイン表記の一部のとき sin(x)
Parameters• デフォルト引数を利用する
• メソッドファミリーの利用より一般的に好ましい
• デフォルトを持つパラメータは最後の方に置く
• 引数ラベルの存在は言語のデフォルト設定に従う
例外:
1.拡張型変換と見られるべきであるイニシャライザ
2.すべてのパラメータが有効に区別できないピアのとき
3.最初の引数がデフォルト設定されたとき
Argument Labels• メソッドや関数の最初の引数は必須の引数ラベルを持つべきではない
• メソッドや関数の他のパラメータは必須の引数ラベルを持つべきである
• イニシャライザのすべてのパラメータは必須の引数ラベルを持つべきである
Exception 1• 拡張型変換(widening type conversions):元が取り得るすべての値を許容可能な型への変換
• 縮小型変換(narrowing type conversions): 値の一部を保持できない型への変換 extension UInt32 { init(_ value: Int16) // widening, so no label init(truncating bits: UInt64) // narrowing init(saturating value: UInt64) // narrowing }
Exception 3• 最初の引数がデフォルト設定されたときの例
extension Document { func close(completionHandler completion: ((Bool) -> Void)? = nil) } doc1.close() doc2.close(completionHandler: app.quit)
extension Document { func close(completion: ((Bool) -> Void)? = nil) } doc.close(app.quit) // Closing the quit function?
extension Document { func closeWithCompletionHandler(completion: ((Bool) -> Void)? = nil) } doc.closeWithCompletionHandler() // What completion handler?
Agenda• 2015-12-03版APIガイドラインの紹介
• Fundamentals: 基礎
• Naming: 命名
• Conventions: 規約
• Special instructions: 特記事項
Special Instructions• 制約のない多態性に細心の注意を払うこと
• Any、AnyObjectと制約のないジェネリックパラメータ
• ドキュメントコメントツールフレンドリーにする
• リッチなフォーマットのドキュメントが自動生成
• Xcodeで表示される
• 優れた要約を記述することがより重要である
Unconstrained Polymorphism• 例えば、以下のオーバーロードのセットを考える:
struct Array { /// Inserts `newElement` at `self.endIndex`. public mutating func append(newElement: Element)
/// Inserts the contents of `newElements`, in order, at /// `self.endIndex`. public mutating func append< S : SequenceType where S.Generator.Element == Element >(newElements: S) }
ElementがAnyのとき、曖昧になる(単一のElementがElementのシーケンスと同じ型扱い) var values: [Any] = [1, "a"] values.append([2, 3, 4]) // [1, "a", [2, 3, 4]] or [1, "a", 2, 3, 4]?
Unconstrained Polymorphism
• 曖昧さを排除するために2番目のオーバーロードをより明確に命名する: struct Array { /// Inserts `newElement` at `self.endIndex`. public mutating func append(newElement: Element)
/// Inserts the contents of `newElements`, in order, at /// `self.endIndex`. public mutating func appendContentsOf< S : SequenceType where S.Generator.Element == Element >(newElements: S) }
Conclusion• Swift 3 API Guidelines Review
• GitHubで公開
• Swift 3 API Guidelinesの成果の一部
• Swift 3インポータ自動適用結果が見れる
➡ Swift 2.xでもある程度意識しながらコードを書いた方がよい
• ただし、ガイドラインはあくまでガイドライン(指針)
• 細かな部分はプロジェクトやグループで決めが必要
• ガイドライン自体がGitHub上に欲しい