cordova8 で plugin addエラー 最低限のpackage.jsonが必要

cordova7まではpluginのpackage.jsonなしでもプラグインの追加を行えた。cordova8からはpackage.jsonの中も見てるようで最低限のpackage.jsonが必要になったぽい。

package.json

{
  "name": "my_plugin",
  "version": "0.0.1",
  "description": ""
}

試したらnameとversionとdescriptionだけあればokだった。メンテされてないプラグインはどうすればいいのだろう。いちおうエラー内容もメモしておく

package.jsonなしの場合

$ ionic cordova plugin add /path/to/cordova-plugin-my-plugin/
> cordova plugin add /path/to/cordova-plugin-my-plugin/ --save
(node:20136) UnhandledPromiseRejectionWarning: CordovaError: Invalid Plugin! /path/to/cordova-plugin-my-plugin needs a valid package.json
    at /Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/cordova/node_modules/cordova-lib/src/plugman/fetch.js:92:41
    at _fulfilled (/Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/cordova/node_modules/cordova-lib/node_modules/q/q.js:787:54)
    at self.promiseDispatch.done (/Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/cordova/node_modules/cordova-lib/node_modules/q/q.js:816:30)
    at Promise.promise.promiseDispatch (/Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/cordova/node_modules/cordova-lib/node_modules/q/q.js:749:13)
    at /Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/cordova/node_modules/cordova-lib/node_modules/q/q.js:509:49
    at flush (/Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/cordova/node_modules/cordova-lib/node_modules/q/q.js:108:17)
    at _combinedTickCallback (internal/process/next_tick.js:131:7)
    at process._tickCallback (internal/process/next_tick.js:180:9)
    at Function.Module.runMain (module.js:695:11)
    at startup (bootstrap_node.js:191:16)
(node:20136) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejec\
tion id: 1)
(node:20136) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

package.jsonに不備の場合

$ ionic cordova plugin add /path/to/cordova-plugin-my-plugin/
> cordova plugin add /path/to/cordova-plugin-my-plugin/ --save
(node:20157) UnhandledPromiseRejectionWarning: CordovaError: Error: npm: Command failed with exit code 1 Error output:
npm ERR! code EINVALIDTYPE
npm ERR! typeerror Error: Argument #2: Expected string but got null
npm ERR! typeerror     at exports.findRequirement (/Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/npm/lib/install/deps.js:721:3)
npm ERR! typeerror     at findChild (/Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/npm/lib/install/deps.js:109:17)
npm ERR! typeerror     at computeMetadata (/Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/npm/lib/install/deps.js:119:9)
npm ERR! typeerror     at Installer.normalizeCurrentTree (/Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/npm/lib/install.js:408:3)
npm ERR! typeerror     at Array.<anonymous> (/Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/npm/node_modules/slide/lib/bind-actor.js:15:8)
npm ERR! typeerror     at LOOP (/Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/npm/node_modules/slide/lib/chain.js:15:14)
npm ERR! typeerror     at /Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/npm/node_modules/slide/lib/chain.js:18:7
npm ERR! typeerror     at iferr (/Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/npm/lib/install.js:362:5)
npm ERR! typeerror     at a (/Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/npm/node_modules/iferr/iferr.js:3:64)
npm ERR! typeerror     at cb (/Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/npm/node_modules/slide/lib/async-map.js:47:24)
npm ERR! typeerror     at /Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/npm/node_modules/call-limit/call-limit.js:22:12
npm ERR! typeerror     at _combinedTickCallback (internal/process/next_tick.js:131:7)
npm ERR! typeerror     at process._tickCallback (internal/process/next_tick.js:180:9)
npm ERR! typeerror This is an error with npm itself. Please report this error at:
npm ERR! typeerror     <https://npm.community>

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/hogehoge/.npm/_logs/2018-08-16T06_30_08_468Z-debug.log
    at /Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/cordova/node_modules/cordova-lib/src/plugman/fetch.js:112:45
    at _rejected (/Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/cordova/node_modules/q/q.js:864:24)
    at /Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/cordova/node_modules/q/q.js:890:30
    at Promise.when (/Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/cordova/node_modules/q/q.js:1142:31)
    at Promise.promise.promiseDispatch (/Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/cordova/node_modules/q/q.js:808:41)
    at /Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/cordova/node_modules/q/q.js:624:44
    at runSingle (/Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/cordova/node_modules/q/q.js:137:13)
    at flush (/Users/hogehoge/.nodebrew/node/v8.11.3/lib/node_modules/cordova/node_modules/q/q.js:125:13)
    at _combinedTickCallback (internal/process/next_tick.js:131:7)
    at process._tickCallback (internal/process/next_tick.js:180:9)
