LocoPartners 開発ブログ

LocoPartners 開発者達によるブログです

【重要だけど緊急度が低いタスクに取り組む日】CLEAR@5回目を開催しました

古田です。

すっかり秋ですね。
今年も残り3ヶ月を切っているとか、時が過ぎるの早すぎ。
と毎年思ってる気がします。
残りの日々も精一杯がんばります。

本題ですが、先日(10/13)CLEARを実施しました。
CLEARについての説明はこちら

loco-partners.hateblo.jp

毎月1回のペースで続けてますが、早くも5回目。
今回もエンジニア自身でピックアップした課題に取り組みました。

作業風景
移転したばかりの綺麗なオフィスでもくもくもく。 f:id:loco-dev:20161013195302j:plain

ランチタイム
CLEARの日のお昼代は会社が負担するのですが、
好きなの食べていいのになぜか毎回ハンバーガー。。w
f:id:loco-dev:20161013195330j:plain

で何したか

こんな感じでリラックスしながら取り組んでいるCLEARなのですが、
今回は、せっかくなので取り組んだタスクの一部を紹介します。

KPIデータのグラフをSlackへ通知

by 三寳

現在もKPIをSlackに通知する仕組みは存在するのですが、
Reluxのシステムの中に組み込まれており、意図せずReluxに
バグを仕込んでしまうリスクがあります。

今回のKPI通知は、RedashGoogleスプレッドシート
GoogleAppsScriptを連携させて作りました。
いじるのはReluxとは独立した外部サービスのみで、
Relux本体には何の影響も与えない仕組みです。

Redashはクエリを作成すると自動的にエンドポイントを生成
してくれるので、そこからクエリ結果をJSONで取得しています。
そのエンドポイントをGoogleAppsScriptで叩いて、
スプレッドシートにデータを書き込む。
シートの方には集計関数とグラフを作成しておいて、
更新したデータで再描画して、Slackにグラフ画像をPOSTしています。

f:id:loco-dev:20161013205049p:plain

Firebaseを導入してみる

by 赤池

AndroidアプリにFirebaseのAnalyticsRemoteConfigを仕込みました。
SDKの導入は簡単!
ただ、弊社アプリはproductFlavorsで開発用アプリの
パッケージ名を切り替えられる用にしているので、
本番/開発用それぞれアプリを登録する必要がありました。

Analyticsはイベントトラッキング・アプリバージョン・デバイスシェア等、
アプリ分析に必要な最低限の情報が取れます。
GoogleAnalyticsとは違いリアルタイムのユーザー数などは見れませんが、
余計な機能は少なくざっと数字をチェックするには十分という印象。
初回起動数と収益金額を元に算出されたLTVが表示されるのも良いです。

RemoteConfigはアプリのABテスト等が簡単に仕込める機能で、
Firebaseのコンソール上で設定した値を条件に基づいてアプリ側で取得できます。
想像していたより実装に考慮が必要な面はありましたが、
こちらも手軽に導入できるので、
Firebase導入の際には試してみると良いと思います。

GoogleFormからBackLogの課題を作る。

by 北山

弊社では、他部署からエンジニアへの問い合わせをGoogleFormで行います。
その中にはバグ報告もあるため開発作業が発生する案件も。
その際にはタスク管理にBackLogを使っているためチケットを切ってから作業します。

しかし、チケットを切る事を漏れる時もしばしば、、
何とか仕組みで解決できないかと自動化することにしました。
やってみれば簡単で、GoogleFormが送信されたことをトリガーに GoogleAppsScriptよりBackLogAPI経由でチケット切るようにしました。

GoogleAppsScriptのメリットは、実行環境の準備が不要で
Googleのサービスとの連携も容易。
今後も何か新しい仕組みを始めようとする時に、
まずGoogleAppsScriptで実装できるか検討してみようと思います。

まとめ

これらのタスクはエンジニア自身で課題だと思うところを考え、
解決するためにはどんな解決策があるか検討/議論して取り組みます。
自部署やプロダクトだけでなく会社全体を見る視野を得られると共に
課題発見/解決の力も付く取り組みにもなってます。

次はどんな課題を解決してくれるのか、、毎回楽しみです^^

お知らせ!

弊社主催で以下のイベントを開催します。
移転したばかりの綺麗なオフィスなので 是非お気軽に遊びに来てください!

