香川県に引っ越しました

f:id:t-kashima:20190701131238j:plain 今日から香川県の仏生山に住みはじめました。

なぜ香川?

もうすぐ子どもが生まれる予定なのですが、これをきっかけに妻の友だちや実家に近いところに住みたいなと思っていました。そこで、香川は大学の時に住んでいて、地域の雰囲気を知っていたこと、仕事で東京へも出やすかったので諸々考えた末に香川に決めました。

仕事は?

基本リモートになりますが、今まで通りアルのモバイルチームで働いています。最近はテストを書いたり、iOS/Androidでクロスして開発していたり、色々双方のことが勉強になって楽しいです!これからも3か月に一度くらいは東京に出社する予定です。

そして

ということで、これからも引き続き宜しくお願いします!

また東京にも定期的に行くので、その時はぜひご飯いきましょう!!

以上、近況でした。

Kotlinリポジトリのテストを動かす方法

Kotlinリポジトリを触る機会があったので、少しはまったテストを動かす方法を残しておきます。基本的にREADMEに書いてある手順で、Working with the project in IntelliJ IDEA の項目まで動かすと動きました!

セットアップ手順

  1. Kotlinリポジトリを手元にcloneしてきます。

    git clone git@github.com:JetBrains/kotlin.git

  2. Build environment requirements に書いてあるように各バージョンのJDKの設定が必要なので、Macの場合は下記からダウンロードして設定します。

    export JAVA_HOME=`/System/Library/Frameworks/JavaVM.framework/Versions/A/Commands/java_home -v "1.8"` export JDK_16=`/System/Library/Frameworks/JavaVM.framework/Versions/A/Commands/java_home -v "1.6"` export JDK_17=`/System/Library/Frameworks/JavaVM.framework/Versions/A/Commands/java_home -v "1.7"` export JDK_18=`/System/Library/Frameworks/JavaVM.framework/Versions/A/Commands/java_home -v "1.8" export JDK_9=`/System/Library/Frameworks/JavaVM.framework/Versions/A/Commands/java_home -v "9"`

  3. エディタとしてIntelliJ IDEAをダウンロードします。

  4. IntelliJ IDEAを立ち上げて、ダイアログのOpenでKotlinリポジトリを開きます。

    f:id:t-kashima:20190422082444p:plain:w480

  5. 次に「File」>「New」>「Module from Existing Sources」を選択して、Kotlinリポジトリにある build.gradle.kts を選択して開きます。

    f:id:t-kashima:20190422083020p:plain:w320

  6. IntelliJの処理が走るので数分待って、下記のように動かしたいテストファイルを右クリックして「Run 〜」を選べばテストを実行できます!

    f:id:t-kashima:20190422083921p:plain:w480

おわりに

はじめはKotlinリポジトリの開き方を間違えてしまい、諸々壊してしまったのですが、改めてREADMEをしっかり読むとスムーズに設定できました! プログラミング言語リポジトリは設定がすごく難しい印象だったので、すごく簡単に設定できてびっくりしました。

CircleCIでFirebase Test Labを動かす方法

CircleCIからFirebase Test Labを動かしてみたので、その際につまずいた所を書いておこうと思います。ほとんどの設定の仕方はCircleCIの「Testing With Firebase Test Lab」に書いてあるので、そちらを参考にしてください。

CircleCIでFirebase Test Labを使うための設定

さっそくですが結果、下記のような config.yml になりました。

version: 2
jobs:
  build:
    docker:
      - image: circleci/android:api-28

    working_directory: ~/repo

    environment:
      JVM_OPTS: -Xmx3200m
      TERM: dumb

    steps:
      - checkout

      # Download and cache dependencies
      - restore_cache:
          keys:
            - v1-dependencies-{{ checksum "build.gradle" }}
            # fallback to using the latest cache if no exact match is found
            - v1-dependencies-

      - run:
          name: gradle dependencies
          command: ./gradlew androidDependencies

      - save_cache:
          paths:
            - ~/.gradle
          key: v1-dependencies-{{ checksum "build.gradle" }}

      - run:
          name: Build debug APK and release APK
          command: |
            ./gradlew :app:assembleDebug
            ./gradlew :app:assembleDebugAndroidTest

      - run:
          name: Store Google Service Account
          command: echo $GCLOUD_SERVICE_KEY | base64 --decode --ignore-garbage > ${HOME}/gcloud-service-key.json

      - run:
          name: Authorize gcloud and set config defaults
          command: |
            sudo gcloud auth activate-service-account --key-file=${HOME}/gcloud-service-key.json
            sudo gcloud config set project ${GOOGLE_PROJECT_ID}

      - run:
          name: Test with Firebase Test Lab
          command: >
            sudo gcloud firebase test android run \
              --type instrumentation \
              --app ./app/build/outputs/apk/debug/app-debug.apk \
              --test ./app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk \
              --results-bucket cloud-test-${GOOGLE_PROJECT_ID} \
              --device model=Nexus6,version=21,locale=ja,orientation=portrait \
              --timeout 10m \
              --use-orchestrator

      - run:
          name: Install gsutil dependency and copy test results data
          command: |
            sudo pip install -U crcmod
            sudo mkdir ~/firebase
            sudo gsutil -m cp -r -U `sudo gsutil ls gs://cloud-test-${GOOGLE_PROJECT_ID} | tail -1` ~/firebase/ | true

      - store_artifacts:
          path: ~/firebase/

