クックパッドマートiOSアプリを楽しく新規開発した話【連載:クックパッドマート開発の裏側 vol.2】

こんにちは。
連載シリーズ2日目を担当します、クックパッド買物事業部 iOSアプリエンジニアの中山(@LimiterJP)です。
早いもので入社して一年が経ちました。

私は去年4月にクックパッドへ入社しました。
その後6月にアプリ開発を始め2018年9月に「クックパッドマート」のiOSアプリをリリースしました🎉

クックパッドマートは
生鮮食品をスマホアプリから簡単に注文することができる生鮮食品ECサービスです。
従来のネットスーパーや生鮮宅配サービスとは異なり、街の精肉店や鮮魚店などの販売店や地域の農家といった生産者など、小規模事業者や個人事業主が参加できるプラットフォームです。

今日はクックパッドマートのiOSアプリの立ち上げを爆速で行った舞台裏について、

  • 新規開発で大切にしたこと
  • 開発速度を上げるための考え方
  • 新規開発に特化した具体的な手法

の観点でお話します。

新規開発で大切にしたこと

ここでは、私が普段から新規開発を行う際に心がけていたことをいくつか紹介します。

開発スピードは大切

開発スピードは早ければ早いほど失敗から機能改善・成功までの速度も上がると考えます。

「ユーザーが実際にアプリを使用し、内容に共鳴・共感しリテンションを保てるか?」
という仮説検証を行う際、
「実際にアプリをリリースし、ユーザーが使用して操作してみないとわからない」
ということを認識する必要があります。

ユーザーがアプリをダウンロードしたときの印象にはレベルが存在すると考えます。
レベル1 使えない・よくわからないアプリ
レベル2 何に使うのか?を理解できる内容のアプリ
レベル3 なかなか使えるので使いつづけようと感じるアプリ
レベル4 神アプリ!シェアしよう!拡めたいと思うほどのお気に入りのアプリ

f:id:degikids:20190408134227p:plain

そのうち最低でもレベル3以上を目指さないと結果を出すことは難しいでしょう。
そして瞬間的にレベル4でも使い続けてもらえない内容だと別の問題にも悩むことになります。
アプリ開発って本当に難しい。

アプリリリース前にしっかり使用するシーン・ストーリー・コンセプト・カスタマージャーマップを組み立て入念な企画を立案します。
デザインを当ててみて機能を設計するなど頭の中でアプリを開発する。
ここまでの工程を私達のチームでは「論理できた」と呼んでいます。

ところがこの「論理できた」の状態でいざ実際にリリースしてみると
「実際のユーザーが継続的に使用することはなくニーズがずれて失敗する」
ことが多いのではないでしょうか?

これらは機能単位で起こっているなんてこともあります。
時間をかけて作った機能が使われないなんてことよくありますよね。

そのため、速く改善してレベル3, 4を目指せる状態を作ることでリテンション(継続率)を高められる状態になると考えます。

スピード重視とはいえ、雑に実装してバグを出してでもスピードを求めるとか
「コードレビューを全く行わないぞ、テストコードを書かないぞ!」とかそういうことではありません。
行う箇所を選定・判断することが大切です。
大切なのは効率化して工夫できることの選択肢を増やし開発スピードを向上させることです。
(後述する「開発速度を上げるための考え方」など)

チームメンバーとよく笑いよく話す

クックパッドマートのチームでは、基本的に誰かが面白いことを言っている現場なので笑いが絶えません。 (誰か反応するまで喋り続けるスタイルです!)

デザイナー、エンジニア、ディレクターなど職域を超えた人同士の雑談も多く、 Slackでやり取りすれば良いようなこともあえて喋るように、人間関係も良好で良いチームだと感じています。
そのおかげか、メンバーがどういう気持ちで仕事に向き合っているか、何を考えているのかも知ることができています。 業務で疑問に思ったことは身近な人に聞けば一瞬で解決することも多く、様々な仕事が円滑に進むので、会社に来ることに楽しさも感じます。

「普段から高い頻度で雑談ができるチームは強い。」 その結果、開発速度も改善速度も上がり、品質も高くなると考えています。   

圧倒的当事者意識

「圧倒的」とつけるとなんだかすごい感じがするのでつけてみたのですが当事者意識は非常に大切です。
自分が作っているサービスを使い倒す。使わないと問題点や改善点は理解できないですよね。
クックパッドマートで販売されているパンはとても美味しいのです🍞
エンジニア自身がアプリの課題を把握することで、自分が使いやすくするためには?という意識が自然に芽生えるものです。
よってアプリをしっかり普段使いすることは非常に重要です。

開発速度を上げるための考え方