relux-mokumoku.connpass.com

kaizen.connpass.com

技術的負債への取り組み『CLEAR』とは

こんにちは、古田です。

最近はボルダリングやらランニング(Pokemon GO)を始めましたが、、
暑いですね。
夏に始めるもんじゃないと思いつつ継続するために奮闘中です。

さて、今回は我が部署で6月頃から始めた新たな取り組みをご紹介します。
その名も『CLEAR(クリア)』です。

サービスローンチ以降、スピードにこだわって機能開発を進めてきました。
1名のエンジニアが立ち上げたサービスも、
現在はアプリ含めて10名ほどの技術者・デザイナーで開発しています。
歴史と共に、当然ながら技術的負債も蓄積されてきました。

この負債による負担が、引換に得られるスピードをいよいよ越えてきたので、
通常の開発業務と切り離して時間を取ることにしました。

CLEARとは

1ヶ月に1日を丸っと使います。
約20日勤務のうちで1日なので5%ほどを投資していることになります。
この取り組みには2つの目的があります。

① 重要だが緊急度の低いタスクを推進

技術的負債が原因で影響範囲を把握しづらく不具合が発生しやすくなっています。
結果として必要以上に労力がかかったり不具合対応に追われるために本質的な開発時間が削られます。
また会社の規模が大きくなり他部署とのやりとり(問い合わせ対応やデータ出力など)も増えました。
それらに対して、リファクタリングや作業自動化を行うことで集中できる環境を自分たちの手で整えていくこと。
これが1つ目の目的です。

② 個人QCD管理能力の成長

1日という限られた時間の中で完結させるためにQCD管理が大事です。
事前の準備から正しい工数設定、また当日の集中力も含めて生産性を最大化する工夫を徹底して行います。
完了後には毎回KPTで振り返りながら運用フローも改善しています。
QCDへの意識付けとスキルアップ、これが2つ目の目的です。

このような背景から、
・働く環境を綺麗に整える
・成長するという目標を達成する
という意味を込めてCLEARと名付けられました。

ちなみに、、ロゴも作っちゃいました!

f:id:loco-dev:20160802094116p:plain

改善は1回で終わるものではない。
継続して繰り返し推進していく、という思いが込められています。

どんなことやってるか

事前準備

作業内容はメンバーが自分で決めます。
困っていること・開発効率を下げている要因を元に時間内で出来ることをピックアップします。
他部署からデータ修正の依頼が多ければ、それを機能化してしまうとか。
手動のテストに時間を取られているのであれば、E2Eテストを書くとか。
それらをタスク管理ツールのTrelloへ、タスクカードを作成していきます。
ここまでを1週間前に済ませておきます。

作業開始

開始時刻の少し前に集まって1人1分ずつ意気込みをアウトプット。 f:id:loco-dev:20160731171307j:plain そしてもくもく作業スタートです。 f:id:loco-dev:20160731171322j:plain 思い思いのスタイルで開発しています。

ランチ

お昼ご飯もみんなで取ります。
集中時間を少しでも確保するために出前を注文。 f:id:loco-dev:20160731171247j:plain たまたま最初に頼んだハンバーガーが人気で2回目も同じところに頼みましたw

そして、また各自作業へ没頭していきます。。。

作業終了

テストも込みでその日にやりきるため最後の方はバタバタしますが、、
無事に完了して締めの時間を少し取ります。 f:id:loco-dev:20160731171340j:plain また各自1分程度で振り返りをアウトプットして終了。
皆やりきった感でいっぱいです。(疲れ気味?)

頑張ったあとは、、、

飲み行きましたw f:id:loco-dev:20160731171621j:plain この日は美味しい串焼きをいただきました^^

おわりに

毎回、たった1日とは思えないほどのアウトプットがあります。
また各自が必要だと思ったことに取り組み課題解決していくため
モチベーションも上がるので本当に良い取り組みだなと思っています。

そんな現場主導の取り組みをしている開発体制に興味ある方、
気軽に遊びに来てください〜^^
CLEARについて、その他開発体制で工夫していることなど
是非いろいろなお話させてください。
以上!

Swift Library & Xcode Plugin

なんか最近あれですね。暑いですね。

