tofucodes diary

にほんごのほう

WWDC 2019 - What's New in Xcode 11

developer.apple.com

エディタ関連

  • 画面分割がなんか賢くなる(あんまり興味ない)
  • エディタの右にファイルのミニマップが出せるようになる
    • 他のエディタではよく見るようなやつ
    • //MARK: - XXXでセクション分割もしてくれる
    • 行数が長いファイルだとちょっと便利かもしれない
  • 関数のドキュメントが賢くなる
    • 引数追加したら追加した引数の分だけドキュメントにも追加してくれる
    • 引数名を変更したらドキュメントも一緒に更新してくれる
  • バージョン管理が賢くなる
    • コードの変更した行の上に、変更前のコードをインラインで表示することができる

Swift Package関連

  • XcodeにSwift Packageを完全に統合する(Swift Package使ったことないから今までどうだったか知らんけど)
  • Project 設定画面に新たにSwift Packageタブが追加される
    • 連携してるバージョン管理システムのアカウントから楽にパッケージを追加できる
    • 自分のレポジトリ、自分が属してるOrganizationのレポジトリ、スターをつけてるレポジトリが一覧で表示されるので選ぶだけ

デザインツール関連

  • StoryboardでLight/Darkモードの表示をすぐに切り替えられるボタン
  • カスタムのシンボルを作成することができる
    • 既存のSF Symbolをカスタマイズするとかそれ系だと思う
  • AssetカタログでLight/Dark用の画像をそれぞれ登録することで自動で画像切り替えが可能になる
  • AssetカタログでLight/Dark用のColorをそれぞれ登録することで自動で色の切り替えが可能になる
    • 例えばButtonColorという1つの名前でLight=青、Dark=オレンジのように
  • Assetカタログで画像のローカライズが可能になる
    • 方法については特に言及されず?だけど多分上記と同じ感じの方法なんだろうと思う
  • 新機能Environment Overwrite
    • デバッグバーに新しいボタンが追加されて、ボタン押すとXcode上に色々な設定が書かれてるポップアップが出現する
    • シミュレータの設定を実際に変更しなくても、設定を変更した状態のUI確認ができる
    • Light/Darkモードの切り替え、テキストサイズの変更とかとか
    • いちいちシュミレータの設定をいじらなくても様々なUIの確認ができて、めちゃくちゃ便利だと思う

デバッグツール関連

  • ネットワークのスループットをシュミレートできるようになる
    • 今までNetwork Link Conditioner使ってやってたやつがXcodeだけでできるようになる感じ
    • 実機でも利用可能
  • Thermal Stateをシュミレートできるようになる
    • 端末が熱かったり冷たかったりするやつ?
    • 何に利用するんだろう

テスト関連

  • 「テストプラン」を作成可能になる
    • 同じテストケースを色々な設定で実行可能になる?
    • 例えば環境変数とか言語とか

シミュレータ関連

  • Apple WatchアプリをApple Watchシミュレータ単独で実行することができる?(今までできなかったの?)
  • Metal上でシュミレータを動かすようにしたので色んなパフォーマンスが向上

Instruments関連

  • CPUとかMetalとかSwift UIとか色々言ってたけどよく分からんかった
  • 要は色んな指標が同時に見れるようになったよってことかな

Swift UI

  • 全く新しいUI実装のフローの実現
    • 今まではコード編集→アプリ起動→デバッグ、だったものがコード編集だけに
    • つまりSwift UIを利用すれば書いたUIコードが即座にプレビューできるということ(だと思ってる)

UITextFieldViewのleftViewに隠された仕様について

TL;DR

  • 1つのViewインスタンスを、複数のUITextFieldViewleftViewに同時に表示することはできない
  • 同時には表示することができないので、leftViewMode.whileEditingに指定すれば1つのViewインスタンスを使い回すことも可能
  • この辺りの仕様は公式ドキュメントには記載されてないのでツライ

やりたかったこと

  • UITableViewCellのsubViewにUITextFieldを配置する
  • UITextFieldleftViewUIImageViewを常に表示する
  • UITextFieldのendEditingのコールバックでsuperViewのUITableViewCellをリロードする

