Wondershake 開発者ブログ

LOCARI(ロカリ)の運営会社の開発者ブログです。

Locariで使用しているCocoaPodsたち

こんにちは、iOS開発担当の藤井です。

Cocoapods使ってますか? 今回はみんな大好きCocoapodsの中でLocariでも使用しているライブラリを幾つか紹介します。

AFNetworking

https://github.com/AFNetworking/AFNetworking

ネットワーク系のライブラリとしては知らない人はいないくらい有名なライブラリで、LocariでもAPIとの接続で利用しています。 単純にURLリクエストを投げることから複雑なことまで何でもできるので、サーバサイドと繋げるアプリの場合は最初の選択肢に上がってくるのはこのライブラリではないかと思います。

APParallaxHeader

https://github.com/apping/APParallaxHeader パララックス効果のついたテーブルヘッダを作るのに便利なのがこのAPParallaxHeaderです。 Locariでは各記事のカバー写真をパララックス効果を入れて表示しているのでこのライブラリを使用しています。

Fabric & Crashlytics

Twitterに買収されたCrashlyticsがFabricとして提供しているのがこれで、バグレポートやアプリの利用状況などをリアルタイムで送ってくれます。

ionicons

http://ionicons.com/ ioniconsはiOS/Androidアプリ内でよく使いそうなアイコンをカスタムフォントとして利用できるライブラリです。UILabel内で文字としても表示できますし、UIImageView内に画像として表示することもできるので非常に使い勝手が良いです。

MagicalRecord

https://github.com/magicalpanda/MagicalRecord これもかなり有名なライブラリですが、CoreDataを扱う上で非常に便利なO/Rマッパーです。これを利用せずにCoreDataを扱うことはもうあまりないのでは…? というくらい依存度が高いです。

SDWebImage

https://github.com/rs/SDWebImage AFNetwowrkingと同じく画像ダウンロードを行うライブラリとしては有名なこのライブラリも入れています。 Jpeg形式の画像だけでなくWebPも扱えるため、転送量のダイエットにも役立ちます。

youtube-ios-player-helper

https://github.com/youtube/youtube-ios-player-helper 記事内でYouTube動画を再生する為に利用しています。VideoIDをセットするだけでYouTube再生用のViewを生成してくれます。フルスクリーンにならずに再生できるのがさり気なく良いです。

VTAcknowledgementsViewController

https://github.com/vtourraine/VTAcknowledgementsViewController どういったCocoapodsを利用しているのかライセンスとしてまとめてくれる便利なライブラリがこれです。plistで設定してあげると自動的に有効なCocoapodsがリストアップされたViewControllerを生成してライセンスとして表示できます。 アプリに使用しているライブラリのライセンス表示を入れたいけどライブラリを追加・削除する度に更新するのが面倒、という時に活躍してくれます。

Locariでは他にもたくさんのCocoaPodsを利用していますが、主だったものを上げてみました。 他にもこんな便利なCocoaPodsあるよ!というのがあればぜひ教えてください :)

またWondershakeではCocoaPodsに詳しいiOSエンジニアも募集中です。 www.wantedly.com

RailsアプリケーションのためのフロントエンドLint環境の整備(JS編)

こんにちは。フロントエンドエンジニアの佐々木です。

まだ入社して2ヶ月ほどですが、最初の1ヶ月は主に Lint 環境の整備などを行ってきたのでその紹介をしてみます。

はじめに

Wondershake では Locari というサービスを運営しているのですが、2年以上運用されており様々な人達が関わって作られてきました。

コードベースは Ruby on Rails 4 の上に作られており、github-flow 形式で PR を作って開発しています。

レビューで致命的な問題は取り除かれているものの、多人数で作り上げられたコードは人によって書き方が違ったりと、どうしても細部のルールが揺れてしまっている状態でした。

