Ionic + Cordova

アプリを自動デプロイ

+ Hibee を開発/運用して

About me

Yoko Karasaki

Excite Japan Co., Ltd.

PHP / Ionic / Kotlin

Hibee

  • 連携したSNSの投稿をストックして
    日記やアルバムを簡単に作成
  • iOS & Android ( β ) 配信中
  • Ionic2 + Cordova
    ( Ionic3 アップグレード中 )

Ionic + Cordova

アプリを自動デプロイ

Purpose

  • 社内で共通の Provisioning Profile や証明書を
    使用したい
  • 手動でコマンド叩いてビルドしてipa / apk を
    配布して…は面倒

How?

push

hook

upload

notify

develop

master

Crashlytics

  • Crashlytics - アプリのクラッシュ収集・解析

  • Answers by Crashlytics -リアルタイムアクセス分析

  • Beta by Crashlytics - ベータ版配布機能

Circle CI

  • SaaS 型 CI (継続的インテグレーション)サービス
  •  GitHubと連携して git push 等をトリガーに
    ジョブを走らせることができる
  • Circle CI 1.0 / Mac OS X のコンテナを使用

Circle CI 2.0 だと Docker イメージが使える
& 複数のジョブを定義できるので、そちらの方が楽かも…

Circle.yml

machine:
  environment:
    XCODE_SCHEME: ""
    XCODE_PROJECT: ""
    PATH: "${PATH}:${HOME}/${CIRCLE_PROJECT_REPONAME}/node_modules/.bin:${HOME}/.config/yarn/global/node_modules/.bin"
    GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError"'
  xcode:
    version: 8.3
dependencies:
  pre:
    - brew install node
    - brew install gradle
    - brew install caskroom/cask/android-sdk
    - sdkmanager "platforms;android-25" "build-tools;25.0.3"
    - npm install -g npm
    - npm install -g yarn
    - yarn global add cordova
    - yarn global add ionic
    - yarn global add ios-deploy
    - ionic info
    - sudo gem update --system
    - sudo gem install
bundlercache_directories:
    - ~/Library/Caches/Yarn/
test:
  override:
    - bundle exec fastlane test
deployment:
  beta:
    branch: develop
    commands:
      - bundle exec fastlane ios beta
      - bundle exec fastlane android beta
  release:
    branch: master
    commands:
      - bundle exec fastlane ios release
      - bundle exec fastlane android release

Set up & install dependecies : 06:59

Deploy ios : 04:21

Deploy android : 05:51

All : 16:31

Fastlane

push

hook

upload

notify

develop

master

Fastlane

  • iOS / Android アプリの
    デプロイ・リリースに
    関連するツールを
    まとめたもの
  • 各ツール ( action ) を
    組み合わせてリリースに
    必要な一連の流れ ( lane )
    を管理する

Example

platform :ios do
  desc "Submit a new Beta Build to Beta by Crashlytics"
  lane :beta do
# Beta by Crashlytics にアップロード
    crashlytics(
      crashlytics_path: "platforms/ios/Myapp/Plugins/cordova-fabric-plugin/Crashlytics.framework",
      ipa_path: "platforms/ios/build/device/Myapp.ipa"
    )
  end

Deploy iOS

push

hook

upload

notify

develop

master

Deploy iOS

machine:
  environment:
    XCODE_SCHEME: ""
    XCODE_PROJECT: ""
    PATH: "${PATH}:${HOME}/${CIRCLE_PROJECT_REPONAME}/node_modules/.bin:${HOME}/.config/yarn/global/node_modules/.bin"
    GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError"'
  xcode:
    version: 8.3
dependencies:
  pre:
    - brew install node
    - brew install gradle
    - brew install caskroom/cask/android-sdk
    - sdkmanager "platforms;android-25" "build-tools;25.0.3"
    - npm install -g npm
    - npm install -g yarn
    - yarn global add cordova
    - yarn global add ionic
    - yarn global add ios-deploy
    - ionic info
    - sudo gem update --system
    - sudo gem install
bundlercache_directories:
    - ~/Library/Caches/Yarn/
test:
  override:
    - bundle exec fastlane test
deployment:
  beta:
    branch: develop
    commands:
      - bundle exec fastlane ios beta
      - bundle exec fastlane android beta
  release:
    branch: master
    commands:
      - bundle exec fastlane ios release
      - bundle exec fastlane android release

