try! Swift Tokyo 2017 hackasonに参加しました

try! Swift Tokyo 2017 hackasonに参加しました

name_holder

今年も福岡からtry! Swiftに参加しました。
去年に引き続き@niwatakoさんがセッションの聞き起こしをして下さっていますので
セッション内容についてはそちらを参照してください。


今年は3日目にハッカソンがあったので、どんなものなのか興味もあり参加しました。
ただ夕方に離脱する可能性があったのでソロ参加です。

アイデアソン

2日目にDerek Leeさんのチームの生産性を改善するために決断疲れを最小化するを聞いていたので
Xcodeユーザーの生産性を少しだけ改善するものをテーマにしました。

作ったもの

実装する必要があるProtocolのメソッドを補完するXcode Source Editor Extension。
継承関係を解析してカーソルがある位置に必要なメソッドを補完します。

GitHub: XcodeSourceEditorExtension-ProtocolImplementation

demo

2つコマンドを作りました。

  • from Completion: 補完リストを使って補完する
  • from Snippet: スニペットを使って補完する(要設定)

例えばUITableViewDataSourceを継承したコードで、

import UIKit

class ViewController: UIViewController {
}

extension ViewController: UITableViewDataSource {
// ここにカーソルがある状態でExtensionを実行
}

ワンアクションで必要なメソッドを展開できるようになります。

import UIKit

class ViewController: UIViewController {
}

extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
<#code#>
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
<#code#>
}
}

キーバインド設定でショートカットキーを割り当てれば、
スニペットを選択して展開する場合と比べると1秒ぐらいは短縮できるんじゃないでしょうか。

from Completionコマンドについて、本当はRequiredなメソッドのみ展開したかったんですが、
今のところoptionalメソッド含めて全メソッドが展開されてしまいます😢

その他、正しく動かないケースもあると思うので改修したいですが、
とりあえず当日の状態のまま晒します。

どうやっているのか

SourceKittenFrameworkを使用して継承関係と補完データを取得して
カーソルがあるスコープに必要なメソッドを出力しているだけです。

これだけですが、SourceKittenを使ったことが無かったのでハマりました。

ハマったポイント

ExtensionでSourceKittenが使えない

そもそもExtensionはApp Sandboxが有効になってるいるため、
SourceKittenFrameworkが使えませんでした。
最初から詰んだかと思いましたが、最終的にXPC Serviceを経由することで回避できました。

ハッカソン中、下記4パターンを試行錯誤したのでメモしておきます。

  1. Processsourcekittenコマンドを叩く
    => ×:ダメでした。
  2. ホストアプリでSourceKittenを使用してApp GroupでExtensionと結果を共有する
    => △:ホストアプリを常時立ち上げておかないといけないのでつらい。
  3. そもそもExtensionをやめてプラグインにする(選択可能な補完リストも出せるし)
    => ×:プラグインを使えるようにしてみたらXcodeが死にましたXO 調べる時間が無かったので断念。
  4. デーモンを作ってプロセス間通信する
    => ○:XPC Serviceを経由させたらいけました。

翌日ググったらSourceKittenのIssueがあっさり見つかりました。XPC Serviceを使うのが正解のようです。

補完されたメソッドがoptionalなのか分からない

メソッド一覧はすぐ取れましたが、optionalなメソッドなのかを判別できる情報が含まれていないようでした。
これが判別できないと全てのメソッドが補完されてしまいます。

すぐにできる解決方法を思いつかなかったので、
スニペットを利用できるfrom Snippetコマンドを追加しました。
それに付随してプロトコルと対応するスニペットを設定できるようにする必要があったので、
今回は簡易的にホストアプリのウィンドウにテキストフィールドを置いて
スニペットの情報をJSON形式で入力できるようにしました。

{
    "Directory": "/Users/tid/Library/Developer/Xcode/UserData/CodeSnippets",
    "Snippets": [
        { "key": "UIViewController", "path": "swift-uiviewcontrollerlifecycle"},
        { "key": "UITableViewDelegate", "path": "swift-uitableviewdelegate"},
        { "key": "UITableViewDataSource", "path":  "swift-uitableviewdatasource"},
        { "key": "UICollectionViewDataSource", "path":  "swift-uicollectionviewdatasource"}
    ]
}

Extensionから使える整形機能がない

Xcodeでスニペットを挿入した時に自動整形されるので
何か方法があるんじゃないかと予想していたんですが見つかりませんでした。
諦めてそれっぽくなるようにインデントを入れています。

まとめ

ハッカソン初参加で得た教訓はこんなところでしょうか。

  • 可能な限り事前準備(アイデア出しと調査)をやっておく
  • 使ったことがないものは使わない

体験しないと分からないこともあり参加してよかったです。
残念ながら途中で離脱することになったので審査を辞退するためDevPostの登録を取り下げた(つもりだった)んですが
今見たらプロジェクトだけ残っていました。すみません🙇🏻

記録としてプロジェクトのリンクを残しておきます。
Xcode Source Editor Extension

次はチームを組んで発表までしたいところですが、福岡だとオープンなハッカソンが少ないので来年になりそうです。