そこでコーディング規約を機械的にチェックする Linter を入れ、ルールに適合しないコードは CI(CircleCI) で落とすようにしました。
これで最低限コーディング規約に従ったコードだけをレビューに出すことができ、一定の品質を保てるようになります。

この記事でやること

  • CoffeeLintの導入
  • ESLintの導入

※ nodejs が既に入っていることを前提として書いています。

CoffeeLintの導入

f:id:sskyu:20160627204133p:plain

CoffeeLint は特に悩むこと無く導入することが出来ると思います。

$ npm i -D coffeelint  # devDependenciesに追加

CoffeeLint は CLI で実行できるようなので、試しに動かしてみます。

./node_modules/.bin/coffeelint app/assets/javascripts
  ✗ app/assets/javascripts/admin_post_form/form/dropdown.coffee
     ✗ #7: Line exceeds maximum allowed length. Length is 82, max is 80.
  ✓ app/assets/javascripts/admin_post_form/form/edit_post_cover_picture.coffee

  # 途中省略

✗ Lint! » 33 errors and 0 warnings in 52 files

ほとんどのエラーが mux_line_length のエラーでした。

※ 各ルールは公式ドキュメントでご確認ください。

CoffeeLint - Lint your CoffeeScript

デフォルトで有効なルールには適合するようにコードを修正したいところですが、CoffeeScript で一行 80 文字制限は少々厳しいので、このルールを無視して実行するようにしたいと思います。

プロジェクトルートに下記の内容で .coffeelint.json を作成します。
(ファイル名はなんでも良いですが、.json で作成するのをおすすめします。)

{
  "max_line_length" : {
    "value": 80,
    "level": "ignore"
  }
}

"level": "ignore" にすることでルールを無視することができます。
"level": "warn" だと警告を出力し、 "level": "error" だとエラーを出力します。

もし一行の文字数の上限を上げたい場合は "value": 120, "level": "error" などにすると良いです。

この設定ファイルを読み込むように指定し、もう一度 CoffeeLint を実行してみます。

./node_modules/.bin/coffeelint -f .coffeelint.json app/assets/javascripts
  ✓ app/assets/javascripts/admin_post_form/form/dropdown.coffee

  # 途中省略

 Ok! » 0 errors and 0 warnings in 52 files

エラーが表示されなくなりました。

最後に、CoffeeLint を動かすコマンドが長いので npm scripts に登録しておきます。

package.json の scripts の項に test:coffeelint として登録します。

"scripts": {
  "test:coffeelint": "coffeelint -f .coffeelint.json app/assets/javascripts"
}

これで $ npm run test:coffeelint だけで CoffeeLint を走らせることが出来るようになりました。

ESLintの導入

f:id:sskyu:20160627203914p:plain

管理画面側の一部で browserify-rails + babelify を使って書かれた ES2015+ なコードがあります。
これらを ESLint を使って構文チェックさせたいと思います。

ESLint には他の人が作った設定をインポートできる仕組みがあるので、定番の airbnb の設定を使います。

eslint-config-airbnb

下記コマンドで一連のモジュールをインストールします。

npm i -D eslint-config-airbnb eslint-plugin-import eslint-plugin-react eslint-plugin-jsx-a11y eslint babel-eslint

それぞれのモジュールの役割は以下の通りです。

  • eslint-config-airbnb
    • airbnb の eslint 設定
    • 今回はこれを使いまわしたい
  • eslint-plugin-import
    • eslint の設定をインポートするために必要
  • eslint-plugin-react
    • eslint で react を扱うために必要
  • eslint-plugin-jsx-a11y
    • eslint で jsx を扱うために必要
  • eslint
    • eslint 本体
  • babel-eslint
    • babel 側で読み込んだ拡張構文を eslint 側に対応させるために必要
    • ES6/ES7 の範囲で書かれていれば不要
      • React Components で static property を使っているため今回は必要

モジュールをインストールできたらプロジェクトルートに .eslintrc を作成します。