以降は実際につまずいたところの解説になります。

Firebase Test Labをgcloudコマンドから使う準備をする

gcloudコマンドは image: circleci/android:api-28 に入っているのですが、Firebase Test Labにアクセスできるようにするために、プロジェクトの情報を読み込ませる必要あります。

  1. まずは Testing With Firebase Test Lab の1〜3に従って、Firebase Test Labにブラウザでアクセスできるようにします。
  2. 次に、Firebase Consoleのプロジェクト設定、サービスアカウントから「新しい秘密鍵の生成」を押して、JSONファイルをダウンロードします。

    f:id:t-kashima:20190404222911p:plain:w200

  3. そして、ダウンロードしたJSONファイルを下記の手順でBase64エンコードして、そのファイルに書かれている文字列をCircleCIの環境変数「GCLOUD_SERVICE_KEY」に設定します。

openssl base64 -in [ダウンロードしたJSONファイル] -out service_key.json

これで下記の部分が動くようになって、Firebase Test Labを使う準備ができました。

      - run:
          name: Store Google Service Account
          command: echo $GCLOUD_SERVICE_KEY | base64 --decode --ignore-garbage > ${HOME}/gcloud-service-key.json

      - run:
          name: Authorize gcloud and set config defaults
          command: |
            sudo gcloud auth activate-service-account --key-file=${HOME}/gcloud-service-key.json
            sudo gcloud config set project ${GOOGLE_PROJECT_ID}

Firebase Test Labをgcloudコマンドから使う

下記の部分でInstrumented Testを指定の端末で実行することができます。1つつまずいたのは「Nexus 6」のように端末名の間にスペースを入れてしまていて、Format must be ARG_FILE:ARG_GROUP_NAME Exited のようなエラーが出てしまっていました。

     - run:
          name: Test with Firebase Test Lab
          command: >
            sudo gcloud firebase test android run \
              --type instrumentation \
              --app ./app/build/outputs/apk/debug/app-debug.apk \
              --test ./app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk \
              --results-bucket cloud-test-${GOOGLE_PROJECT_ID} \
              --device model=Nexus6,version=21,locale=ja,orientation=portrait \
              --timeout 10m \
              --use-orchestrator

テスト結果をCircleCIのArtifactsに保存する

Firebase Test Labのテスト結果は、上記で指定したGoogle Cloud Storageのバケットにアップロードされます。そのため最新のものを一件ダウンロードして、指定のフォルダに保存します。そして、それをCircleCIのartifactsとして保存します。

      - run:
          name: Install gsutil dependency and copy test results data
          command: |
            sudo pip install -U crcmod
            sudo mkdir ~/firebase
            sudo gsutil -m cp -r -U `sudo gsutil ls gs://cloud-test-${GOOGLE_PROJECT_ID} | tail -1` ~/firebase/ | true
      - store_artifacts:
          path: ~/firebase/

あまりまとまった記事がなかったのですが、CircleCIからFirebase Test Labを使うのはすごく簡単で、エミュレータを立ち上げたりする苦労をしなくていいのは本当に最高だなと思いました!

参考

CircleCIでInstrumented Testをする時はFirebase Test Labでよさそう

概要

アプリ側にCloud Firestoreからデータをとってくるロジックが書いてある場合に、それをCircleCIでどうやってテストしようか迷ったので、色々施行錯誤した結果を書いておきます。

結論

CircleCIが推奨しているのであれですが、Firebase Test Labを使うことにしました!無料枠も豊富にあって、超過したテスト時間しか課金されないので Instrumented Test をする時はこれでいいんじゃないかと思います。

f:id:t-kashima:20190404022655p:plain:w400
Firebase Test Lab - 割り当てと課金

施行錯誤

結論に至るまにでやったことです。

CircleCIでエミュレータが立ち上がらない

Androidに依存があるテストを実施する場合、 Instrumented Testと呼ばれるテストの種類に該当し、エミュレータを立ち上げてテストを行う必要があります。Cloud FirestoreはAndroidに依存する部分(ContextやGoogle Play Serviceなど)が多いので、まずはこの方法を試してみました。

