iTunes Connect に Upload 後の bitcode 再コンパイルでエラーが出た

iTunes Connect にバイナリのアップロードは完了したんだけど、 iTunes Connect: Processing stopped for HogeApp みたいなメールが来た。 何か問題があって bitcode の再コンパイルが止まってしまったようだ。

メール全文:

iTunes Connect: Processing stopped for HogeApp

Dear Kensuke Hoshikawa,

While processing your iOS app, HogeApp, errors occurred in the app thinning process, and your app couldn’t be thinned. If your > app contains bitcode, bitcode processing may have failed. Because of these errors, this build of your app will not be able to be submitted for review or placed on the App Store. For information that may help resolve this issue, see Tech Note 2432.

Regards,

The App Store team

解決策: 再アップロード

なんとなく再度バイナリアップロードしたら直る気がしたので、再アップロードした。
そしたら次は has completed processing の連絡が来た。

まあよくありますよね、 Apple 様の機嫌が悪かったようだ。

Tech Note 2432

メール本文には Tech Note 2432 に対処法が書いてあるから見てくれって書いてある。
今回は再アップロードで済んだけど、本当に何か問題がある場合は修正が必要になりそう。

参考

iTunes Connect に Upload しようとしたら Info.plist 関連で怒られた

f:id:star__hoshi:20171202012835p:plain

Unable to process application at this time due to the following error: This bundle is invalid. The Info.plist file is missing or could not be parsed. Please check it for embedded control characters..

iTunes Connect にバイナリアップロードしようとしたらこんなエラーがでた。
Info.plist が見つからないか、パースができないよ、と書かれている。

しかし Info.plist は問題ないし plist のフォーマットも問題なさそう。

ググる

ここに書いてる問題ではなさそう...。

LaunchScreen

Info.plist を眺めてみると、UILaunchStoryboardNameLaunch Screen になっている、正しくは LaunchScreen だった。
このスペース 1 つでエラーが出ていた、スペースを消したら無事提出ができた。

The Info.plist file is missing or could not be parsed. と書いてあって、 Info.plist そのものには問題がなくても UILaunchStoryboardName などがちゃんと設定されてないとうまく読み込めずこのエラーが出てしまうようなので注意しましょう。

console.log で改行させたくない

node: v8.2.1 でのはなし。

JavaScript でログを出力するときは console.log が一般的だけど、これだと改行が入ってしまう。

console.log('hoge')
console.log('hoge')

# hoge
# hoge

そうじゃなくて、 hogehoge って続けてログ出力してほしい。

ググる

node.jsで標準出力に改行なしで出力する - Qiita

という記事が出て来た。

util.print

sys モジュールは deprecated なんで、util モジュールを使いましょう。

なるほど、 util なんてもんがあるのか、と思って使ってみる。

const util = require('util')

util.print('hoge')
util.print('hoge')

# hogehoge(node:67917) [DEP0026] DeprecationWarning: util.print is deprecated. Use console.log instead.

hogehoge って出たけど Warning も出ている。

The util.print() API is deprecated. Please use console.log() instead. Deprecated APIs | Node.js v9.2.0 Documentation

なるほど util.print() は deprecated だから console.log() を使えと...。

process.stdout.write

process.stdout.write('hoge')
process.stdout.write('hoge')

# hogehoge

これで良さそう。

Node.jsのconsole.logとprocess.stdout.writeの違い - Tatsuya Oiwa を読むに、 console.log は process.stdout.write のラッパーっぽいので信用して大丈夫そうだ。

Stripe の Order で送料を商品によって変更するときは Callback API を実装する必要がある

Stripe の Order で送料を設定するパターンは 4 つある。

  • Free
  • Flat-rate
  • Callback
  • Provider

f:id:star__hoshi:20171129120506p:plain

Stripe Document

この情報は 2017-11-29 時点の情報であり、正確な情報は Orders API Dynamic Shipping and Tax Calculation を参照してほしい。

Order の種類

Order は sku に対し行うもので、初期状態だと送料を設定できない。
送料を設定するには https://dashboard.stripe.com/account/relay/settings の設定を変更する。

Free

送料無料。デフォルトでこれ。

Flat-rate

一律同じ送料。商品の個数や配達先に関係なく送料が同じになる。

Provider

サードパーティの配達先と連携できる。
海の向こうだとこういうサービスが整っていて、 API 投げたらよしなに送料など計算して配送してくれるようだ。

Callback

一番自由度が高く、しかし実装コストが面倒。コールバック URL を指定する必要がある。