(node:20157) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejec\
tion id: 1)
(node:20157) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

以上です

【ionic3】iOSでvideoインライン再生

ionic3でvideoタグのインライン再生しようとしたら上手くいかなかったのでそのときのメモ

HTMLはこんな感じ

<video muted="true"                                                                                                                                                                                                
       preload="auto"                                                                                                                                                                                              
       webkit-playsinline playsinline controls                                                                                                                                                                     
       src="assets/imgs/sample.mp4">                                                                                                                                                                               
</video>

調べてみると wkwebview の設定で allowsInlineMediaPlayback という設定があった。
CDVWKWebViewEngine.m を見てると allowsInlineMediaPlayback を設定している箇所もあった。

config.xml

<preference name="AllowInlineMediaPlayback" value="true" />

これだけでした。以上です

【Ionic3(Angular)】Componentを作成してHTMLを分割する方法まとめておく

はじめに

今回やりたかったことは以下のようなコードの「リスト」の部分が複雑になったので別ファイルに分けたいということでした。AngularのComponentを作成すれば実現できるようでIonicコマンドでComponentを作成できるのでこちらの使い方をメモしておく。

<ion-content>
  <!-- タイトル -->
  <div>Title</div>

  <!-- リスト -->
  <ion-list>
    <ion-item>Item1</ion-item>
    <ion-item>Item2</ion-item>
    ・・・
  </ion-list>

</ion-content>

やりたいことは以下
・ファイルを分割したい
・ionicのコンポーネントを使いたい
・呼びだし側と呼ばれる側とで値を共有したい

実装

Componentを作成

$ ionic g component mylist

src/componentsディレクトリが作成されてひな形が作成される

components.module.ts

import { NgModule } from '@angular/core';
import { MyListComponent } from './my-list/my-list';

@NgModule({
  declarations: [MyListComponent],
  imports: [],
  exports: [MyListComponent]
})

export class ComponentsModule {}

さらに src/components/my-list ディレクトリも作成されて html、scss、tsファイルのひな形が作成される。これをアプリから呼びだすことができるようにapp/app.module.tsに以下を追記

app.module.ts

・・・
import { ComponentsModule } from '../components/components.module' // ★追記
・・・

@NgModule({
  ・・・
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp),
    ComponentsModule, // ★追記
  ]
})

これで追加したmy-listコンポーネントを以下の形でよびだせるようになる。

<ion-content>
  <!-- タイトル -->
  <div>Title</div>

  <!-- リスト -->
  <my-list></my-list>

</ion-content>

Ionicコンポーネントを呼びだせるようにする

続いてmy-listコンポーネント実装していく。ionicのコンポーネント(ion-list)を使いたいのでcomponents.module.tsにIonicModuleを追記

・・・
import { IonicModule } from 'ionic-angular' // ★追記
・・・
@NgModule({
  declarations: [MyListComponent],
  imports: [IonicModule],
  exports: [MyListComponent]
})

components/my-list/my-list.html

<ion-list>
    <ion-item>Item1</ion-item>
    <ion-item>Item2</ion-item>
    ・・・
</ion-list>

呼び出し側と呼ばれる側とで値を共有する

値の共有の例として以下を実装する
・ion-listに表示する値をコンポーネントに渡す
・ion-listでクリックされた行を親に渡す

AngularのngIf、ngForを使えるようにする

追加したComponentでAngularのngIfやngForを使いたい場合はこれらも使えるようにBrowserModuleを追加する必要がある。

components/components.module.ts

import { BrowserModule } from '@angular/platform-browser';
・・・

@NgModule({
  ・・・
  imports: [BrowserModule, IonicModule],
  ・・・
})
コンポーネントに値を渡す(コンポーネントが値を受け取る)

受け取る側

components/my-list/my-list.ts

import { Component, Input } from '@angular/core';

・・・

export class MyListComponent {

  @Input() items: any

}

component/my-list/my-list.html

<ion-list>
  <ion-item *ngFor="let item of items">{{ item.name }}</ion-item>
</ion-list>