ここでは、具体的にどのように開発速度を上げていくか、私なりの考え方について紹介します。

パレートの法則(2:8の法則)で物事を考える

パレートの法則とは全体の数値の大部分は全体を構成するうちの一部の要素が生み出しているという理論です。

例えば「アプリ利用者のうち8割は、全機能の2割しか使わない」とすると、

  • すべての機能のうち2割の重要な機能に集中する
  • 2割のユーザーしか使わない機能はほどほどに作る

などのヒントが得られそうです。

何が正しいかはチームで決めると良いでしょうし、実際にそのまま採用するのではなく頭で考える作業をします。 明らかに仕様頻度が少なく重要度の低いものに時間をかけない選択やphase分けをして最初のリリース時には実装しないなどの判断をすればいいと思います。

パレートの法則に当てはめることで、

  • 開発の優先順位をつけることでリリースまでの最短距離の見通しが良くなる
  • なにか取り掛かる際にこれらを意識することで開発スピードが目に見えて上がる

などの効果があると考えています。 やる事とやらない事をはっきりさせ、見通しをよくする事が大切ですね。

何事もphaseで分けた考え方を持つ

なぜならはじめから大きなものを作ろうとすると疲弊し、その他がおろそかになる可能性があります。
「段階を経て完成を目指す」というのでしょうか。
この時点ではその「作ろうとしているもの」がユーザーに受け入れられるかはわかりません。

そこで比較的工数がかかる実装はphaseで分けて考える場面があると思います。 具体的には複数の緯度経度と場所情報の情報を持つリスト構造の情報があったとします。

リスト構造の情報を地図上にピンが立てタップし直感的に俯瞰できると見やすいでしょう。 しかしリリース序盤だと受け取り場所の情報が少なすぎて 逆に見づらい上に寂しい感じもします。

f:id:degikids:20190408134619p:plain

そこでリリース時はただのリスト構造からタップして選択するUIを選択し、ロケーションが増えたらMapからピンをタップして選択するUIへ再構築するという判断が生まれます。

新規開発に特化した具体的な手法

ここでは、iOSアプリで新規開発を行う際の具体的な手法について紹介します。

同じコードを極力書かないようにコードスニペットは磨いておく

高い頻度で使用するコード記述をスニペットに登録しておけばいちいちGoogle検索したり、昔書いたソースコードを探してみたりすることなく、 使いたい時に正確な記述をサッと呼び出して使うことが可能です。

存在は知っていてもあまり使用されていないのが現実です。 私はどの言語を書くときでもIDEに付属しているコードスニペットを活用しています。

f:id:degikids:20190408142542p:plain

コードスニペットは定期的に磨いておくと開発効率が抜群に上がります。
iOSではXcodeのsnippetを活用しおり開発をしているとこれらは何度も登場します。

  • TableView, CollectionViewの最低限動くdelegateメソッド一式
  • GoogleMapの最低限動くdelegateメソッド一式
  • 地図からルート探索
  • 緯度経度のリスト構造と現在の位置情報からdistanceが近い順に並べ替え
  • UIActivityやShareなどのイベントハンドラ
  • 位置情報取得(パーミッションdelegate含む)
  • アラートやフルスクリーンの透過ダイアログ・モーダルウィンドウ各種
  • WebView・SafariViewの設置
  • チュートリアルなどのスライドをcollectionViewのpagingを使用して実装
  • カメラ起動 最低限動くdelegateメソッド一式
  • ライブラリから写真選択、アルバムから画像抽出
  • プッシュ通知の実装
  • GCD各種

登録時はCompletion Scopes でしっかり分類しcompletion shortcut は検索性を保った名称設計を心がけます。 たったこれだけでも、手を抜いてしまうと開発効率は落ちます。

人にもよりますが私の場合すべてのsnippet の completion shortcut prefixにsw_をつけています。(sw_はswiftを表していますObjective-C時代からの歴史的経緯もあります)

sw_ から始まるものはすべてsnippetであるという分類ルールを持ち通常の補完と区別しやすいようにします。

f:id:degikids:20190408142347p:plain

普段SwiftでXcodeを使用してコーディングする時は

sw_xxxx で補完

または

 command + shift + 「l」

検索

↑↓cursor

Enter

の順でコードを書いていきます。

その結果、通常のコード補完よりも早く正確に書けるようになります。 初見のコードも一旦雑に書き、使い回せるようにコードレビューを繰り返し、磨き上がったらsnippetに丁寧に放り込みます。

チーム開発するときは
~/Library/Developer/Xcode/UserData/CodeSnippets を共有しておくととても便利です。