Deploy iOS - circle.yml

machine:
  environment:
    XCODE_SCHEME: ""
    XCODE_PROJECT: ""
    PATH: "${PATH}:${HOME}/${CIRCLE_PROJECT_REPONAME}/node_modules/.bin:${HOME}/.config/yarn/global/node_modules/.bin"
  xcode:
    version: 8.3
dependencies:
  pre:
    - brew install node
    - npm install -g npm
    - npm install -g yarn
    - yarn global add cordova
    - yarn global add ionic
    - yarn global add ios-deploy
    - ionic info
    - sudo gem update --system
    - sudo gem install
bundlercache_directories:
    - ~/Library/Caches/Yarn/
test:
  override:
    - bundle exec fastlane test
deployment:
  beta:
    branch: develop
    commands:
      - bundle exec fastlane ios beta
  release:
    branch: master
    commands:
      - bundle exec fastlane ios release

Deploy iOS - Fastfile

fastlane_version "2.48.0"
platform :ios do

  desc "Setup a target platform"
  lane :setup do |options|
    sh "ionic platform add ios"
  end

  desc "Submit a new Beta Build to Beta Crashlytics"
  lane :beta do
    setup(inhouse: true)
    # 証明書やプロファイルを取得
    match(type: "appstore", app_identifier: "com.myapp", username: "my_app@mail.com", readonly: true)
    # ビルド
    sh "ionic build ios --device --prod --debug"
    # Beta by Crashlytics にアップロード
    crashlytics(
      crashlytics_path: "platforms/ios/Myapp/Plugins/cordova-fabric-plugin/Crashlytics.framework",
      ipa_path: "platforms/ios/build/device/Myapp.ipa"
    )
  end

  desc "Deploy a new version to the App Store"
  lane :release do
    setup()
    # 証明書やプロファイルを取得
    match(type: "appstore", app_identifier: "com.myapp", username: "my_app@mail.com", readonly: true)
    # ビルド
    sh "ionic build ios --device --prod --release"
    # App Store にアップロード
    pilot(ipa: "platforms/ios/build/device/Myapp.ipa", skip_submission: true, skip_waiting_for_build_processing: true)
  end
end

after_all do |lane|
    slack(message: "Successfully deployed new App Update.")
end

error do |lane, exception|
    slack(message: exception.message, success: false)
end

Deploy Android


push

hook

upload

notify

develop

master

Deploy Android - circle.yml

 

machine:
  environment:
    XCODE_SCHEME: ""
    XCODE_PROJECT: ""
    PATH: "${PATH}:${HOME}/${CIRCLE_PROJECT_REPONAME}/node_modules/.bin:${HOME}/.config/yarn/global/node_modules/.bin"
    GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError"'
  xcode:
    version: 8.3
dependencies:
  pre:
    - brew install node
    - brew install gradle
    - brew install caskroom/cask/android-sdk
    - sdkmanager "platforms;android-25" "build-tools;25.0.3"
    - npm install -g npm
    - npm install -g yarn
    - yarn global add cordova
    - yarn global add ionic
    - yarn global add ios-deploy
    - ionic info
    - sudo gem update --system
    - sudo gem install
bundlercache_directories:
    - ~/Library/Caches/Yarn/
test:
  override:
    - bundle exec fastlane test
deployment:
  beta:
    branch: develop
    commands:
      - bundle exec fastlane ios beta
      - bundle exec fastlane android beta
  release:
    branch: master
    commands:
      - bundle exec fastlane ios release
      - bundle exec fastlane android release

Deploy Android - circle.yml

 

machine:
  environment:
    PATH: "${PATH}:${HOME}/${CIRCLE_PROJECT_REPONAME}/node_modules/.bin:${HOME}/.config/yarn/global/node_modules/.bin"
    GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError"'
dependencies:
  pre:
    - brew install node
    - brew install gradle
    - brew install caskroom/cask/android-sdk
    - sdkmanager "platforms;android-25" "build-tools;25.0.3"
    - npm install -g npm
    - npm install -g yarn
    - yarn global add cordova
    - yarn global add ionic
    - ionic info
    - sudo gem update --system
    - sudo gem install
bundlercache_directories:
    - ~/Library/Caches/Yarn/