問題

初回表示時は問題なくleftViewが表示されましたが、Cellをリロードした直後にアプリがハングし、以後メモリ使用量が増え続ける問題が発生しました。

仮説1

メモリが増え続けていたことから、まずはメモリリークを疑いました。 XcodeのInstrumentsで調べてみましたが、これといったリーク箇所を見つけることができませんでした。

仮説2

Cellのリロード直後に問題が発生していたことから、再レンダリングされる時にleftViewに指定しているViewのインスタンスが解放されていることを疑いました。 leftViewをstatic変数にするなど試してみましたが解決しませんでした。

デバッグ

UITextFieldleftViewMode.alwaysから.whileEditingに変更してみたところ、問題が発生しなくなることが判明しました。.unlessEditingだと依然として問題は発生しました。

以上のことからUITableViewに起因する問題ではなく、UITextFieldそのものが起因の問題もしくは仕様なのではないかと考えられます。

検証

UITableViewを利用せず、以下のようなシンプルなサンプルを実装して検証してみました。

class ViewController: UIViewController {

    @IBOutlet weak var textField_1: UITextField!
    @IBOutlet weak var textField_2: UITextField!

    let icon = UIImageView(image: #imageLiteral(resourceName: "any"))

    override func viewDidLoad() {
        super.viewDidLoad()

        self.textField_1.leftView = icon
        self.textField_1.leftViewMode = .always

        self.textField_2.leftView = icon
        self.textField_2.leftViewMode = .always
    }
}

案の定、アプリが動かなくなり、メモリが増え続けます。前述した問題と同じ症状です。どうやらUITextFieldに何らかの仕様か問題があることが確定しました。ググりやすくなります。

ググる

https://stackoverflow.com/a/6967456/4834226

A single UIView (or UIImageView) cannot be displayed on screen twice at the same time. I fixed this by creating separate padding views for the UITextFields.

つまり、「1つのUIView(もしくはUIImageView)は同時に2回スクリーン上に表示することはできない」。

公式ドキュメントには書いてないけど・・・。

developer.apple.com

結論

公式ドキュメントには特に記載がありませんが、1つのViewインスタンス複数のUITextField.leftView同時に表示することはできない、という仕様ということが判明しました。

UITableViewCellのケースでは、Cellが再利用される(複数のViewに利用される)ことでアプリがハングしていたと考えられます。

Apple IDの2FA必須化に伴うCI環境でのfastlane実行の問題と対応

問題

Travis CIでfastlaneの実行に利用していたApple ID(foo@example.comとする)に2FAを設定したらfastlaneがうまいこと動かなくなった。

対策1

fastlane公式ドキュメントにちゃんと対応方法が書いてある👏

docs.fastlane.tools

以下の2つの環境変数を利用して2FAのアカウントを利用する際の対応方法を試してみた。

  • FASTLANE_SESSION
  • FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD

ところが、結局毎回6digitコードを聞かれてしまいダメだった。

[06:10:52]: Login to App Store Connect (foo@example.com)
Two Factor Authentication for account 'foo@example.com' is enabled
If you're running this in a non-interactive session (e.g. server or CI)
check out https://github.com/fastlane/fastlane/tree/master/spaceship#2-step-verification
Please enter the 6 digit code: 

No output has been received in the last 10m0s, this potentially indicates a stalled build or something wrong with the build itself.
Check the details on how to adjust your build configuration on: https://docs.travis-ci.com/user/common-build-problems/#Build-times-out-because-no-output-was-received

The build has been terminated

対策2

公式マニュアルより

The easiest way to get fastlane running on a CI system is to create a separate Apple ID that doesn't have 2-factor authentication enabled - doesn't have the Account Holder role