こんばんは。LocoPartersエンジニアインターンの竜口です。 普段はサーバーサイドを担当していますが、 最近Swiftを触っているので 今回のブログでは、実際に使ってみて良かった SwiftのLibrary と XcodeのPlugin を紹介します。

目次

  • Swift
    • Alamofire
    • AlamofireImage
    • SwiftyJSON
    • Quick
    • Nimble
  • XCode
    • VVDocumenter-Xcode
    • Alcatraz

Swift

Alamofire

github.com

機能
  • HTTPネットワークライブラリ
  • HTTPリクエストを書きやすくする
  • 非同期通信
使い方
Alamofire.request(
    .POST, //HTTPメソッド
    "https://hoge.com",  //リクエストURL
    parameters: ["test": "hoge"], //パラメーター
    headers: ["Content-Type" : "application/json"] //ヘッダー
    ).response { (request, response, data, error) -> Void in
        //レスポンス
    }

AlamofireImage

github.com

機能
  • 非同期通信で画像取得
  • 画像のキャッシュ管理もしてくれる
  • 画像取得中のplaceHolderも設定可能
使い方
customCell.placeImg.af_setImageWithURL(
    NSURL(string: "https://hoge/image")!, // URL
    placeholderImage: nil, //プレースホルダーの画像を設定
    filter: nil //取得した画像の加工(角丸等)
)

SwiftyJSON

github.com

機能
  • JSONを扱いやすくする
  • Alamofireと連携して使える  → responseをJSON型で取得できる
使い方
{
    "count" : 2,
    "Hotels" : [
        {
            "Hotel" : {
                "name": "hogeHotel",
                "Price": 300000
            },
            "Hotel" : {
                "name": "hogeRyokan",
                "Price": 500000
            }
        }
    ],
}
let data = JSON(jsonData)
let hotels = data["Hotels"]

hotels.forEach{(id, hotel) in
    print(hotel["name"].string) // return "hogeHotel"
    print(hotel["price"].int)   // return 300000
}

Quick / Nimble

github.com

機能
使い方
let val = 3 + 2
it("is equal"){
    expect(val).to(equal(4)) //失敗 エラー文言:expected to equal <4>, got <5>
}
it("is equal"){
    expect(val).notTo(equal(4)) //成功
}

XCode

Alcatraz

alcatraz.io

機能
使い方

1.インストール

curl -fsSL https://raw.github.com/supermarin/Alcatraz/master/Scripts/install.sh | sh

2.Xcodeを再起動
3.Window > PackeageManager (cmd+shift+9) で表示

VVDocumenter-Xcode

github.com

機能
  • コメントを保管
  • 引数、返り値も自動で保管
使い方

/// と入力するだけで完了

https://camo.githubusercontent.com/58e452b57245cd79c2e59ac7926609be4dffbfd8/68747470733a2f2f7261772e6769746875622e636f6d2f6f6e65766361742f5656446f63756d656e7465722d58636f64652f6d61737465722f7676646f63756d656e7465722d73776966742e676966

Firebaseを軽い気持ちで触ってみたら使い勝手が予想を遥かに超えてきた話

こんにちは。Loco Partnersのエンジニア、鈴木です。

わたくし普段は弊社サービスReluxのサーバーサイドの開発を担当していますが、元々はiOSエンジニアでした。
かれこれ1年近くも実務でのアプリ開発から離れているので、iOSは話題のキャッチアップで精一杯…。
たまには手を動かそう、ということで、話題のFirebaseをさわってみました。

Firebaseとは?

そもそもFirebaseとはなんでしょう?
元々は米国で始まったmBaasであり、2014年にGoogleに買収されたことでその名が広く知れ渡りました。
また先日のGoogle I/Oにてバージョンアップを発表、その中には無料で利用できるプッシュ通知の提供もありました。
年始に同じくプッシュ通知サービスを提供するmBaasであるParseの終了アナウンスがあったため、代替サービスとして注目している人も多いのではないでしょうか。
そんな時勢も相まって、今もっとも注目を集めるmBaasと言っても過言ではありません。

何ができるの?

f:id:loco-dev:20160627162600p:plain Firebaseは実に多くの機能を提供しています。
Authentication、Cloud Messaging、Crash Reporting、Notificationsなどなど、どれも本来であればサーバー開発に大きな工数がかかるもの。
これらの機能が高水準で提供されており、かつ無料で利用できる枠も大きいときたら、個人デベロッパーも企業も試さない手はないでしょう。