test:
  override:
    - bundle exec fastlane test
deployment:
  beta:
    branch: develop
    commands:
      - bundle exec fastlane android beta
  release:
    branch: master
    commands:
      - bundle exec fastlane android release
  • Circle CI の Mac OS には Android SDK が含まれていない
  • Android SDK API 25 には gradle が
    含まれていない

Deploy Android - Fastfile

 

fastlane_version "2.48.0"
platform :android do

  desc "Setup a target platform"
  lane :setup do |options|
    sh "ionic platform add android"
    # build-extras.gradle をコピー
    sh "cp ../build/build-extras.gradle ../platforms/android/"
  end

  desc "Submit a new Beta Build to Beta by Crashlytics"
  lane :beta do
    setup(inhouse: true)
    # ビルド
    sh "ionic build android --device --prod --debug"
    # Crashlytics にアップロード
    crashlytics(apk_path: "platforms/android/build/outputs/apk/android-armv7-debug.apk")
  end

  desc "Deploy a new version"
  lane :release do
    setup()
    # ビルド
    sh "ionic build android --device --prod --release"
    # Google Play にアップロード
    supply(
    apk_paths: "platforms/android/build/outputs/apk/android-armv7-debug.apk",
    skip_upload_metadata: true,
    skip_upload_images: true,
    skip_upload_screenshots: true
    )
  end
end

after_all do |lane|
    slack(message: "Successfully deployed new App Update.")
end

error do |lane, exception|
    slack(message: exception.message, success: false)
end

Deploy Android - extra-gradle.build

 

ext.cdvBuildMultipleApks = true
ext.cdvCompileSdkVersion = 25
ext.cdvBuildToolsVersion = "25.0.3"
ext.postBuildExtras = {
    android {
        dexOptions {
            preDexLibraries = false
        }
        defaultConfig {
            multiDexEnabled = true
        }
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_7
            targetCompatibility JavaVersion.VERSION_1_7
        }
        allprojects {
            compileOptions {
                sourceCompatibility = JavaVersion.VERSION_1_7
                targetCompatibility = JavaVersion.VERSION_1_7
            }
        }
    }
}

Cordova hooks

Cordova hooks - mv extra-build.gradle

module.exports = function(ctx) {
  // make sure android platform is part of build
  if (ctx.opts.platforms.indexOf('android') < 0) {
    return;
  }
  var fs = ctx.requireCordovaModule('fs'),
  path = ctx.requireCordovaModule('path'),
  deferral = ctx.requireCordovaModule('q').defer();

  var extraGradle = path.join(ctx.opts.projectRoot, 'build-extras.gradle');
  var platformRoot = path.join(ctx.opts.projectRoot, 'platforms/android');
  var newExtraGradle = path.join(platformRoot, 'build-extras.gradle');

  var r = fs.createReadStream(extraGradle);
  var w = fs.createWriteStream(newExtraGradle);

  r.on("error", function (err) {
    deferral.reject('Failed to move extra-gradle');
  });
  w.on("error", function (err) {
    deferral.reject('Failed to move extra-gradle');
  });
  w.on("close", function (ex) {
    console.log('Move extra-gradle to' + newExtraGradle);
    deferral.resolve();
  });
  r.pipe(w);

  return deferral.promise;
};

Reffered to

Hibeeを開発/運用してみて

Pros:

  • 1コードで両 OS + タブレットに対応できる
  • デザインの自由度が高い
  • 開発速度はネイティブより早い

Cons:

  • プロダクションビルドは躓くことも多い…
    • ES2015 (ES6)  , asyn/await 多用時の babili/uglifyjs#harmony のエラー (ES7 未対応)
  • 実機デバッグがしづらい
  • 各 OS の最新機能が使えない

Android O App shortcuts

iOS 11 SiriKit

Native or Hybrid?

Native or Hybrid?

  • Case by Case
    • OS 固有の機能を使いたい -> Native App
    • とにかくサクッと両 OS 作りたい -> Hybrid App
  • Hybrid App でリリース後 Native App に乗り換える選択肢
    • ユーザが増えて規模が大きくなり、
      端末特有の問題が目立つようになった
    • OS 固有の機能を使いたい
    •  Apple Watch / Android wear / TV などに対応したい

We're hiring

Excite Japan Co., Ltd.

Contact : @karage

https://ionic2.slack.com