渡す側

xxx.ts

export class Hoge {

  items: any = [
    { id: 1, name: "Item1"},
    { id: 2, name: "Item2"},
    ・・・
  ]

}

html

<ion-content>
  <!-- タイトル -->
  <div>Title</div>

  <!-- リスト -->
  <my-list [items]="items"></my-list>

</ion-content>
コンポーネントから値を受け取る

渡す側

components/my-list/my-list.ts

import { Component, Output } from '@angular/core';

・・・

export class MyListComponent {

  @Output() selected = new EventEmitter<number>()

  ・・・

  select = (id: number) => {
    this.selected.emit(id)
  }

}

component/my-list/my-list.html

<ion-list>
  <ion-item *ngFor="let item of items" (click)="select(item.id)">{{ item.name }}</ion-item>
</ion-list>

受け取る側

xxx.ts

export class Hoge {

  select = (id) => {
    console.log(id)
  }

}

html

<ion-content>
  <!-- タイトル -->
  <div>Title</div>

  <!-- リスト -->
  <my-list (selected)="select($event)"></my-list>

</ion-content>

ずらずら書いたけど以上です。

Ionic3とAngularのバージョン確認

IonicはCLIフレームワークとでそれぞれバージョン違うし。Angularはバージョンどんどん上がってる印象なので。自分がそれぞれどのバージョン使ってるかわからなくなってきた。

CLIのバージョン

$ ionic -v
3.20.0

FWのバージョンはpackage.jsonを見るとわかる

{
  ・・・

  "dependencies": {
    "@angular/animations": "5.2.11",
    "@angular/common": "5.2.11",
    "@angular/compiler": "5.2.11",
    "@angular/compiler-cli": "5.2.11",
    "@angular/core": "5.2.11",
    "@angular/forms": "5.2.11",
    "@angular/http": "5.2.11",
    "@angular/platform-browser": "5.2.11",
    "@angular/platform-browser-dynamic": "5.2.11",

    ・・・

    "ionic-angular": "3.9.2",

    ・・・
  },

  ・・・
}

Angularが5.2.11で、Ionicが3.9.2だった。以上です

CircleCI2、Fastaneでの2段階認証が設定されたアップルIDでのアプリアップロードを諦めた話

fastlaneのドキュメントに2段階認証のことものってた
https://docs.fastlane.tools/best-practices/continuous-integration/

環境変数 FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD に2段階認証の場合はAPPパスワードを発行してセットするように書いてあったのだがやってみてもうまくいかず。。

deliverでiTtunesConnectにアップロードしようとするところで、こんなエラーで落ちる。ソース追う気力もないけど2段階認証のところでなんかやろうとして落ちてる感じだ。

