AdventCalendar
iOS
Feedback
Swift
dogfooding
iOSDay 10

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

More than 1 year has passed since last update.

iOS Advent Calendar 2016 10日目のかっくんです。

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

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

チームで開発しているアプリで社内の人に「ここバグってるんだけど」と言われて言われた動作をしてみても再現しない。
同じOS、同じ種類の端末、アプリのバージョンも同じ、、なのに再現しない。(ビルド番号は異なります)

そんな事無いですか?
せめて誰が変更した物なのかが分かれば原因が突き止められるかもしれない!
弊社の場合、プロジェクト毎にGitのブランチが違うのでブランチ名が大きなヒントになります。

という事でアプリ内にGitのブランチ名を表示してみましょう。
既にXcodeのプロジェクトがある前提で進めます。

まずプロジェクトのトップに Branch.plist を作成します。

20161016170521.png

次にスキームを編集します。

20161016150618.png

BuildPre-actions から+ をクリックして New Run Script Action を選択します。

20161016165744.png

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

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

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

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

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 にサンプルをアップしてありますので見て頂ければと思います。

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

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

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

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
  }
}

次に画面を撮影します。

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にアップしています。
Carthageにも対応しているので気軽に試せるかと思います。

github "fromkk/SlackFeedback"

FeedbackSlack

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

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

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


まとめ

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