昔のCircleCIはエミュレータの立ち上げに対応していたのですが、CircleCI2になってからは公式でサポートされていません。そのため、無理やり下記の方法を試してみたのですがいずれも上手くいきませんでした。

他にも施行錯誤してみたのですが、結局エミュレータが立ち上がりませんでした。

RobolectricではCloud Firestoreに繋がらない

次にRobolectricを使ってCloud Firestoreに繋げられないかやってみました。Robolectricエミュレータを立ち上げずに、JVM上でテストするためのライブラリです。Cloud FIrestoreを使うには、AndroidのViewの情報などは不要なため、Robolectricで十分テストできるのではないかと考えました。

結果、 色々やってみたのですが、Cloud FirestoreはGoogle Play Serviceに依存している部分が多いためか、Cloud Firestoreにうまく繋ぐことができませんでした。

結果

CircleCIで完結している方が分かりやすいかと思って、色々やってみたのですが、結局今後UIのテストが増えたり分かりやすさを考えると、Firebase Test Labを使うのが良いという結論に至りました!(あと実は課金形態を勘違いしていたので、気づいていればこれ一択でした...)

参考

UIScrollViewの中でUICollectionViewがスクロールしない

iOSで上下左右にスクロールさせたい時、UIScrollViewで縦スクロール、その中にUICollectioViewを置いて横スクロールをさせる方法を思いつきました。

f:id:t-kashima:20190129234339p:plain:w200

しかし、これだけだとUIScrollViewがタッチをとってしまい、縦スクロールしか効きません。

そこで調べた所、UIScrollViewの「Direction Lock Enabled」にチェックを入れることで、縦スクロールのタッチだけに反応するようになって、UICollectionViewが横スクロールできるようになりました!

f:id:t-kashima:20190129234448p:plain:w300

参考: ios - UIScrollView AutoLayout Vertical Only - Stack Overflow

ionicのプラグインを作る時につまずいたこと

最近、ionicのJSからiOSの機能を使えるようにするプラグインを作ることがあったので、その際にいくつかつまずいたので解決方法をまとめておきます。

何度もJSのコールバックを呼びたい

ネイティブ側から何度もJSのコールバックを呼びたい時は、CDVPluginResultに setKeepCallbackAs(true) を呼び出す必要があります。

let result = CDVPluginResult(status: CDVCommandStatus_OK, messageAs: "Hello")
result.setKeepCallbackAs(true)
commandDelegate.send(result, callbackId:command.callbackId)

通常は一度しかcallbackIdを使えないのですが、こうすることで何度もcallbackIdに対して結果を通知することができます。

あきらめかけた時に下記の記事を読んでいたら書いてありました。

Beacapp SDK を Cordova Plugin 化してみた

非同期にJSのコールバックを呼びたい

callbackIdをインスタンス変数に保存することで、非同期の処理が終わったタイミングでコールバックを呼ぶことができました。

private var saveCallbackId: String? = nil

func hello(_ command: CDVInvokedUrlCommand) {
    saveCallbackId = command.callbackId

    // 非同期処理
    ... {
        let result = CDVPluginResult(status: CDVCommandStatus_OK, messageAs: "hello")
        commandDelegate.send(result, callbackId:saveCallbackId) 
    }
}

プラグインの開発、デバッグの方法

そもそもネイティブの機能を開発、デバッグをどうすればいいのか全く分からず手探りやっていました。その中で、ほどほどに速度が出て開発しやすい方法を見つけました。

  1. とりあえず本体のプロジェクトにプラグインをインストールする
  2. Xcodeで本体プロジェクトを開いて、Pluginsにあるプラグインのファイルを直接編集して開発を進めていく
  3. 動くようになったら、そのファイルの内容をコピーして、プラグインのファイルに貼りつけてプラグインを再インストールする

他にも方法があると思いますが、この方法だと普通にiOSの機能を開発している時と変わらず開発を進めることができました!


ionic(cordova)のプラグインの詳細をまとめた記事があまりなかったので、公式ドキュメント を読んだり、公式のcordova-plugin-geolocationcordova-plugin-ble-centralソースコードを読みながら開発を進めました。

今回サンプルで作ったリポジトリも置いておきます!プラグインには下記のメソッドが含まれています。

  • 引数にhelloを付けて返す
  • タイマーで5秒おきに処理を実行する
  • ユーザーの現在地を取得する

ionic-sample

ClearDBでmax questionsになった時の対処法

Heroku上でClearDBを使ってサービスを作っているのですが、ClearDBにめちゃくちゃ接続するタスクを heroku run で実行してしまい、以下のエラーが出続けるようになってしまいました。

Mysql2::Error (User '...' has exceeded the 'max_questions' resource (current value: 18000))

ほとんどバックエンドの知識がないのですごく焦りましたが、Herokuをメンテナンスモードにして、コネクションが全てキャンセルされたことを確認した後で元に戻すことで解決しました。