/Library/Ruby/Gems/2.0.0/gems/highline-1.7.10/lib/highline/question.rb:413:in `remove_whitespace': [!] undefined method `strip' for nil:NilClass (NoMethodError)
        from /Library/Ruby/Gems/2.0.0/gems/highline-1.7.10/lib/highline.rb:873:in `get_line'
        from /Library/Ruby/Gems/2.0.0/gems/highline-1.7.10/lib/highline.rb:891:in `get_response'
        from /Library/Ruby/Gems/2.0.0/gems/highline-1.7.10/lib/highline.rb:264:in `ask'
        from /Library/Ruby/Gems/2.0.0/gems/highline-1.7.10/lib/highline.rb:365:in `choose'
        from /Library/Ruby/Gems/2.0.0/gems/fastlane-2.61.0/spaceship/lib/spaceship/two_step_client.rb:35:in `handle_two_step'
        from /Library/Ruby/Gems/2.0.0/gems/fastlane-2.61.0/spaceship/lib/spaceship/client.rb:473:in `send_shared_login_request'
        from /Library/Ruby/Gems/2.0.0/gems/fastlane-2.61.0/spaceship/lib/spaceship/tunes/tunes_client.rb:111:in `send_login_request'

        (省略)

        from /usr/local/bin/fastlane:23:in `load'
        from /usr/local/bin/fastlane:23:in `<main>'

ドキュメントの冒頭に以下の記述があった

The easiest way to get fastlane running on a CI system is to create a separate Apple ID that doesn't have 2-factor auth enabled, with a long, randomly generated password. Additionally make sure the newly created Apple account has limited\
 access to only the apps and resources it needs.

「2段階認証を無効にしたApple IDを別途作成して、必要な権限を与えて使ってください」とのことでした。説明見てるとできそうだけど、時間かけてもあまり意味もないと思うので今回は諦めました。以上です

ionic(cordova) CircleCI2とFastlaneでiOSアプリをデプロイゲートにアップロードする手順まとめ

はじめに

今回やりたかったことは
・ionicで開発したiOSアプリをCircleCI2上でAdHoc版ビルド
・ビルドしたアプリをデプロイゲートへアップロード
・Fastlane Matchは使わず手元にある証明書とプロビジョニングプロファイルで行う
ビルドとアップロードはFastlaneというツールを使う

必要なもの
・証明書
キーチェーンアクセスから書き出す「iPhone Distribution:xxxxxxxxxxx(xxxxxx)」いつものp12ファイル。
・プロビジョニングプロファイル
・デプロイゲートのAPIキーとアカウント名

流れ

CircleCI上で証明書等を予め登録しておく
・証明書とプロビジョニングプロファイルをCircleCI上に暗号化して登録(環境変数を利用)
・デプロイゲートのアカウントとAPIキーもCircleCI上に登録(環境変数を利用)
https://circleci.com/gh/[githubアカウント]/[リポジトリ名]/edit#env-vars
↑ここから登録できる

以下を用意
・.circleci/config.yml
・fastlane/Fastfile

イメージとしては普段macでおこなっている作業をCircleCI上のmacで行うだけ。
・cordova、ionic と依存するnode_modulesのインストール
・予め登録した証明書とプロビジョニングプロファイルを復号化してマシンにインストール
・インストールした証明書等を使ってアプリをビルド
・予め登録したデプロイゲートの情報を使って、デプロイゲートへアップロード

ハマったこと

証明書とプロビジョニングプロファイルを正しく指定してるのにSigning Errorが消えない

エラー

Code Signing Error: Signing for "circleci_demo" requires a development team. Select a development team in the project editor.
Code Signing Error: Code signing is required for product type 'Application' in SDK 'iOS 11.2'

xcodeでいうところの「Automatically manage signing」がONになっている状態だとエラーになってしまっていた。
ONが奨励されてる?のかわからないけど今回はこれをOFFにすることで解決した。
後述するFastlane の disable_automatic_code_signing メソッドを使うとOFFになる。

cordova-iosが生成するxcodeの project.pbxproj ファイルの形式が不完全

エラー

Seems to be a very old project file format - please open your project file in a more recent version of Xcode

TargetAttributes属性がないとFastlaneがこのエラーで終了してしまう。
Cordovaで生成した直後のプロジェクトだとこの属性がないらしい(最新のcordova-iosでは改善してるかも)
で、platforms/ios/circleci_demo.xcodeproj/project.pbxproj もgitで管理してビルドする前にこのファイルを使うことで解決することにした。

アーカイブに成功してエクスポートでエラー

エラー

Exit status: 70
No provisioning profile provided
Make sure to pass a valid provisioning for each required target
Check out the docs on how to fix this: https://github.com/fastlane/fastlane/tree/master/gym#export-options

よくわかってないのだが、xcodebuildでのアーカイブで出力されるplistをエクスポートの際に使用するようなのだが、それが正しくないらしい。スタックオーバーフローとか見てると、xcode上でエクスポートする際に作られるExportOptions.plistを使うと良いらしい。これもGitで管理するようにしてFastfileで指定することで解決した。gymのexport_optionsで指定可能でした。

実装

証明書とプロビジョニングプロファイルをCircleCI2に登録
$ base64 -i xxxxx.p12 | pbcopy
$ base64 -i xxxxx. mobileprovision | pbcopy

こちらのページに細かく書かれている(というかまんま)
http://developabout0309.blogspot.com/2018/05/circleci20fastlanefabric_1.html

今回は以下の変数名で登録した
CERTIFICATES : pm12ファイル
PROVISIONING_PROFILES : プロビジョニングプロファイル
DEPLOYGATE_USER : デプロイゲートのアカウント
DEPLOYGATE_API_KEY : デプロイゲートのAPIキー

.circleci/config.yml
version: 2
jobs:
  build:
    macos:
      xcode: "9.2.0"
    shell: /bin/bash --login
    working_directory: ~/workspace
    steps:
      - checkout
      - run:
          name: install ionic
          command: |
            # Ionicインストール
            npm install -g cordova@6.5.0
            npm install -g ionic@2.2.3                                                                                                                                                                             
            npm install                                                                                                                                                                                            
                                                                                                                                                                                                                   
            # 最新のcordova-iosでプロジェクト作成                                                                                                                                                                  
            ionic platform rm ios                                                                                                                                                                                  
            ionic platform add ios@4.5.4                                                                                                                                                                           
            ionic prepare                                                                                                                                                                                          
      - run:
          name: build and upload deploygate
          command: |
            # stagingブランチの場合、デプロイゲートへアップロード
            if [ "${CIRCLE_BRANCH}" == "staging" ]; then                                                                                                                                                           
              # 証明書をデコード
              base64 -D -o circleci_demo.p12 <<< $CERTIFICATES                                                                                                                                                     
                                                                                                                                                                                                                   
              # プロビジョニングプロファイルのインストール                                                                                                                                                         
              mkdir -pv ~/Library/MobileDevice/Provisioning\ Profiles/                                                                                                                                             
              base64 -D -o ~/Library/MobileDevice/Provisioning\ Profiles/CircleCI_Adhoc.mobileprovision <<< $PROVISIONING_PROFILES                                                                                 
                                                                                                                                                                                                                   
              # xcodeprojファイルをチェックアウト                                                                                                                                                                  
              git checkout platforms/ios/circleci_demo.xcodeproj/project.pbxproj                                                                                                                                   
                                                                                                                                                                                                                   
              # AdHocビルド                                                                                                                                                                                        
              fastlane ios build                                                                                                                                                                                   
                                                                                                                                                                                                                   
              # デプロイゲートへアップロード                                                                                                                                                                       
              fastlane ios upload_deploygate                                                                                                                                                                       
                                                                                                                                                                                                                   
            # masterブランチの場合、AppStoreへアップロード                                                                                                                                                         
            elif [ "${CIRCLE_BRANCH}" == "master" ]; then                                                                                                                                                          
            fi                                                                                                                                                                                                     
fastlane/Fastfile
platform :ios do
  desc "Build for iOS"
  lane :build do
    if is_ci?
      setup_circle_ci
      import_certificate(
        keychain_name: ENV["MATCH_KEYCHAIN_NAME"],
        keychain_password: ENV["MATCH_KEYCHAIN_PASSWORD"],
        certificate_path: 'circleci_demo.p12',
        certificate_password: ''
      )
    end
    update_app_identifier(
      xcodeproj: "platforms/ios/circleci_demo.xcodeproj",
      plist_path: "circleci_demo/circleci_demo-Info.plist",
      app_identifier: 'ciecleci.test'
    )
    disable_automatic_code_signing(
      path: "platforms/ios/circleci_demo.xcodeproj"
    )
    gym(
      scheme: 'circleci_demo',
      workspace: 'platforms/ios/circleci_demo.xcworkspace',
      configuration: 'Debug',
      export_method: "ad-hoc",
      clean: true,
      verbose: true,
      output_directory: "build",
      output_name: "circleci_demo",
      xcargs: "OTHER_SWIFT_FLAGS='$(inherited) -DSTGING' PROVISIONING_PROFILE_SPECIFIER='CircleCI Adhoc' CODE_SIGN_IDENTITY='iPhone Distribution'",
      export_xcargs: "-allowProvisioningUpdates",
      export_options: "fastlane/ExportOptions.plist"
    )
  end
  desc "Upload To Deploygate"
  lane :upload_deploygate do
    deploygate(
      api_token: ENV["DEPLOYGATE_API_KEY"],
      user: ENV["DEPLOYGATE_USER"],
      ipa: "build/circleci_demo.ipa"
    )
    sh "rm -rf ../build"
  end
end

こんなもんかな。以上です。

シンボリックリンクを相対パス指定で作る

基本

$ ln -s 実際のパス リンク

こんなファイル構成でdir2/aaa.txtにdir1/aaa.txtへのシンボリックリンクを貼りたい

dir1/aaa.txt
dir2

これでいけると思ったらNG

$ ln -s dir1/aaa.txt dir2/aaa.txt

これならOK

$ ln -s ../dir1/aaa.txt dir2/aaa.txt

リンクからの相対パスを指定しないとダメなのか。たまにしかやらないから忘れる。以上です。