ドッグフーディング用機能開発のススメ

[iOS Advent Calendar 2016](http://qiita.com/advent-calendar/2016/ios) 10日目の[かっくん](http://qiita.com/fromkk)です。

皆さんドッグフーディングしてますか？
とは言うものの僕も今年の[iOSDC](https://iosdc.jp/2016/)で[Yahoo! Japanの西さんの発表](https://iosdc.jp/2016/c/node/133)で感銘を受けでから意識する様になりました。
社内の人に開発中のアプリを如何に使ってもらうか、どうアプリを改善していくかをトライしているのでその内容を共有出来ればと思います。

# アプリの中にブランチ名を表示する

チームで開発しているアプリで社内の人に「ここバグってるんだけど」と言われて言われた動作をしてみても再現しない。
同じOS、同じ種類の端末、アプリのバージョンも同じ、、なのに再現しない。(ビルド番号は異なります)  
そんな事無いですか？
せめて誰が変更した物なのかが分かれば原因が突き止められるかもしれない！
弊社の場合、プロジェクト毎にGitのブランチが違うのでブランチ名が大きなヒントになります。

という事でアプリ内にGitのブランチ名を表示してみましょう。
既にXcodeのプロジェクトがある前提で進めます。  
まずプロジェクトのトップに **Branch.plist** を作成します。
  
![20161016170521.png](https://qiita-image-store.s3.amazonaws.com/0/2846/4086f06a-63ab-8db3-6923-3fb24f637f04.png)


次にスキームを編集します。  
![20161016150618.png](https://qiita-image-store.s3.amazonaws.com/0/2846/ca582794-8e60-2ac8-8853-b3a467266a6f.png)


**Build** の **Pre-actions** から**+** をクリックして **New Run Script Action** を選択します。  
![20161016165744.png](https://qiita-image-store.s3.amazonaws.com/0/2846/c097871c-729d-b75c-0017-e17840574085.png)


**Provide build settings from** をアプリのターゲットを選択し、**Type a script or drag a script file from your workspace to insert its path** と表示されている場所に下記のコードを貼り付けます。  

```bash
branch="master"
if [ ! -z "${CIRCLE_BRANCH+x}" ]; then
    branch=$CIRCLE_BRANCH
elif [ ! -z "${TRAVIS_BRANCH+x}" ]; then
    branch=$TRAVIS_BRANCH
elif [ ! -z "${BITRISE_GIT_BRANCH+x}" ]; then
    branch=$BITRISE_GIT_BRANCH
else
    git_path=$(which git)
    if [ -e $git_path ]; then
        branch=$($git_path --git-dir=$PROJECT_DIR/.git rev-parse --abbrev-ref HEAD)
    fi
fi

plistName=$PROJECT_DIR/Branch
plistPath=$plistName.plist
if [ ! -e $plistPath ]; then
    touch $plistPath
fi
chmod u+wr,g+wr,o+r $plistPath
defaults write $plistName "branch" $branch
```

![20161016170147.png](https://qiita-image-store.s3.amazonaws.com/0/2846/07568cc9-717c-bc27-954b-d088676e076a.png)


これでプロジェクトをビルドすると **Branch.plist** の **branch** キーの値が現在のブランチ名になっているかと思います。
後はこの値をアプリ内に表示すればビルドした環境のブランチ名が分かるので誰が書いたコードが影響してるのかはっきりするはずです！

ブランチ名を取得するサンプルもあるので記述しておきます。

```swift
struct Branch {
    private enum Constants {
        static let defaultBranch: String = "master"
        static let plistFileName: String = "Branch"
        static let branchNameKey: String = "branch"
    }

    static var current: String {
        guard let path: String = Bundle.main.path(forResource: Constants.plistFileName, ofType: "plist") else {
            return Constants.defaultBranch
        }
        guard let plist: [String: String] = NSDictionary(contentsOfFile: path) as? [String: String] else {
            return Constants.defaultBranch
        }
        return plist[Constants.branchNameKey] ?? Constants.defaultBranch
    }
}

//usage
let branchName: String = Branch.current
```

[Github](https://github.com/fromkk/GitBranchName) にサンプルをアップしてありますので見て頂ければと思います。

# フィードバック機能を作成する

こちらはiOSDCで発表されたものの完全なるオマージュです（笑）
[西さんもFeedbackKit](https://github.com/nishimao/FeedbackKit)を公開していますが、Slackに投稿したい時はSlackKitの利用が必要になります。
意外とこのSlackKitの容量が重く、気軽にアプリに組み込むにはハードルが高かったので自分で作成しました。
テストユーザーが開発者にフィードバックする際に一番良くする事ってなんだろうって思った時にスクリーンショットを撮影する事だと思ったので、スクリーンショットを撮影した事を検知するとフィードバック機能が有効になる仕組みを作成しました。

スクリーンショットを撮影した時のNotificationが用意されているのでそちらを利用します。

```swift
class FeedbackSlack: NSObject {
  override init() {
    NotificationCenter.default.addObserver(self, selector: #selector(self.screenshotNotification(_:)), name: NSNotification.Name.UIApplicationUserDidTakeScreenshot, object: nil)
  }

  func screenshotNotification(_ notification: Notification) {
    //TODO: take screen shot
  }
}
```

次に画面を撮影します。  

```swift
guard let window: UIWindow = UIApplication.shared.delegate?.window! else {
    return
}
UIGraphicsBeginImageContextWithOptions(window.bounds.size, false, 0.0)
window.drawHierarchy(in: window.bounds, afterScreenUpdates: true)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
```

この `UIImage` をSlack APIに `POST` すれば完成です。  
処理としては単純ですが、UIの作成とか結構面倒くさいので一通りの処理を書いたものを[Github](https://github.com/fromkk/SlackFeedback)にアップしています。
Carthageにも対応しているので気軽に試せるかと思います。

```Cartfile
github "fromkk/SlackFeedback"
```

![FeedbackSlack](https://github.com/fromkk/SlackFeedback/blob/master/feedbackslack.gif?raw=true)

社内ではこれを開発用のアプリにのみ有効になる様に利用しています。

```swift
#if DEBUG_APP
FeedbackSlack.setup("YOUR_SLACK_TOKEN", slackChannel: "#your_slack_chennel")
#endif
```

気に入って頂けたらスター下さい（笑）

---

# まとめ

これらを導入する事でバグが発生した時の原因究明の役に立ったり、UIが崩れている時にも複雑な事をしなくても気軽にくフィードバックが出来る様になりました！
ただ、如何にしてアプリをよく使ってもらえるかという点ではまだ課題があります。
今後そういった所も改善してアプリをより良い物にしていけたらいいなと思います！