  • Account Holderじゃなくて
  • 2FAを設定してない

別のApple IDを利用するのが一番簡単とのこと。

別のApple ID(bar@example.comとする)を試してみる。

そして、エラーになる。

[09:00:07]: Making sure the latest version on App Store Connect matches '1.9.9' from the ipa file...
[09:00:08]: '1.9.9' is the latest version on App Store Connect
[09:00:11]: Uploading metadata to App Store Connect
[09:00:13]: Successfully uploaded set of metadata to App Store Connect
[09:00:14]: Starting with the upload of screenshots...
[09:00:14]: Successfully uploaded screenshots to App Store Connect
[09:00:14]: Uploading binary to App Store Connect
[09:00:16]: Fetching password for transporter from environment variable named `FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD`
[09:00:16]: Going to upload updated app to App Store Connect
[09:00:16]: This might take a few minutes. Please don't interrupt the script.
[09:00:21]: [Transporter Error Output]: Your Apple ID or password was entered incorrectly. (-20101)
-------------------------------------------------------------------------------------
Please provide your Apple Developer Program account credentials
The login information you enter will be stored in your macOS Keychain
You can also pass the password using the `FASTLANE_PASSWORD` environment variable
See more information about it on GitHub: https://github.com/fastlane/fastlane/tree/master/credentials_manager
-------------------------------------------------------------------------------------
The login credentials for 'bar@example.com' seem to be wrong
The password was taken from the environment variable
Please make sure it is correct
[09:06:08]: Please run this tool again to apply the new password
[09:06:08]: Transporter transfer failed.
[09:06:08]:
[09:06:08]: Your Apple ID or password was entered incorrectly. (-20101)
[09:06:08]: [iTMSTransporter] [2019-03-28 09:00:21 GMT] <main> DBG-X:   parameter StatisticsPreviousCallDurationInSecs = 0.338798522

[09:06:08]: [iTMSTransporter] [2019-03-28 09:00:21 GMT] <main> DBG-X:   parameter TransporterArguments = -m upload -u bar@example.com -p **hidden value** -f /tmp/1220373112.itmsp -t DAV -t Signiant -k 100000

[09:06:08]: [iTMSTransporter] [2019-03-28 09:00:21 GMT] <main> DBG-X:   parameter Version = 1.13.0

[09:06:08]: [iTMSTransporter] [2019-03-28 09:00:21 GMT] <main> DBG-X:   parameter iTMSTransporterMode = upload

[09:06:08]: [iTMSTransporter] [2019-03-28 09:00:21 GMT] <main>  INFO: id = 20190328090021-173

[09:06:08]: [iTMSTransporter] [2019-03-28 09:00:21 GMT] <main>  INFO: iTMSTransporter Correlation Key: 16925438-6961-40a7-a474-0b6386e4ea3e

[09:06:08]: [iTMSTransporter] [2019-03-28 09:00:21 GMT] <main> DBG-X: Apple's web service operation return value:

[09:06:08]: [iTMSTransporter] [2019-03-28 09:00:21 GMT] <main> DBG-X:   parameter Errors = [Your Apple ID or password was entered incorrectly. (-20101)]

[09:06:08]: [iTMSTransporter] [2019-03-28 09:00:21 GMT] <main> DBG-X:   parameter EnableJWTForAllCalls = false

[09:06:08]: [iTMSTransporter] [2019-03-28 09:00:21 GMT] <main> DBG-X:   parameter RestartClient = false

[09:06:08]: [iTMSTransporter] [2019-03-28 09:00:21 GMT] <main> DBG-X:   parameter ErrorCode = -20101

[09:06:08]: [iTMSTransporter] [2019-03-28 09:00:21 GMT] <main> DBG-X:   parameter ErrorMessage = Your Apple ID or password was entered incorrectly. (-20101)

[09:06:08]: [iTMSTransporter] [2019-03-28 09:00:21 GMT] <main> DBG-X:   parameter Success = false

[09:06:08]: [iTMSTransporter] [2019-03-28 09:00:21 GMT] <main> ERROR: Your Apple ID or password was entered incorrectly. (-20101)

[09:06:08]: [iTMSTransporter] [2019-03-28 09:00:21 GMT] <main> DBG-X: Returning 1

[09:06:08]: iTunes Transporter output above ^
[09:06:08]: Your Apple ID or password was entered incorrectly. (-20101)
Return status of iTunes Transporter was 1: Your Apple ID or password was entered incorrectly. (-20101)
The call to the iTMSTransporter completed with a non-zero exit status: 1. This indicates a failure.

CredentialsManagerを試してみる

See more information about it on GitHub: https://github.com/fastlane/fastlane/tree/master/credentials_manager

ということなのでリンク先を参考にfastlane-credentialを試してみる。

Traviss-Mac-6:ios-app travis$ fastlane fastlane-credentials add --username bar@example.com
[✔]
Password: ************
Credential bar@example.com:************ added to keychain.

が結果は変わらず。

想像力を働かせる

[09:00:14]: Uploading binary to App Store Connect
[09:00:16]: Fetching password for transporter from environment variable named FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD
...
The login credentials for 'bar@example.com' seem to be wrong

ビルドログを何度も眺め直してから、ある仮説を立てる。

  • ipaのアップロードにFASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORDを利用してるようだ
  • このパスワードはAccount Holderのfoo@example.comApple IDで作成したもの
  • でもfastlane deliverbar@example.comのアカウントで行うように設定してる
  • つまりFASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORDがなにか余計なことしてる?

ということで対策1で環境変数にセットしていたFASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORDを削除してみたところ、無事にipaのアップロードに成功🎉

結論

  • 2FA設定してるApple IDでfastlane deliverが動かない(たぶん自分の設定が悪いけど原因わからない)
  • 2FA設定してないApple IDでfastlane deliverするときは環境変数FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORDセットしちゃうとAppStoreConnectにログインできないので注意

DeployerでColofulBoxにLaravelアプリケーションをデプロイする

対象読者

  • ColorfulBoxにPHPアプリケーションをデプロイしたい
  • GitなどでLaravelアプリケーションのソースコードをバージョン管理してる
  • composerを利用しててvendorディレクトリはバージョン管理対象から外してる

ColorfulBoxの制限

ColorfulBoxはいわゆる共用サーバです。VPSや専用サーバとは違って他のユーザに影響を与えないようにいくつか制限があります。

さくらインターネットなど他の共用サーバを利用したことがないのでこれらが一般的なのか分かりませんが、ColorfulBoxには現状気づいてるだけでも以下の制限が存在しています。

  1. CLIPHPバージョンはcPanelからは設定できない
  2. php.iniのallow_url_fopen=Offなのでcomposer installとかできない
  3. サーバにrsyncがインストールされてないのでrsyncできない

これらの制約がある中でも、これから紹介する方法を利用すれば、ローカル開発環境からコマンド1つでデプロイすることが可能になります。


ちなみに、CLIPHPバージョンを変更する方法は以下の記事で解説しています。 tofucodes.hatenablog.jp

Deployerとは

deployer.org

人気のフレームワークをサポートしているPHP製のデプロイツールです。 LaravelやCakePHP、他にもたくさんのPHPフレームワークがサポートされています。 他のデプロイツールとの比較はできませんが、数行のスクリプトでLaravelのアプリケーションがデプロイできて感動しました。

方法

🛠Deployerのインストール

$ curl -LO https://deployer.org/deployer.phar
$ mv deployer.phar /usr/local/bin/dep
$ chmod +x /usr/local/bin/dep

👨‍🍳Laravel用のレシピを利用してDeployerのスクリプトを作成

$ dep init -t Laravel

📝作成されるdeploy.phpを編集(解説は後述します)

<?php
namespace Deployer;

require 'recipe/laravel.php';

set('application', 'アプリ名');

set('repository', 'リポジトリのURL');

set('git_tty', true);

set('writable_mode', 'chmod');

// 1.
set('bin/php', '/home/ユーザ名/bin/php');

// 2.
set('bin/composer', function () {
    if (commandExist('composer')) {
        $composer = locateBinaryPath('composer');
    }
    return '{{bin/php}} -d allow_url_fopen=1 ' . $composer;
});

host('ホスト名')
    ->set('deploy_path', 'デプロイ先のパス');

after('deploy:failed', 'deploy:unlock');

before('deploy:symlink', 'artisan:migrate');

🚢デプロイ実行

$ dep deploy [-vvv]

解説

1. bin/php

set('bin/php', '/home/ユーザ名/bin/php');

ColofulBoxサーバのCLIPHPバージョンはデフォルト5.6となっていて最新版のLaravelをダウンロードすることができません。そのためDeployerが利用するCLIPHPをバージョン7.1や7.2にする必要があります。

私はシステムのPHP7.1をユーザのbinディレクトリにコピーしてPATHを通して利用しているため上記のような設定になっていますが、バージョンさえ合っていればパスはどこでもいいと思います。

2. bin/composer

set('bin/composer', function () {
    if (commandExist('composer')) {
        $composer = locateBinaryPath('composer');
    }
    return '{{bin/php}} -d allow_url_fopen=1 ' . $composer;
});

制限2.で記載した通り、ColorfulBoxサーバではphp.iniのallow_url_fopen=Offになってる都合上、単純にcomposer installを実行することができません。そこでcomposer install時に-d allow_url_fopen=1をつけることで一時的にallow_url_fopen=Onの状態を作っています。またcomposer install時のphpに関しても先ほど2.で指定したbin/phpが利用されるようにします。

ColorfulBoxでCLIのPHPのバージョンを変更する方法

www.colorfulbox.jp

ColorfulBoxというレンタルサーバーを使ってPHPのアプリケーションを公開してみました。

レンタルサーバーというものを人生で初めて触れているもので勝手がどうにも分からず、PHPのバージョン変更するだけでもだいぶ手こずってしまいました。

分かってしまえば簡単なことだったので自責の念を込めつつ共有します。

結論