{
  "parser": "babel-eslint",
  "extends": "airbnb",
  "env": {
    "browser": true,
    "node": true
  },
  "globals": {
    "ga": true,
    "$": true,
    "I18n": true
  },
  "rules": {
    // 必要ならここに airbnb の eslint ルールを上書きしていく
  }
}

"extends": "airbnb"eslint-config-airbnb の設定を読み込んでいます。
"globals" には window 直下に作られているグローバル変数を記述しておきます。
airbnb のルールは結構厳しめで作られているので、不都合ある場合は "rules" に追記してルールを上書きしていきます。

この時点で一度 ESLint を実行してみます。

./node_modules/.bin/eslint --ext .js,.jsx app/assets/javascripts

  # 中略

  5:398  error    Missing semicolon                                                                                              semi
  5:401  error    'jQuery' is not defined                                                                                        no-undef

✖ 53669 problems (52666 errors, 1003 warnings)

出力が完了するまで20秒くらいかかりました。
これだけ時間がかかった原因は app/assets/javascripts/lib/* 以下の js ファイルも ESLint 対象になっているためのようです。

外部ライブラリは ESLint 対象にする必要がありませんので、プロジェクトルートに下記の内容で .eslintignore を作成し、ESlint の対象にしたくないファイルを追記していきます。

app/assets/javascripts/lib/*

もう一度実行してみます。

./node_modules/.bin/eslint --ext .js,.jsx app/assets/javascripts

/path/to/hoge.js
  6:3  warning  Unexpected constant condition  no-constant-condition

/path/to/fuga.js
   8:3  warning  Unexpected constant condition  no-constant-condition
  26:3  warning  Unexpected constant condition  no-constant-condition

/path/to/piyo.js
  6:3  warning  Unexpected constant condition  no-constant-condition

✖ 4 problems (0 errors, 4 warnings)

出力が減って見やすくなりました。

no-constant-condition の警告が出力されています。
当該箇所のコードは while (true) {} で無限ループを作っている箇所でした。
これはどうしてもコード側を修正することができないので、警告を表示しないようにルールを追加します。

{
  "parser": "babel-eslint",
  "extends": "airbnb",
  "env": {
    "browser": true,
    "node": true
  },
  "globals": {
    "ga": true,
    "$": true,
    "I18n": true
  },
  "rules": {
    // 追記
    "no-constant-condition": ["error", { "checkLoops": false }]
  }
}

もう一度実行してみます。

./node_modules/.bin/eslint --ext .js,.jsx app/assets/javascripts

出力無しなので正常終了したようです。

こちらもコマンドが長いので package.json の scripts に登録しておきます。

"scripts": {
  "test": "npm run test:lint",
  "test:lint": "npm run test:coffeelint && npm run test:eslint",
  "test:coffeelint": "coffeelint -f .coffeelint.json app/assets/javascripts",
  "test:eslint": "eslint --ext .js,.jsx app/assets/javascripts"
}

これで $ npm t を実行するだけで npm run test:coffeelintnpm run test:eslint が実行されるようになりました。
(あとで "test:unit" を作ろうと思っているので "test""test:lint" を分けています)

まとめ

今回は CoffeeLint と ESLint の導入方法をご紹介しました。
ブランチをプッシュしたら CI で npm t を実行するようにすればコーディング規約を逸脱したコードがマージされることを防ぐことができます。

今回は長くなったので JS 編としました。
似たような感じで scss-lint の導入についても後日ご紹介できればと思います。

Wondershake では サーバーサイドエンジニアや iOSAndroid ディベロッパーを募集しています。
興味が湧いた方は是非こちらからご応募下さい!

www.wantedly.com

Androidアプリのテスト自動化

WondershakeでLocariのAndroidアプリを開発している代蔵です.

今回は,私が担当しているAndroidアプリのCIについてご紹介しようと思います. Androidアプリの開発者が私一人ということもあり,テストなどを出来るだけ自動化することで開発に専念できるような環境を作りました.

大まかな流れ

  1. GithubへPush (marge)
  2. PushをトリガーにCircleCIでUnitテスト,UIテスト
  3. Slackに結果を通知
  4. Fabricで開発チーム(release)・社内全体(master)にAPKを配布
  5. AWS Device Farmでモンキーテスト(master)

f:id:takumiumiu:20160623175741p:plain

1 GithubへPush(marge)

書いたコードをGitHubへPush.または,release, masterブランチへのPRのmerge.

2 PushをトリガーにCircleCIでUnitテスト,UIテスト

以下はcircle.ymlの一部抜粋になりますが,CircleCI上でUnitテストとUIテストを行っています. UnitテストはJUnit,UIテストはSpoonを使っています. Spoonを使っている主な理由は,画面のスクリーンショットを記録することでUIに問題ないかどうかを目視で確認できるようにするためです.

test:
  pre:
    # create sdcard
     - mksdcard -l e 512M mysdcard.img

    # start emulator
     - emulator -avd circleci-android22 -no-audio -no-window -sdcard mysdcard.img:
         background: true
         parallel: true

    # wait for booted
     - circle-android wait-for-boot

    # avoid com.android.builder.testing.api.DeviceException: com.android.ddmlib.ShellCommandUnresponsiveException
     - sleep 10

  override:
     - ./gradlew testDebug

     - ./gradlew spoonDebugAndroidTest:
         timeout: 1800

     - cp -r /home/ubuntu/locari_android/app/build/outputs/apk/* $CIRCLE_ARTIFACTS
     - cp -r /home/ubuntu/locari_android/app/build/spoon-output/* $CIRCLE_ARTIFACTS

3 Slackに結果を通知

CircleCIの結果をSlackに通知しています.

4 Fabricで開発チーム(release)・社内全体(master)に配布

releaseブランチでテストが全て通った場合は開発チームに,masterブランチでテストが全て通った場合は社内全体へFabric経由でAPKが配布されます. その他のブランチでは,配布されないようになっています.

5 AWS Device Farmでモンキーテスト(master)

最後に,masterブランチですべてのテストが通った場合にのみ,AWS Device Farmでテストを行うようになっています. これにより,Android版Locariでは最低限の品質を担保しています.

アプリのリリース

CircleCI上のUIテストで取得したスクリーンショットAWS Device Farmで問題が無いかどうかを確認. 全てに問題がなかった場合にのみCircleCIからAPKをダウンロードし,Google Playでリリースするという流れになっています. f:id:takumiumiu:20160623180604p:plain

最後に

細かい部分などは省略していますが,弊社のリリースまでの全体像を把握することが出来るかと思います. アプリのリリース作業などはまだ自動化していませんが,今のところ自動化の必要性は感じていません.

最近CircleCI上でOutOfMemoryErrorが多発していて,複数端末でのUIテストが出来ていないので,CircleCIから別のものに移行しようと検討中です.

弊社ではAndroidiOS,サーバサイドエンジニアを募集しています.ご興味の有る方がいれば是非こちらからご応募下さい!

www.wantedly.com

WWDC16 キーノート発表内容のまとめ

こんにちは、iOSエンジニアの藤井です。

現地時間13日の朝に行われたばかりのWWDC 16のキーノートで発表された内容のうち、注目すべき点を速報っぽく簡単にまとめました。

watchOS

watchOSは3が発表されました。

  • アプリの起動速度が爆速の7倍に。メモリとバックグラウンド更新を活用して実現。
  • アプリにすぐにアクセスできるドック機能も追加。
  • 手書き入力の強化。アルファベットだけでなく漢字(中国語)にも対応してます。
  • SOS発信機能。長押しすることで自動的に警察や登録しておいた家族などに自身の位置などを送信。
  • その他エクササイズ系の機能充実。

と3になってやっとそれらしい機能が充実してきたかなという印象。

tvOS

Apple TVに入っているtvOSは主にSiriの進化。文章での音声検索が相当優秀になったとのこと。 他には、シングルサインオン機能で、iOS側でログインしていればApple TVでも自動でログインされ、ID/Passwordの入力が不要になります。

macOS

OS Xは噂通りmac OSへ改名。ここ数年のカルフォルニアに関する固有名詞を付けるルールは引き継がれ、Sierraとなりました。 元ネタはシエラネバダ山脈のことかな。

主な追加機能としては

  • Siri Mac版がついに登場。ファイル検索をしてくれるなどよりアシスタントっぽくなりました。
  • オートアンロック機能。Apple Watchで画面ロックを解除できるようになりました。
  • クリップボード共有機能。Macでコピーした文章などをiPhoneで貼り付けできるように。またその逆もしかり。
  • iCloud経由でファイル間共有の強化。
  • Macのハードディスクが一杯になった時に不要なファイルを自動で整理して空き容量を増やす便利機能もしれっと追加。
  • Apple Payでの支払い。ブラウザ上でApple Payボタンを利用する際に手元のiPhoneで認証すればそのまま支払いできる。

辺り。

iOS

iOSはついに10へ! ということで10の機能更新が行われました。

  1. Notificationやコントロール機能の充実。3D Touchで更に情報を引き出せるようになったり、返信周りがかなり拡充されたのでもはやアプリを開かなくても用が済むように。

  2. Siriのサードパーティへの開放。Siriがサードパーティのアプリと一部連携できるようになり、WeChatのメッセージに返信したり、Uberを呼んだりできるようになりました。

  3. キーボードがよりインテリジェンスに。Deep Learningによる予測変換で会話の流れを読んで変換候補を出してくるようになりました。単純にテキストの候補だけでなく現在位置や電話帳、スケジュールなどを候補に出したり登録したりといったことができるようになります。ちゃんとワークすればかなり便利そうな予感。

  4. 写真アプリも更新。顔や場所などを認識して自動でカテゴリ分け、ロードムービーまで生成してくれるようになりました。Googleフォトやショートムービー作成系アプリを一気に倒しにきてる感。

  5. 地図アプリもスケジュールなどから行く場所を予測して表示などができるように。ナビもちょっと優秀になりました。

  6. ミュージックアプリはデザインの一新。一応使いやすくなったとのこと。

  7. Newsアプリも同じくデザインの一新。ミュージックアプリ同様Boldのフォントを使ったデザインを押し出していく様子。

  8. 日本だといまいち馴染みの薄いHome KitはHomeアプリとして登場。手元のアプリひとつで家のもの全てが操作できるように(したい)。

  9. 電話も更新。留守番電話のメッセージの書き起こし(beta)やサードパーティーで提供されている電話機能と繋げて電話できるように。

  10. メッセージアプリは大幅アップデートで、サードパーティーで提供されているようなおもしろ機能を一通り追加。そしてメッセージストアを用意して開発者へ開放、スタンプなど拡張機能を自由に追加できるように。

Swift

Swift関連ではSwift playgroundsというiPadアプリが登場。小学生くらいの子どもたちが楽しくSwiftやプログラミングを学べる仕組みが用意されているようです。子ども向けながら、大人の初学者でも利用できそうなくらいの作り込み。

まとめると

watchOS, tvOS, macOS, iOSという4種類のOSとそれが入ったデバイス間でのスムーズな連携の強化が今回のテーマの一つのように感じられました。前バージョン辺りからその傾向はありましたが、今回もオートアンロック機能やクリップボード共有、Apple Payでの支払いなど、今まで欲しかった連携機能が欲しい形で追加されています。どれか一つのOSやデバイスに集約するのではなく、それぞれに役割を与えて連携させていくという考えた方は個人的に好感が持てます。 他では、Siriとそれに付随するDeep Learning機能の追加、今まで開発者へ開放されていなかったAPIの部分開放辺りが注目すべき点でしょうか。

4つの新しいOSの開発者プレビュー版は本日から利用開始とのことで、早速触ってみようと思います!

SQLだけで勤怠データから曜日ごと、日中・深夜勤務時間を算出する(Postgres編)

こんにちは。千葉です。

開発者ブログをはじめよう、ということで初ポストです。

弊社には勤怠管理用の社内システムがあり(ちょうどいい勤怠システムってあんまりないですよね...!)そのメンテナンスもしています。

そこで今回「曜日や日中・深夜の勤務時間によって時間数を分けて表示してほしい」という案件が降ってきました。 素直にRuby on Railsで実装したり、適切な設計がしてあれば簡単な実装ですが、諸事情によりSQL(Postgres)だけで表示したい!ということになりました...!厄介ですね。

どうしよう、どうしようと先延ばしにしているうちに同じく開発部のエリックが考えてくれました。 方針としてはこうです。

  • 曜日は EXTRACT(DOW FROM date) すれば簡単に出せるのでそれで判定
  • 時間は LEASTGREATEST を駆使して境目の時間ごとに判定

Locariでは普段MySQLを使っているので、Postgresの勉強になりました。

この方針にのっとって、下記のテーブルがあるとすると、 (実際のテーブルのカラムをいくつか省いています)

                                            Table "public.user_attendances"
          Column          |            Type             |                           Modifiers
--------------------------+-----------------------------+---------------------------------------------------------------
 id                       | integer                     | not null default nextval('user_attendances_id_seq'::regclass)
 status                   | integer                     | not null default 0
 user_id                  | integer                     | not null
 is_at_office             | boolean                     | not null default true
 date                     | date                        | not null
 started_at               | timestamp without time zone |
 ended_at                 | timestamp without time zone |

平日の日中の勤務時間を出すクエリーは下記のようになります。

  • 実際には休憩時間も引く必要があるのでもうちょっと複雑になります
  • DBにはUTCで保存されているので、9時間足しています。
SELECT
  "User Attendances"."user_id" AS "Id",
  EXTRACT(EPOCH
          FROM
          CASE WHEN EXTRACT(DOW FROM date) <> 0
            THEN
              GREATEST(
                  INTERVAL '0',
                  LEAST(
                      (started_at + INTERVAL '9 hours') :: DATE + INTERVAL '22 hours',
                      ended_at + INTERVAL '9 hours'
                  ) - GREATEST(
                      (started_at + INTERVAL '9 hours') :: DATE + INTERVAL '5 hours',
                      started_at + INTERVAL '9 hours'
                  )
              )
          ELSE INTERVAL '0'
          END
          +
          CASE WHEN EXTRACT(DOW FROM date + INTERVAL '1 days') <> 0 AND
                    ((started_at + INTERVAL '9 hours') :: DATE < (ended_at + INTERVAL '9 hours') :: DATE)
            THEN
              GREATEST(
                  INTERVAL '0',
                  LEAST(
                      (ended_at + INTERVAL '9 hours') :: DATE + INTERVAL '22 hours',
                      ended_at + INTERVAL '9 hours'
                  ) - (
                    (ended_at + INTERVAL '9 hours') :: DATE + INTERVAL '5 hours'
                  )
              )
          ELSE INTERVAL '0'
          END
  ) / 60                       AS "月~土通常時間"
FROM "public"."user_attendances" AS "User Attendances"
WHERE ("User Attendances"."date" BETWEEN {CALENDAR.START} AND {CALENDAR.END}
AND ("User Attendances"."status" = 1))

すごいですね...!久しぶりにこんなクエリーを書きました。 でもおかげで、正しい時間数が算出できるようになりました。

WondershakeではSQLを駆使してLocariや会社をよくしてくれる人を探しています! よかったらご応募ください!