Realtime Database

f:id:loco-dev:20160704152603p:plain 今回は数ある機能の中から Realtime Database を試してみました。
便利そう、面白そう、かつフルスクラッチで作ったらとても大変そう、という理由です。
Realtime Database という名前からお察しのことと思いますが、特徴をざっくり箇条書きにするとこんな感じです。

  • 端末から送信されたデータがクラウド上に保存される
  • クラウド上のDBを介して、複数の端末、複数のユーザー間でデータの同期が可能である
  • オフラインでもローカル上でデータ保存され、オンラインになったタイミングで自動でクラウド上にデータ保存される

特に便利なのが最後のオフライン時の挙動。これをフルスクラッチで実現しようと思ったらぞっとしますね…。
たしかGoogle AnalyticsSDKがこんな仕組みだったと思うのですが、同じ技術を用いているのでしょうか。

導入編

それでは導入から見ていきましょう。
まずは登録から。
Googleアカウントと連携できるので、1クリックで完了です。
f:id:loco-dev:20160704154918p:plain

コンソールはこんな感じ。
ほとばしるGoogle感。
f:id:loco-dev:20160704155129p:plain

その後DLしたplistをプロジェクトに追加。 f:id:loco-dev:20160704160506p:plain

最後にCocoapods経由で追加してあげれば導入完了です。簡単ですね。
ちなみにpodfileへの追加分はこんな感じ。

pod 'Firebase'
pod 'Firebase/Database'

作るアプリ

複数端末でのデータ同期、ときたらやはりチャットでしょうか。
サンプルとして簡易的なチャットアプリを作ってみます。
最先端のマテリアルデザインを取り入れた、最高にイケてるUIです。
わかっているので何も言わないでください。
f:id:loco-dev:20160704161411p:plain

ユーザー名とテキストを入れるだけの、簡素なものです。

ソースコード

では実装に入っていきましょう。
AppDelegateに以下を追記します。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [FIRApp configure];
    return YES;
}

ViewControllerは以下。

- (void)viewDidLoad {
    [super viewDidLoad];
    
    _nameTextField.delegate = self;
    _messageTextField.delegate = self;
    
    databaseRef = [[FIRDatabase database] reference];
    [databaseRef observeEventType:FIRDataEventTypeChildAdded withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
        NSString *name = [[snapshot.value objectForKey:@"name"] stringByAppendingString:@": "];
        NSString *message =[[snapshot.value objectForKey:@"message"] stringByAppendingString:@"\n"];
        _textView.text = [_textView.text stringByAppendingString:[name stringByAppendingString:message]];
    }];
}

- (IBAction)submitBtn:(id)sender {
    NSString *name = _nameTextField.text;
    NSString *message = _messageTextField.text;

    [databaseRef.childByAutoId setValue:@{@"name":name, @"message":message}];
    _messageTextField.text = @"";
}

- (BOOL)textFieldShouldReturn:(UITextField *)sender {
    
    [sender resignFirstResponder];
    return true;
}

これですべてです。

やっつけコードなのはご容赦いただくとして、やっていることはこれだけです。

  • DBの読み書きをするためのインスタンスを取得
  • DBの変更を監視、変更があった場合の処理をブロック構文で書く(この場合はテキストの追加)
  • 送信ボタンが押下されたら、記入者とメッセージ内容を保存する

DBの読み書きが本当にシンプルに記述できます。

実行結果

それでは実行結果を見てみましょう。
f:id:loco-dev:20160704165655p:plain

コンソールにこんな具合で、リアルタイムにデータが追加されていきます。
ちなみに編集や削除もこちらからできます。debug時にも便利ですね。

今回シミュレータと実機でチャットを試してみましたが、問題なくやりとりできました。
また一方の端末でネットワークの接続を切って更新、ネットワーク再接続、というテストをしてみたところ、期待通り再接続時にもう一方でテキスト内容が更新されました。
意識することなくこれが実現出来るということだけでも Realtime Database のすごさがわかっていただけると思います。

総括

いかがでしたか?
繰り返しになりますが、その導入の簡単さ、記述するソースコードの簡潔さ、オフライン時の挙動の便利さには目を見張るものがあります。
今回はサンプルということで簡素な使い方しかしていませんが、色々とアイディアが膨らむサービスです。
次回は他の機能も試していこうと思います。