  • WebサーバーのPHPバージョン設定とCLIPHPバージョンは別物
  • レンタルサーバーの都合上CLIPHP実体を変えることはできない
  • PATHの順番を弄って自分のホームディレクトリに配置したPHPを利用するようにする

2種類のPHPバージョン

そもそもPHPのバージョンと一口にいっても以下の2種類を考慮する必要があるみたいです。

  • WebサーバのPHPバージョン(つまりアプリケーションのランタイムで使われるPHP?)
  • CLIPHPバージョン

前者のPHPバージョンについてはColorfulBoxの管理画面であるcPanel上で変更できるようになっています。

help-cf.colorfulbox.jp

help.colorfulbox.jp

CLIPHPはというと以下のようにPHP 5.6.40がデフォルトで使われていて、このバージョンではcomposerやLaravelのコマンドで色々困ることが出てきます。

$ ll /usr/local/bin/
total 56
lrwxrwxrwx 1 root root    37 Dec 28 17:57 ea-php53 -> /opt/cpanel/ea-php53/root/usr/bin/php
lrwxrwxrwx 1 root root    37 Dec 28 17:57 ea-php54 -> /opt/cpanel/ea-php54/root/usr/bin/php
lrwxrwxrwx 1 root root    37 Dec 28 16:59 ea-php55 -> /opt/cpanel/ea-php55/root/usr/bin/php
lrwxrwxrwx 1 root root    37 Dec 28 16:59 ea-php56 -> /opt/cpanel/ea-php56/root/usr/bin/php
lrwxrwxrwx 1 root root    37 Dec 28 16:59 ea-php70 -> /opt/cpanel/ea-php70/root/usr/bin/php
lrwxrwxrwx 1 root root    37 Dec 28 17:57 ea-php71 -> /opt/cpanel/ea-php71/root/usr/bin/php
lrwxrwxrwx 1 root root    37 Dec 28 17:57 ea-php72 -> /opt/cpanel/ea-php72/root/usr/bin/php
-rwxr-xr-x 1 root root 28264 Aug 10  2017 lsphp
-rwxr-xr-x 1 root root 28264 Aug 10  2017 php

$ /usr/local/bin/php -v
ea-php-cli Copyright 2017 cPanel, Inc.
PHP 5.6.40 (cli) (built: Jan 24 2019 18:26:19)
Copyright (c) 1997-2016 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2016 Zend Technologies
    with the ionCube PHP Loader v4.7.5, Copyright (c) 2002-2014, by ionCube Ltd.

かといって/usr/local/bin/phpの実体を他のバージョンにすることもレンタルサーバーの都合上、無理ですよね。

色々と試行錯誤して諦めかけたその時に、神の啓示が...(笑)

(結局はものすごい簡単なことで対応することができました)

方法

まずは/usr/local/bin/の中にあるPHPの一覧から自分の好きなバージョンを$HOME/bin/にコピーします。(例ではPHP 7.1)

$ cp /usr/local/bin/ea-php71 $HOME/bin/php #もしくは$HOME/.local/bin/php

次に$HOME/.bash_profileを編集します。

# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
        . ~/.bashrc
fi

# User specific environment and startup programs

# PATH=$PATH:$HOME/.local/bin:$HOME/bin  # 元々はこれ
PATH=$HOME/.local/bin:$HOME/bin:$PATH  # こちらに変更

export PATH

このように編集することで/usr/local/bin/phpよりも$HOME/bin/phpが先に探索されるようになるので、先ほどコピーしたPHPを利用することができるようになります。

$ which php
~/bin/php

$ php -v
PHP 7.1.26 (cli) (built: Jan 24 2019 17:47:13) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2018 Zend Technologies
    with the ionCube PHP Loader (enabled) + Intrusion Protection from ioncube24.com (unconfigured) v10.2.4, Copyright (c) 2002-2018, by ionCube Ltd.

追記

書いてて思いついたんですけど、エイリアスとかでも対応できたかも?

$ alias php=/usr/local/bin/ea-php72

$ php -v
PHP 7.2.14 (cli) (built: Jan 24 2019 17:28:26) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
    with the ionCube PHP Loader (enabled) + Intrusion Protection from ioncube24.com (unconfigured) v10.2.4, Copyright (c) 2002-2018, by ionCube Ltd.
    with Zend OPcache v7.2.14, Copyright (c) 1999-2018, by Zend Technologies

IQKeyboardManagerで親Viewが異なるUITextFieldを兄弟とみなす方法

github.com

課題

Qiitaで紹介されていて使ってみたIQKeyboardManager、すごい便利ですね。

IQKeyboardManagerは、デフォルトでは画像のように別の親Viewに属しているUITextFieldなどを兄弟と見なしてくれません。(つまりToolbarの↑↓でUITextFieldを移動できません。)

f:id:tofucodes:20190117154326p:plain
2つのUITextFieldは兄弟にならない

原因

IQKeyboardManagerのソースコードを見てみます。 UITextField兄弟間をToolbarの↑↓で移動するロジックは、兄弟をresponderViews()という関数で探索していることが分かります。

@objc public var canGoNext: Bool {
    //Getting all responder view's.
    if let textFields = responderViews() {
        if let  textFieldRetain = _textFieldView {
            //Getting index of current textField.
            if let index = textFields.index(of: textFieldRetain) {
                
                //If it is not first textField. then it's previous object canBecomeFirstResponder.
                if index < textFields.count-1 {
                    return true
                }
            }
        }
    }
    return false
}

responderViews()はまず、全てのsuperViewからtoolbarPreviousNextAllowedClassesの配列に含まれるクラスと一致するものを探索します。

もし一致するsuperViewが見つかった場合は、そのViewの全ての子Viewから兄弟を探索し、Viewの配列として返却します。

もし一致するsuperViewが1つも見つからなかった場合は、対象のUITextFieldと同じ階層にある兄弟だけを探索して返却します。

/** Get all UITextField/UITextView siblings of textFieldView. */
private func responderViews()-> [UIView]? {
    
    var superConsideredView : UIView?

    //If find any consider responderView in it's upper hierarchy then will get deepResponderView.
    for disabledClass in toolbarPreviousNextAllowedClasses {
        
        superConsideredView = _textFieldView?.superviewOfClassType(disabledClass)
        
        if superConsideredView != nil {
            break
        }
    }

//If there is a superConsideredView in view's hierarchy, then fetching all it's subview that responds. No sorting for superConsideredView, it's by subView position.    (Enhancement ID: #22)
    if let view = superConsideredView {
        return view.deepResponderViews()
    } else {  //Otherwise fetching all the siblings
//...

つまり、兄弟にしたいUITextFieldを全て、toolbarPreviousNextAllowedClassesに含まれるクラスの子Viewにすれば、兄弟として判別してくれるというロジックのようです。

対策

まず、親View用のカスタムViewクラスを定義します。 今回はTextFieldsContainerViewという名前でクラスを定義しました。

import UIKit

/// Class for letting IQKeyboardManager treat all text fields/views whose parent is this container view
/// as a sibling so that it enables to handle all on its toolbar.
/// We don't need this class all the time but useful when we have text fields/views that have different parent.
/// See also: https://github.com/hackiftekhar/IQKeyboardManager/blob/master/IQKeyboardManagerSwift/IQKeyboardManager.swift#L1716-L1728
class TextFieldsContainerView: UIView {

}

次に、IQKeyboardManagerのtoolbarPreviousNextAllowedClassesに先ほど定義したTextFieldsContainerViewクラスを追加します。

IQKeyboardManager.shared.toolbarPreviousNextAllowedClasses.append(TextFieldsContainerView.self)

最後に、TextFieldsContainerViewクラスをstoryboard/xibやコードで各UITextFieldの親Viewクラスになるように実装します。

f:id:tofucodes:20190117154307p:plain
2つのUITextFieldは兄弟と見なされる

BitbucketのコードをHerokuに自動デプロイする方法が超簡単だった

最近仕事が暇すぎて業務時間中に個人プロジェクトのコード書いてコミットしてたりしたんですが

プライベートレポジトリじゃないんでGithubのContribution activity(草の下の方とかに出るやつ)を同僚や上司に見られたらあまり良くないよなぁと思い

GithubのオープンレポジトリからBitbucketのプライベートレポジトリに移行しました。

元々はGithubのレポジトリとHerokuを連携させて自動デプロイの設定をしてたのですが

BitbucketとHerokuの自動デプロイ連携を今回やってみたので備忘録的なやつ。

(まさかこの直後にGithubがプライベートレポジトリを無料解放するとは、この時の僕は知る由もなかった...)

blog.github.com

ではGithubのプライベートレポジトリが無料になったこの時代に、BitbucketとHerokuの自動デプロイ設定をしていきましょう。

ゴール

bitbucketにコミットすると自動的にpipelineがアプリケーションをビルドしてHerokuにデプロイする。

f:id:tofucodes:20190115225929p:plain
bitbucketのpipeline画面

f:id:tofucodes:20190115225940p:plain
pipeline #4の詳細画面

f:id:tofucodes:20190115230218p:plain
Heroku管理画面のactivity履歴

手順

  1. bitbucket-pipelines.yml の作成
  2. レポジトリに環境変数の設定

1. bitbucket-pipelines.yml の作成

f:id:tofucodes:20190115233442p:plain
bitbucketのUI上でpipeline作成

参考までにこちらが私の bitbucket-pipelines.yml です。

image: php:7.1.3

pipelines:
  default:
    - step:
        name: Deploy to production
        deployment: production
        caches:
          - composer
        script:
          - apt-get update && apt-get install -y unzip git
          - curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
          - composer install
          - git push https://heroku:$HEROKU_API_KEY@git.heroku.com/$HEROKU_APP_NAME.git HEAD
  • bitbucketのデフォルトのPHPバージョンは7.1.1でしたが僕のプロジェクトにはPHP7.1.3が必要だったので7.1.3にしてます。
  • それが原因か不明ですが、以下のようなエラーになるのでapt-getでgitもインストールしてます。
+ git push https://heroku:$HEROKU_API_KEY@git.heroku.com/$HEROKU_APP_NAME.git HEAD
bash: git: command not found

2. レポジトリの環境変数を設定

f:id:tofucodes:20190115234702p:plain
bitbucketのUI上で環境変数の設定

試してませんが、Repositoryの環境変数ではなくて、その下にあるDeploymentsの変数の方でも動作するかもしれません(し、そちらの方が適切かもしれません)

Deploymentsの変数の説明

Variables that can only be used in deployments to a specific environment.

HEROKU_API_KEYの作成方法は公式ドキュメントを参照で。

devcenter.heroku.com