流れとしてはこんな感じになる。

  1. ユーザが商品を購入する
  2. Stripe で Order.create する
  3. Stripe から送料いくらにする? って聞かれる
    • Callback API が叩かれるので、 API を用意しておく
  4. 送料が設定され Order.create が完了する

Callback API

コードは雑なんだけど、ざっくりこんな感じで実装する、TypeScript で書いてある。
Stripe から飛んできた Order に対し送料を渡して Update する。

export const orderCallback = functions.https.onRequest(async (request, response) => {
  if (request.method.toLowerCase() !== 'post') { response.status(405).end() }

  const stripeOrderID: string = request.body.order.id
  const itemIDs: string[] = request.body.order.metadata.itemIDs
  const items: Item[] = itemIDs.map(itemID => {
    // 送料などの情報を持った Item を取得
  }

  const shippingMethods = items.map(item => {
    return {
      id: item.id,
      amount:item.postage,
      description: item.shippingMethod,
      currency: 'jpy'
    }
  })

  const json = {
    order_update: {
      order_id: stripeOrderID,
      shipping_methods: shippingMethods
    }
  }

  response.status(200).json(json)
})

雑実装なので、 Production 入れる前にもっとエラーハンドリングしないとまずいですね。

おわり

Order するときに shipping_methods も渡せればいいのにと思うけど、まあ色々事情があるのだと思う。

ちなみにこの情報は https://stripe.com/docs を眺めていてもどこにもリンクがなく、 Stripe の設定画面を眺めていたら配送設定みたいなのがあって、そこから document にたどり着いた。
重要な情報だし最初からリンク載せておいてくれ〜...

Xcode の Other Swift Flag で -D STAGING を指定したのに #if STAGING が有効にならなかった

Xcode で環境別にコンパイルするために Staging の Configuration を作ったんだけど、 -D STAGING を指定しても #if STAGING が false になっていた。

f:id:star__hoshi:20171127214536p:plain

なんでかな〜 って設定眺めていたら、 Active Compilation Conditions が Debug になっていたのが原因だった。

f:id:star__hoshi:20171127215036p:plain

これを STAGING にしたら #if STAGING がうまくいった。

f:id:star__hoshi:20171127215120p:plain

Rails 開発で GitHub に Push して CircleCI のテストが通ったら Heroku に deploy する

CircleCI を使って、テストが通ったら Heroku に deploy するという流れを構築する。

サンプルリポジトリ: starhoshi/rails-circleci

環境

  • GitHub
  • Rails 5.X
  • Heroku
  • CircleCI 1.X
    • ここでのサンプルは 2.0 ではない

ソースコードstarhoshi/rails-circleci

まず rails new

$ gem install rails
$ rails new . --api -d=postgresql
$ rails g controller

これで Rails Project + users_controller が作成される。
また、コメントアウトされている test のコメントを外し、通るようにする。

test/controllers/users_controller_test.rb

require 'test_helper'

class UsersControllerTest < ActionDispatch::IntegrationTest
  test "the truth" do
    assert true
  end
end

GitHub に Push

GitHub に Repository を作って、 Push しておく

CircleCI と Repository の連携

CircleCI に GitHub でログインしておく。
CircleCIでRails4アプリをHerokuへデプロイする - Qiita にあるように、 ssh key の登録が必要かもしれない。

Projects > Add Project > rails-circleci を Set Up する。

f:id:star__hoshi:20171126170222g:plain

そうすると勝手に CircleCI が回り出して、テストが成功する。

Heroku

Heroku App 作成

こんな感じで作成する。

f:id:star__hoshi:20171126170458p:plain

API Key

https://dashboard.heroku.com/account から API Key を取得

f:id:star__hoshi:20171126170540p:plain

CircleCI の Heroku Deployment 設定

https://circleci.com/account/heroku に取得した API Key を設定する。

f:id:star__hoshi:20171126170746g:plain

これで Heroku / CircleCI の設定が完了。

circle.yml

deployment:
  production:
    branch: master
    commands:
      - heroku maintenance:on --app rails-circleci
      - git push git@heroku.com:rails-circleci.git $CIRCLE_SHA1:refs/heads/master
      - heroku run 'rake db:migrate; rake db:seed_fu' --app rails-circleci
      - heroku maintenance:off --app rails-circleci

こんな感じで作成。 --app と git@heroku.com:rails-circleci.git は自分の環境に合わせる。

git push すると CircleCI が動いて Heroku Deploy される

git push すると自動で CircleCI が動いて、 test が実行されたあとに deploy される。

f:id:star__hoshi:20171126171100p:plain

これでめでたく当初やりたかったことができた。

test がコケたらどうなる?

Not Deploying になって、 deploy がされないことがわかる。

f:id:star__hoshi:20171126171248p:plain

bundle update のプルリクエストを毎日自動で作成する

Gemfile.lock を最新に保つため、bundle update を毎日自動でできるようにしたい。
Tachikoma.io というサービスもあるみたいだけど、 private repo は有料っぽいので自作した。

勝手に update されてアプリケーションがバグると困るので、

  1. Gemfile.lock を更新したプルリクエストを作る
  2. プルリクエストを人間が確認してマージ

という手順で行う。

先人の知恵

ここら辺を参考にした。

環境

  • CircleCI 1.X
    • 1年前に構築した環境なので 2.0 ではない...
  • GitHub Private Repository
  • Heroku
  • Rails 5.X

流れ

  1. Heroku Scheduler が CircleCI を叩く
  2. CircleCI で bundle update を実施
  3. Gemfile.lock 更新があれば CircleCI が GitHub に PR を作成
  4. PR を人間が確認してマージボタンをポチ

1. 前準備

2. rake task 作成

lib/tasks/bundle_update_pull_request.rake を作成する。

# frozen_string_literal: true
namespace :bundle_update_pull_request do
  desc 'pull request if bundle updated'

  task check: :environment do
    project = 'repo-team/repo-rails'
    branch = 'master'
    token = 'CircleCI Token'
    params = '{"build_parameters": {"BUNDLE_UPDATE": true}}'

    header = { 'Content-Type' => 'application/json' }
    client = Net::HTTP.new('circleci.com', 443)
    client.use_ssl = true
    response = client.post("/api/v1/project/#{project}/tree/#{branch}?circle-token=#{token}", params, header)
  end
end

CircleCI の Nightly Builds を叩くだけの task。
params に '{"build_parameters": {"BUNDLE_UPDATE": true}}' を渡していて、 CircleCI ではこのパラメータをみて bundle update するのか判断する。

この task は以下のコマンドで実行できる。

$ bundle exec rake bundle_update_pull_request:check

3. circle.yml

circle.yml をこんな感じで書く。

test:
  post:
    - >
      if [ -n "${BUNDLE_UPDATE}" -a "${CIRCLE_BRANCH}" = 'master' ] ; then
        bundle update
      fi

deployment:
  master:
    branch: master
    commands:
      - >
        if [ -n "${BUNDLE_UPDATE}" ] ; then
          bash script/circleci/create_pull_request_if_needed.sh
        fi

2 で params に渡した BUNDLE_UPDATE が true だったら bundle update 実行 + PR 作成するようになっている。

4. create_pull_request_if_needed.sh

script/circleci/create_pull_request_if_needed.sh を作成する。

#!/bin/bash

export BRANCH=bundle-update-`date -u "+%Y%m%d"`
if [[ -n `git status -sb 2> /dev/null | grep Gemfile.lock` ]] ; then
  git config --global user.email git@git.com
  git config --global user.name 'git'
  git add Gemfile.lock
  git commit -m 'Bundle update'
  git branch -M $BRANCH
  git push origin $BRANCH
  bundle exec ruby script/circleci/create_pull_request.rb
fi

3 で実行された bundle update で、 Gemfile.lock に差分があれば PR を作成するだけ。

5. create_pull_request.rb

script/circleci/create_pull_request.rb を作成。

# frozen_string_literal: true
require 'octokit'

client = Octokit::Client.new(access_token: 'GitHub Personal access tokens')
client.create_pull_request(
  'repo-team/repo-rails',
  'master',
  ENV['BRANCH'],
  'Bundle update',
  ''
)

これで PR が作成される。

ここまでで bundle exec rake bundle_update_pull_request:check で Bundle update の PR が自動で作成されるようになった。

6. Heroku Scheduler で定期実行

ここでは Heroku を使っているけど、定期実行できればなんでも良い。

f:id:star__hoshi:20171126030819p:plain

動かしてみる

こんな感じで GitHub に毎日 PR がくるようになります。

f:id:star__hoshi:20171126031005p:plain

これを人間が目視で確認して問題なければマージしましょう。

しかし、目視といっても version が変わったことしかわからないので、これを実際に運用するにはテストがしっかりと書かれていて、そのテストが壊れてないことを確認したらマージするようにしないとダメですね。

MusicPlayer で音楽を再生しようとしたら __abort_with_payload というエラーが出た

import MediaPlayer

...
    override func viewDidLoad() {
        super.viewDidLoad()
        let player = MPMusicPlayerController.systemMusicPlayer
        player.setQueue(with: MPMediaQuery.songs())
        player.play()
    }

これで音楽が再生できたんだけど、再生開始した瞬間にアプリがクラッシュした。どうやら権限が足りていないようだ。

端末の音楽情報を参照するには Info.plist に privacy を書かねばならない。

 <key>NSAppleMusicUsageDescription</key>
    <string>Reference music information to play music.</string>

こんな感じで NSAppleMusicUsageDescription に対してアクセスしたい理由を書けば OK。
(PropertyList で見る場合は Privacy - Media Library Usage Description になっている。)

参考: ios - App crashes when running on iPhone with violations as exception - Stack Overflow

TestFlight でリリースごとにアプリを配布していたらそれに助けられた

TestFlight という、アプリのベータ版などを配布する Apple 純正のサービスがある。

TestFlight - Apple Developer

これのいいところは「リリースするアプリと同じアプリをテストできる」というところ。
実際にユーザが使うアプリと同じもので動作確認ができるし、同じアプリファイルで Apple は審査を行うのでバグがあったら Self Reject もしやすい。

なので私はいつも

  1. iTunesConnect にアプリファイルを提出
  2. TestFlight で配布 & Apple 審査申請
  3. TestFlight で動作確認
  4. Apple 審査完了
  5. リリース

という流れでリリースをするようにしている。
(この流れは fastlane で自動化している fastlane-example/Fastfile, fastlane deliver を使ってコマンド1発で Waiting For Review までもっていく)

助けられた

先日アプリがクラッシュっするバグを出してしまって、そのバグの原因はアプリの過去バージョンに起因するものだった。
クラッシュする原因は以下の2つ。

  1. ver 3.0.0 が初回インストールだったユーザ
  2. ver 1.X.X -> ver 3.X.X に飛び越えてアップデートしたユーザ

今回 TestFlight に助けられたのは 2 のパターンの原因特定 / デバッグのところ。

TestFlight は 90 日過去バージョンをインストールできる

最初はクラッシュする原因が全然わからなかったんだけど、 TestFlight を見てたら過去バージョンがズラ〜っと並んでいて、なんとなく ver 1.X.X のをインストールした後に ver 3.0.1 をインストールしたらクラッシュして原因特定できた。

原因特定できた後も修正確認のために ver 1.X.X をインストールして、 ver 3.1.0 をその後インストールして動作が問題ないか、というテストができた。

もし TestFlight がなかったらデバッグが大変になってて、 ver 1.X.X をインストールするためには Xcode 8.X を持ってきて... というところから始めないといけなくなる。 AppStore からも基本的に過去 ver は DL できないし。

TestFlight で配布していたおかげで過去バージョンからのマイグレーションテストも手軽にできて助かった。

Apple 特急審査 2017

ここ最近は Apple の審査が早いけど、致命的なバグなどを出してしまったら急いでレビューして欲しい、そういう時のために apple expedited review というのが用意されている。

これを使うと優先的にアプリをレビューしてくれる。
しかしこれには注意が必要で、使いすぎてはいけないのでマジでやばい時にだけ使うべき。

今回はマジでやばいバグを生み出したので特急審査を出した。

f:id:star__hoshi:20171121021826p:plain

* マジでやばいバグの図

やり方

1. Contact Us

iTunes Connect にログインして右下の Contact Us をクリック

f:id:star__hoshi:20171121021855p:plain

2. Request an Expedited App Review

  1. App Review
  2. App Store Review
  3. Request Expedited Review

を選択すると特急審査へのリンクが表示されるので、 Request an Expedited App Review をクリック。

f:id:star__hoshi:20171121022059p:plain

3. Contact the App Review Team

なんか突然今っぽいデザインになる。
内容をいい感じに入力して、 Explanation に説明を書く。
ここはなぜ特急審査が必要なのか真面目に書いた方がいいと思う。

自分は Apple への連絡はいつも英語 + 日本語で投げるようにしている。(英語に自信がないので...)

f:id:star__hoshi:20171121022132p:plain

最後に Send を押すと「問い合わせ受け付けたよ!」みたいな画面が出ておわり。
なお、問い合わせ完了メールなどは来ないもよう。

あとは迅速に審査が終わることを祈るのみ、しかし日本時間15時に特急審査願いを出して12時間経っても音沙汰なし。
普通のレビューと大して変わらないのでは...?