Facebook Messengerのchatbotリリースを振り返って

こんにちは、Loco技術Gの古田です。

突然ですが、、、

relux公式Facebookページ(http://www.facebook.com/rlx.jp)のメッセンジャーで、

チャットボットによる旅館・ホテルの検索サービス「reluxトラベルボット」が開始!

(詳しくはこちらのプレスリリースをご覧ください)

http://m.me/rlx.jp こちらのリンクからお試しいただけます。

ということで、今回は開発に至る背景から苦労したところなどなど、

振り返りも兼ねてご紹介しようと思います。

はじまり

きっかけは社長の鶴の一声、、、ではなく。

弊社は新しい技術や仕組みにいち早く取り組もうとする文化があります。

だからFacebook Messenger Platformが公開されたなら、

その可能性を知るためにも Tryしてみよう!

という流れになるのは自然なことでした。

まずリリースをすることを決める。で実装を考えていかないと

これってリリースできるかなとか本当に必要あるのかとか

やらない言い訳はいくらでも出てきてしまいます。

時代の流れというか人々の中心にチャットがあることは明らかであり、

お客様とのコミュニケーションを大事にしている弊社にとっては

うまく活かしていきたいプラットフォームでした。

また、自分で作ろうと思った理由としては2つあり、

1つは元から自分自身でもAIには興味持っていたこと。

もう1つは、LINEなどchatbotの流れが来ているけれど、

具体的な活用イメージを描くためにも直接作りたいという点でした。

設計

他社の事例も参考にしながらユーザーストーリーを作りました。

既に良い事例がいくつも出ていたのでかなり参考になりました。

(参考例)
http://m.me/hiponcho
http://m.me/cnn
http://m.me/techcrunch
http://m.me/alterra.cc

設計で気を付けたことは3つです。

  • 盛り込み過ぎない。必要最低限のミニマムスタート。
  • 実用的か。ちゃんと使ってもらえるものか。
  • reluxらしい機能か。

これを元にまずはGoogleSlide5,6枚の簡単なものを作成しました。

今回はreluxの検索機能コンシェルジュへと繋げる機能としました。

実装

まずは足がかりとして、こちらのイベントへ参加。

connpass.com

最初は何をやっていいか全く分からず当日を迎えましたが、

雰囲気を察して、鸚鵡返しを作ると良さそうだと分かり実装開始。

色々なサイトを参考にしながらHerokuを活用して実現できました。

ここまでは意外とあっさり。

周りの参加者もお昼頃にはそこまで完了している様子。

次に、検索を実現するためにDBを参照する方法を検討。

既存のrelux自体はPHPで書いているので、

PHPでbotAPIを作成するという手もありました。

ただ、本体機能と切り離したい&アクセス数が読めない中で

そのためのリソースを確保するのも難しいと思い、

Lambda + API Gatewayで実現することにしました。

その勉強会ではLambda環境までいかずにタイムアップ。

続きは宿題となりました。

〜〜〜

Lambdaの導入、ここは意外とはまりました。

RDSを参照するためVPCにアクセスできるように設定しつつ、

Messengerへ投稿するために外部へ通信できる必要がありました。

そのためのNAT ゲートウェイをサブネットに設定するのですが、

通信できない状況が続きました。。

試行錯誤を繰り返し環境を構築。(方法はどこかで書けたら書きますw)

Lambda使って鸚鵡返しに成功したことを確認できました。

次はメッセージ内容の保存です。

検索機能ではchatやりとりの中で入力した条件を利用するので、

テキスト内容とメッセージの種類(日付・泊数・人数など)を

保存する先としてAWSのDynamoDBを選択しました。

これはLambdaで標準に使えるaws-sdkを使えば簡単に実装できます。

最後に検索条件に従って宿泊できる施設を検索する機能です。

これもSDKを使えばDBとの接続は難しくありません。

クエリを書く際にはセキュリティに配慮して実装しました。

途中気づいたのが写真付きのパネルは最大10個までという点です。

今回は施設を探すエリアをパネルから選択できるようにしたのですが、

本来relux上のエリア区分は12あります。10では全て選択できません。

統合も検討しましたが、まずはサクッと進めるために

取り扱い施設数が少ないエリアを除くという一時対応としました。

他にも難しいなと思ったのはチャットだから自由な表現ができる点。

同じ意味を伝えるのにも様々な表現がある。

かつ検索フローと関係なく、いつでも好きなテキストを打ててしまう。

全ての言葉に正しく反応することを考え始めると時間がかかるので

検索中それ以外とで状態を定義して、

それぞれに一定の文言を返す様にしました。

申請

Facebook開発者 - 開発者向けFacebook から申請を行います。

一連の操作が含まれている動画を用意する必要があります。

出したはいいのですが、最初の申請では2週間以上待たされました。

変だなと思っているところで社内の人間が他社に聞くと2日程度で完了したとのこと。

普通ではなさそうなので思い切って審査をキャンセルし再度申請したところ2日で通過。。

審査完了したのにOKボタンを押し忘れてたのかな。。。

2週間のロスは痛いですが、無事にリリースできました。

リリース反響

プレスリリース当日は凄まじい勢いで投稿数が増えました。

普段は月に数名というところ、初日で1000名を超える方からアクセスいただきました。

おかげで社内コンシェルジュも大慌て、嬉しい悲鳴でした。

実際やってみることで世の中の注目度を身をもって感じました。

おわりに

世の中の流れに乗るということのインパクトの大きさを感じると共に

初期に参加することによる反響の大きさ、その重要性を改めて実感。

これからサービスや組織が大きくなっても初動スピードは大事にしていきます。

そして、今回は本当にプレイヤーとして存分に楽しませてもらいました。

新しいことにtryするって良いですね〜

ということで、こんなスピード感でいろいろ挑戦したい!

というエンジニアの方がいれば、

ぜひ一度弊社に遊びに来てください。いつでもwelcomeです!

採用情報 | 株式会社Loco Partners

reluxのAndroidアプリコーディング規約

こんにちは。Androidアプリ開発を担当している赤池です。梅雨ですね。梅雨です。

弊社reluxのAndroidアプリは基本的にエンジニア一人体制で開発しています。 一人体制だとチーム開発によるコード統一性の乱れやコンフリクト等が生じない一方、他者にはわかりづいらい自分流の開発スタイルになりがちという問題があります。reluxのAndroidアプリのコードにも他者が見るとクエッションになりそうな箇所が見受けられたので(自分で言う!)、今後の複数人開発に備えコーディング規約の作成を進めています。

背景についての補足

話は変りまして、弊社開発グループでは月に一度「CLEAR」という日を設けております。 日頃なかなか時間を取ることができない品質改善のためのタスクを、この日は自分で好きに選んで行うことができます。 CLEARに関しては別のブログでも触れると思うので詳細は割愛しますが、コーディング規約はこの日のタスクの一つとして着手しました。

まずはじめに見るべきもの

みなさまAndroidアプリのコーディング規約はどうしていますか?独自に定めていますか? 実はAndroidアプリコーディング規約はGoogleがすでに制定しています。 こちらには「ガイドラインや推奨ではなく厳格な決まり」という記述があります。 AndroidのContributorにならないにせよ、従っておいて間違いないでしょう。

Code Style for Contributors | Android Open Source Project

また、GoogleJavaに関するスタイルも定めてます。 前者でカバーしていない部分はこちらを参考にします。

Google Java Style

規約の主な内容

reluxのサーバサイドのプログラムはPHPで書かれています。PHPのコーディング規約はPSRをベースにしているのですが、それとの違いを中心に規約内容をいくつかピックアップしてみます。

フィールド(メンバ変数)の命名規則
//非パブリック・非スタティックは頭にmを
private int mPrivate

//スタティックは頭にsを
private static MyClass sSingleton;

//定数は大文字と_のみ使用
public static final int SOME_CONSTANT = 42;
中括弧について
//中括弧で行を始めてはならない(改行しない)
class MyClass {
    int func() {
        if (something) {
            // ...
        } else if (somethingElse) {
            // ...
        } else {
            // ...
        }
    }
}
// 条件文には中括弧をつける
if (condition) {
      body();  
  }

// 一行で収まる場合はつけなくても構わない
if (condition) body(); 

// これはNG
if (condition)
      body();
インデントについて
//4スペース
for (int i = 0; i < n; i++) {
    doSomething(i);
}

//関数呼び出しと引数を含む場合の改行は8スペース
Instrument i =
        someLongExpression(that, wouldNotFit, on, one, line);
頭字語や略語は単語として扱う
//OK
XmlHttpRequest
//NG
XMLHTTPRequest

//OK
getCustomerId
//NG
getCustomerID

まとめ

以上のように公式の規約に従うことだけで大部分はカバーできるので、あとは特に定めのないものやアプリケーション特有のものに関して付け加えていけばOKかと思います。 弊社ではリーダブルコードを題材にした社内勉強会を行っていますので、それらを参考にしながら仕上げていく予定です。

relux iOSアプリ開発にfastlaneを導入

こんにちは Loco Parteners の武井です。

WWDC2016まであと少し 発表内容が気になりますね。
特にiPhone7の"想像もつかないような新機能"が楽しみです。

本エントリでは fastlane の導入ついて書きたいと思います。
fastlaneを導入することでテストアプリの配信コストを
大幅に削減することができました。ぜひ参考にしてみてください。

fastlaneとは

XcodeiTunes Connect 上で行う
リリースフローの作業を自動化することができるツールです。

レールの概念

fastlaneにはレールという概念が存在します。
リリースフローの作業(テスト配信、AppStoreの申請など)を
それぞれをレーンとみなし、提供されているアクションを
組合せてレーンを作成します。
用意されているものだけでも十分ですが
プロダクトに合わせてカスタマイズすることも可能です。

導入方法

基本的にコンソールの指示に従っていくだけで初期設定が完了します。

#インストール
$ sudo gem install fastlane

#初期化
$ fastlane init

#画面の指示にしたがって値を入力していく
Your Apple ID (e.g. fastlane@krausefx.com): XXX@XXX.XXX
Starting login with user 'XXX@XXX.XXX'

-------------------------------------------------------------------------------------
The login information you enter will be stored in your Mac OS Keychain
You can also pass the password using the `FASTLANE_PASSWORD` env variable
More information about it on GitHub: https://github.com/fastlane/credentials_manager
-------------------------------------------------------------------------------------

Password (for XXX@XXX.XXX): *****

+----------------+-------------------------------------------+
|                      Detected Values                       |
+----------------+-------------------------------------------+
| Apple ID       | XXX@XXX.XXX                           |
| App Name       | relux                                     |
| App Identifier | XXX.XXX.relux                         |
| Workspace      | /Users/XXX/relux_ios/relux.xcworkspace    |
+----------------+-------------------------------------------+

Please confirm the above values (y/n)
y

Created new file './fastlane/Appfile'. Edit it to manage your preferred app metadata information.
Loading up 'deliver', this might take a few seconds
Login to iTunes Connect (XXX@XXX.XXX)
Login successful

+--------------------+-------------------------+
|            deliver 1.10.3 Summary            |
+--------------------+-------------------------+
| username           | XXX@XXX.XXX          |
| app_identifier     | XXX.XXX.relux        |
| metadata_path      | ./fastlane/metadata     |
| screenshots_path   | ./fastlane/screenshots  |
| skip_binary_upload | false                   |
| skip_screenshots   | false                   |
| skip_metadata      | false                   |
| force              | false                   |
| submit_for_review  | false                   |
| automatic_release  | false                   |
+--------------------+-------------------------+

Writing to 'fastlane/metadata/ja/description.txt'
Writing to 'fastlane/metadata/ja/keywords.txt'
Writing to 'fastlane/metadata/ja/release_notes.txt'
Writing to 'fastlane/metadata/ja/support_url.txt'
Writing to 'fastlane/metadata/ja/marketing_url.txt'
Writing to 'fastlane/metadata/ja/name.txt'
Writing to 'fastlane/metadata/ja/privacy_url.txt'
Writing to 'fastlane/metadata/copyright.txt'
Writing to 'fastlane/metadata/primary_category.txt'
Writing to 'fastlane/metadata/secondary_category.txt'
Writing to 'fastlane/metadata/primary_first_sub_category.txt'
Writing to 'fastlane/metadata/primary_second_sub_category.txt'
Writing to 'fastlane/metadata/secondary_first_sub_category.txt'
Writing to 'fastlane/metadata/secondary_second_sub_category.txt'
Successfully created new configuration files.
Downloading all existing screenshots...
Downloading existing screenshot '1_iphone4_1.4.0inc-01.jpg'
Downloading existing screenshot '2_iphone4_2.4.0inc-02.jpg'
Downloading existing screenshot '3_iphone4_3.4.0inc-03.jpg'
Downloading existing screenshot '4_iphone4_4.4.0inc-04.jpg'
Downloading existing screenshot '5_iphone4_5.4.0inc-05.jpg'
Downloading existing screenshot '1_iphone35_1.3.5inc-01.jpg'
Downloading existing screenshot '2_iphone35_2.3.5inc-02.jpg'
Downloading existing screenshot '3_iphone35_3.3.5inc-03.jpg'
Downloading existing screenshot '4_iphone35_4.3.5inc-04.jpg'
Downloading existing screenshot '5_iphone35_5.3.5inc-05.jpg'
Downloading existing screenshot '1_iphone6_1.4.7inc-01.jpg'
Downloading existing screenshot '2_iphone6_2.4.7inc-02.jpg'
Downloading existing screenshot '3_iphone6_3.4.7inc-03.jpg'
Downloading existing screenshot '4_iphone6_4.4.7inc-04.jpg'
Downloading existing screenshot '5_iphone6_5.4.7inc-05.jpg'
Downloading existing screenshot '1_iphone6Plus_1.5.5inc-01.jpg'
Downloading existing screenshot '2_iphone6Plus_2.5.5inc-02.jpg'
Downloading existing screenshot '3_iphone6Plus_3.5.5inc-03.jpg'
Downloading existing screenshot '4_iphone6Plus_4.5.5inc-04.jpg'
Downloading existing screenshot '5_iphone6Plus_5.5.5inc-05.jpg'
Successfully downloaded all existing screenshots
Successfully created new Deliverfile at path 'fastlane/Deliverfile'
'snapshot' not enabled.
'cocoapods' enabled.
'carthage' not enabled.
Created new file './fastlane/Fastfile'. Edit it to manage your own deployment lanes.
fastlane will send the number of errors for each action to
https://github.com/fastlane/enhancer to detect integration issues
No sensitive/private information will be uploaded
You can disable this by adding `opt_out_usage` to your Fastfile
Successfully finished setting up fastlane

テストアプリ配信の自動化

弊社ではdeploygateを利用しており手順は以下のようになります。
fastlaneでは全ての工程を自動化することができます。

テストアプリ配信フロー

f:id:loco-dev:20160522231452p:plain

1. pod install の実行
2. アプリケーションのビルド
3. プロビジョニングファイルの更新
4. ipaファイルの生成
5. deploygateへのアップロード

Fastfile

fastlane/Fastfileに実行させたいアクションを書いてきます。
initした状態で一通りのものは揃っているので
どういう書き方をすれば良いか迷うことはないと思います。

事前処理

レーンを実行する前の事前処理を書いておくことができます。

  • 実行後にSlackに通知できるようにWebhookURLを設定
  • cocoapods pod installの実行
before_all do
   ENV["SLACK_URL"]="XXXXX"
   cocoapods
end
deploygateレーン

deploygate配信用のレーンを新しく用意しています。
アップロードする際のメッセージは最後のコミットメッセージを設定しています。

  • sigh プロビジョニングファイルの更新
  • gym アプリケーションのビルド
  • deploygate deploygateへのアップロード
lane :dg do
   sigh(adhoc: true)
   gym(scheme: "relux", configuration: "Debug")
   message = "relux for iOS\nCommit message: #{last_git_commit[:message]}"
   deploygate(user: "XXXXX", api_token: "XXXXX", message: message)
end
事後処理

レーン実行した後の処理を書くことができます。
ここでは レーンが成功したことをSlackに通知するようにしています。

after_all do
   slack(message: "Successfully fastlane.")
end
Slack上ではこのように表示されます。

f:id:loco-dev:20160522234304p:plain

レーンの実行

今回紹介したdeploygateはこのように実行します。

$ fastlane dg

とても簡単です。導入前は手動でアップロードしていたのですが
コマンド1つでアップロードできるようになりかなり楽になりました。
deploygateには必ず最新版のテストアプリがあるという状態を保つことができています。

おわりに

今回はfastlaneの導入について紹介しました。
アプリチームでは世界一の宿泊予約アプリを目指し
日々 UIUXの改善を行っています。
ご興味がありましたらWantedlyをご覧ください!

www.wantedly.com