例えばアプリ上でGoogleMapを設置しピンを立てて位置情報取得して画面でみて見ましょうか?という場面があったとします。 プロトタイピングツールでは難しい地図のモック作成の場面でも短時間で実装しディレクターやデザイナーと実際に地図を動かし良い悪いの議論することができます。この時点でプロトタイピングツールすら必要なく開発を進めることができます。

Xcodeのプロジェクトテンプレートを磨いておくと結果的にすべての開発効率が向上する

私はxcodeのプロジェクトテンプレートを利用しています。(具体的な用意の仕方はここでは割愛します)

ですが単純に自分の雛形プロジェクトテンプレートを用意しておけば良いと思っています。(新規プロジェクトで雛形アプリを作成)

以下のような機能を雛形の中に含めています。

  • 設定画面
  • TabBarController
  • お問い合わせ(WebView or SafariView)
  • アプリの使い方(WebView or SafariView)
  • プッシュ通知
  • 利用規約(同意の機構)
  • プライバシーポリシー
  • アプリバージョンの表記
  • ライセンスの表記
  • レビュー催促の仕組み
  • ログイン画面
  • Cloud FirestoreのCRUD

その他たくさん

まず上記を含んだ雛形のプロジェクトを複製し、その案件で必要ない機能は削除していきます。
上記の実装がXcodeから新規作成したときに既に実装されていたらどうでしょうか?
それはもう「強くてニューゲーム」です。

f:id:degikids:20190408140326p:plain

最初のスタートダッシュで大きな差がつきます。

Storyboardベースでつくる

Xcode6以前はコードベースで進めるほうが効率的でしたが、
iOS9.0以降からはStoryboardの分割が容易になりコードベースで作るより圧倒的に開発が楽になりました。

Storyboard Reference の登場によりファイルの分割や関連付けも容易に。
以前と比べてConflictの可能性も低くくなったと感じます。

f:id:degikids:20190408140945p:plain

Storyboardを活用しデザイン確認が行えると開発が円滑に進む場面が多いように思えます。
例えばちょっとしたボタンの位置、配色などデザイナーと机を合わせて確認・議論ができます。簡単なモックもコードを汚さず作ることが可能です。

SwiftGenを活用する

SwiftGenとはXcodeで使用される画像イメージ、フォント、 カラー、segue等のリソース名を自動的に生成し型付を行ってくれるライブラリです。 https://github.com/SwiftGen/SwiftGen

Storyboardの遷移はコードで行っています。
理由として再利用率が高い画面を切り離して考えられるからです。

簡易的な値渡し例えばWebViewで開く情報を segue で渡すとシンプルかつ直感的に実現できます。

画面遷移では performSegue を使用しており、prepere で値渡しを行っています。
そのためにはSegueIDをStoryboardから設定する必要がありこれを手作業で管理するのは厳しい。
Segueのデメリットは「いろんな場所に設定が散らばっていて情報が隠れやすく流用がしづらい」ということだと思います。
コードからSegueIDやStoryboardを利用しようとした場合に、補完が効かないためハードコードをするか、手動で管理をする必要があります。

そこでSwiftGenの登場です。
簡単にStoryboardの名前からStoryboardのSegueのIDで名まで自動で生成し型付してくれます。

f:id:degikids:20190408142658p:plain

buildしなくても角丸とかボーダーなどを確認できるようになります。

class CustomView: UIView {
    @IBInspectable var customBool: Bool = false
    @IBInspectable var customInt: Int  = 0
    @IBInspectable var customFloat: CGFloat = 0.0
    @IBInspectable var customDouble: Double = 0.0
    @IBInspectable var customString: String = ""
    @IBInspectable var customPoint: CGPoint = CGPointZero
    @IBInspectable var customSize: CGSize = CGSizeZero
    @IBInspectable var customRect: CGRect = CGRectZero
    @IBInspectable var customColor: UIColor = UIColor.clearColor()
    @IBInspectable var customImage: UIImage = UIImage()
}

まとめ

クックパッドマートのiOSアプリはまだまだ発展途上で完成はしていません。
現在もコツコツと開発を進めており日を重ねるごとに利便性は向上しています。
使用できるエリアの皆さんは是非使っていただけると幸いです。ありがとうございます!
この記事を通して、クックパッドマートのサービス開発にご興味を持っていただけた方がいらっしゃいましたら、ぜひ一緒にサービスを作りましょう!

www.wantedly.com

お知らせ

クックパッドマートでは、4/24(木)に、買物事業部のエンジニアによる発表とエンジニアとのミートアップを開催予定です。

cookpad.connpass.com

今回の記事のような生鮮ECそのものの仕組みや、流通の仕組みの開発に興味がある方、クックパッドマートのエンジニアと直接話してみたい方はぜひご応募ください!お待ちしています!

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/