20210106のiOSに関する記事は6件です。

[Swift] プロパティオブザーバ willSetとdidSet

プロパティオブザーバとは

格納型プロパティの値が更新される時、それをきっかけにして処理を書く事が出来ます。
例えば、プロパティオブザーバを使用する事で
var price = 100 の値を更新した時に、更新の回数をカウントする処理を書く事が出来たりする訳です。
このプロパティオブザーバには、値が変更される直前に呼び出されるwillSetと変更後に呼び出されるdidSetがあります。
これらはどちらか一方だけ書くこともできます。
書き方は以下の通りです。

var プロパティ名:  = 初期値 {
    willSet (仮引数) {
         変更直前に呼ばれる処理
   }
    didSet (仮引数) {
         変更直後に呼ばれる処理
   }
}

それぞれの仮引数

willSetdidSetにはそれぞれ仮引数を指定する事が出来ます。
willSetでは、プロパティに格納される直前の新しい値を仮引数で参照する事が出来ます。
仮引数を省略する場合は、newValueという名前で参照できます。
didSetでは、プロパティに今まで格納されていた古い値を仮引数で参照できます。
省略する場合は、oldValueで参照できます。

willSet使ってみた

textFieldに購入金額を入力して、ボタンを押すとその回数と前回の金額との差を表示するアプリを作成してみました。

class ViewController: UIViewController {
    @IBOutlet var label: UILabel!
    @IBOutlet var priceTextField: UITextField!
    @IBOutlet var countLabel: UILabel!
    @IBOutlet var hikakuLabel: UILabel!
    var count = 0
    var  priceDifference = ""
    let minPrice = 1.0

    var buyingPrice: Double = 0 {
        willSet {
            count += 1
            guard newValue > buyingPrice else {
                priceDifference = "前回より\(buyingPrice - newValue)円低い購入価格です"
                return
            }
            priceDifference = "前回より\(newValue - buyingPrice)高い購入価格です"
        }
    }

    @IBAction func countUpButton(_ sender: Any) {
        buyingPrice = Double(priceTextField.text!) ?? 0
        countLabel.text = "購入回数は\(count)回です"
        hikakuLabel.text = priceDifference
    }
}
実行結果

willSet内で購入回数を取得し、価格差を計算して表示する事が出来ました。

スクリーンショット 2021-01-06 19.55.20.png

スクリーンショット 2021-01-06 19.55.44.png

スクリーンショット 2021-01-06 19.56.06.png

didSet使ってみた

didSetを使用して数字の最低値を設定し、それ以下にならないようにコードを書いてみます。

class ViewController: UIViewController {
    @IBOutlet var numberTextFIeld: UITextField!
    @IBOutlet var numberLabel: UILabel!
    let minNumber = 1

    var seisuu:Int = 1 {
        didSet {
            if seisuu < minNumber {
                seisuu = minNumber
            }
        }
    }

    @IBAction func button(_ sender: Any) {
        seisuu = Int(numberTextFIeld.text!) ?? 0
        numberLabel.text = String(seisuu)
    }
}
実行結果

変数seisuuに数字がセットされた時に、最低値以下の場合は最低値になるように指定しているので、ボタンを押した時に1以下の数字は1と表示する事ができた。

スクリーンショット 2021-01-06 20.33.24.png
-30と入力しても1と表示する。↓
スクリーンショット 2021-01-06 20.33.46.png

メリット

これらを使用する事で、コードがかなりコンパクトになります。
別のフィールドを作成する事なく、値を保持して取り出す事が出来ます。

参考文献

https://qastack.jp/programming/24006234/what-is-the-purpose-of-willset-and-didset-in-swift

荻原 剛志、『詳解 Swift 第5版』、SBクリエイティブ株式会社、2019年11月25日、557ページ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOSのショートカットアプリでsshスクリプト実行:Rasberery PiやLinux操作が捗る!

はじめに

皆さんはiOSのショートカットAppはお使いでしょうか?私は最近まで全く使っていませんでした…。
ただ最近Rasberry Piとの組み合わせ(※他の機器でも使えますが)に無限の可能性を感じるようになり、この感動を共有したいと思って筆を取りました!
image.png

本記事では主に下記内容を取扱います。

  • SSH接続設定方法(iOS13から対応の公開鍵認証)
  • 任意のスクリプトを実行
    • 簡単な例
    • 連続実行するプログラムをバックグラウンドで実行する
    • バックグラウンドで実行しているプログラムを終了する
    • リモートマシンをシャットダウン/再起動する

なお私は最近このあたりに手を出し始めたため、もし間違っている内容などあればコメントを頂けると幸いです。

SSH接続設定方法(iOS13から対応の公開鍵認証)

iOS12まではパスワード認証による接続のみでしたが、iOS13以降は公開鍵認証に対応しています。接続方法はとても簡単です。

1.iPhoneでの操作:公開鍵の共有

まずはショートカットAppを立ち上げ、検索欄から「SSH経由でスクリプトを実行」を選択します。
image.png

そうすると下記のようにSSH接続設定を入力する画面が表示されます。
ホスト・ポート・ユーザはご自分の環境に合わせて入力ください。
認証方式はデフォルトではパスワードですが、SSHキーを選択することが可能です。
image.png

SSHキーを選択すると下記のような画面が表示されます。
まず①の「新しいキーを生成」を選択して、所望の種類・ビット長を選択します。
次に②の「公開鍵を共有」を選択すると公開鍵が表示されます。メールで送信するなど、自分の都合の良い共有方法を選択します。
SSH接続したいマシンがパスワード認証を無効化していなければ、ここで生成された公開鍵をクリップボードにコピーして、パスワードでSSH接続→公開鍵を設定ファイルに追記などすれば、iPhoneだけで一通りの操作が可能かと思います。
私はこの時点でパスワード認証を無効化してしまっていたので、いったん公開鍵認証済みの別PCにiPhoneの公開鍵をメールで送り、別PCを使って公開鍵を追加しました。

image.png

2.SSH接続したい相手先マシンでの操作

こちらはいろいろな記事があるので詳細は割愛し、リンク類だけ記載いたします。私はRasberry Piを使いたかったので、ソースがそちら寄りになる点はご容赦ください。

一次ソース;
https://www.raspberrypi.org/documentation/remote-access/ssh/passwordless.md

二次ソース:
https://tool-lab.com/raspi-key-authentication-over-ssh/

これで接続設定は完了です。 あとはスクリプトを記載していきます。具体的な例に沿って説明していきます。

任意のスクリプトを実行

簡単な例

まずはリモートPCで実行中のタスク一覧を取得してiPhone上に表示する簡単な例を実行してみます。
タスク一覧の取得はpsで実行可能ですが、それだけですと結果をiPhone上で表示することができません。そこで「表示」のアクションを追加して上げると、デフォルトで「シェルスクリプトの結果」を受け取ることができます。
これを実行すると通知領域にリモートPCで実行中のタスク一覧が表示されるはずです。

image.png

連続実行するプログラムをバックグラウンドで実行する

次の例です。
「サーバを立ち上げる」ような、連続実行するプログラムの場合、ショートカットAppはプログラムの終了を待ち続けてしまい、アクションが完了しません。
また上記問題を回避しても、SSH接続切断後もリモートPC側で処理を継続させる必要があります。

今回は試しに ~/myapp/mainというプログラムをバックグラウンドで実行するケースについて記載します。

cd ~/myapp
nohup ./main >/dev/null 2>&1 &
  • nohup: このコマンドによってSSH接続後も処理を続行させることができます
  • 最後の &: これによってバックグラウンドで処理を実行します
  • >/dev/null 2>&1: 上記のようにnohup ./main &だけを実行すると、諸々の標準出力が表示されてしまうためか、アクションが完了しませんでした。そこで標準出力・エラーを読み捨てるために本コマンドを追記します

以上の処理で、バックグラウンドで処理を実行し、かつSSH接続切断後も動作を継続させることができました。

バックグラウンドで実行しているプログラムを終了する

さて、バックグラウンドで処理を実行できたは良いものの、これをkillできないのは問題です。
そんなときは下記スクリプトを実行します。(mainの部分は終了したいタスク名に置き換えてください)
こちらを参照させて頂きました。

ps aux | grep main | grep -v grep | awk '{print "kill -9", $2}' | sh

リモートマシンをシャットダウン/再起動する

さてさてさて、Rasberry Pi等を使っていると地味に面倒なのがマシンのシャットダウンです。
毎回sudo shutdown -h nowしているのですが、できればボタン一つで実行したいところです。

そんなときにもショートカットAppが使えます :innocent:

手順としては簡単で、上記例と同じようにスクリプトを記載するだけですが、sudoする際にパスワードを入力する必要があり、それをショートカットAppで実現するのに一手間必要でした。

再起動の場合

echo (毎回尋ねる) | sudo -S reboot 2>/dev/null
  • sudo -Sとすることで標準出力からパスワードを受け取ることができます。
  • そのためechoを実行しますが、なんとなくその後にパスワードを平文で保存しておくことは気後れします。そのためショートカットの「変数」機能を使用し、パスワードは毎回尋ねることにしました。

これでボタン一つでショートカット・再起動が可能です…!

おわりに

いかがでしたでしょうか。今回は私が実践したケースに基づいた例を紹介しましたが、組み合わせ次第では他にもいろいろな事例に使えるのではないでしょうか?
こんなことにも使えるよというようなコメント大歓迎です…!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

EC2 on MacでiOSビルド環境の夢を見た

※個人の感想です
※2021-01-06時点での情報と調査結果をもとに書いているので今後変わる可能性たかし君です

やろうと思ったこと

2020年12月頭くらいに発表された、EC2でmacが起動できるって話で、
ついにiosビルド環境をクラウドで完結できる日が来たかと思いいわゆるCI/CD環境を構築しようかと思った。

=> 断念

苦しい

mac1.metalインスタンスは専有ホストでしか利用できない。これが運用面・料金にダイレクトに響く。

起動が結構長い

us-west2でやりましたが、30分近くかかった。

1度起動すると24H解放されない

物理マシンを借りてるみたいなもんらしいから致し方なしとは思いますが…

専有ホストはインスタンスが起動しているかによらず課金される(多分)

これもし認識違ったらそっと教えて欲しい…けど
専有ホスト作成しようとしたときのコンソールの
インスタンスごとではなく、割り当てられた専有ホストごとに請求
って文言や、そもそも物理マシンを借りるって性質である以上、使ってない時間は課金されないなんて虫の良い話があると思えないので…

断念理由まとめ

上記3つがジェットストリームアタックのように襲い掛かってくるので、
・1回起動すると必ずおよそ3000円かかる(us-west2)
・使う時だけ起動して終わったら即解放って運用もかなり苦しそう

というわけでクラウドでのios CI/CD環境として利用するのは現在のところ厳しいかなあという印象でした。
2-3か月フル稼働でまっくぶっくが買えてしまう…
月に1-2日だけピンポイントで使いたい日がある、みたいな特殊な要件でならあるのかなあ?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Building for iOS Simulator, but the linked and embedded framework 'XXX.framework' was built for iOS + iOS Simulator. に対応した

Xcodeを12.3にした所、
「Building for iOS Simulator, but the linked and embedded framework 'XXX.framework' was built for iOS + iOS Simulator.」
が発生する様になってしまったので対応したメモ

ググってやった事

・Build Settings - Architectures - Excluded Architectures - Debugに
Any iOS Simulator SDKを追加し、
[arm64 arm64e armv7]を設定

・Build Settings - Build Options - Validate Workspace - Debugの設定をNoからYESに変更

この変更でエラーから警告に変わって取り合えずデバッグ出来る様にはなった。

参照先 https://stackoverflow.com/questions/65303304/xcode-12-3-building-for-ios-simulator-but-the-linked-and-embedded-framework-wa

Archiveを実行した所上記設定だけだとエラーとなってしまったので、
・Build Settings - Build Options - Validate Workspace - Distributionの設定をNoからYESに変更

この変更でエラーから警告に変わって取り合えずArchive出来る様にはなった。

追記 エラーも警告も無くなった

今まで変更してきた設定を元に戻し、
Build Settings - Architectures - Excluded Architectures - Debugに
Any iOS Simulator SDKを追加し、
[arm64 arm64e armv7]ではなく[XXX.framework]を設定した所、
エラーも警告も無くなった。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[RxSwift]combineLatestについて

Rxの学習過程で学んだことを後からでも見れるようにメモしていきます
本記事はこちらRxSwiftを参考にしています

CombineLatestとは

Observableシーケンスのいずれかが要素を生成するたびに、指定されたObservableシーケンスを、1つのタプルのObservableシーケンスにマージする。

使用例

TextFieldに入力した値をリアルタイムで合計し、ラベルに表示する。
といった機能を作っていきます。

数値入力用のTextFieldを3つと合計結果表示用のLabelをひとつ用意しいます。

Observable.combineLatest(textField1.rx.text.orEmpty, textField2.rx.text.orEmpty, textField3.rx.text.orEmpty) { textValue1, textValue2, textValue3 -> Int in
            return (Int(textValue1) ?? 0) + (Int(textValue2) ?? 0) + (Int(textValue3) ?? 0)
        }
        .map { $0.description }
        .bind(to: label.rx.text)
        .disposed(by: disposeBag)

結果

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOSのコード署名がなんのためにどうやって行われているかを理解する

背景

最近iOS開発に入門したんですが、いろいろなことがわからない中でもとくにコード署名がわからない。Provisioning Profileとか.p12とか名前は聞いたことがあるけど結局何をしているのか不明な概念がいきなり登場してきて、アプリを動かすまでのコード署名の手続きは実行できてもそれぞれの手続きがなんのために行われているかわかりませんでした。

  • そもそもコード署名はなんのために、どうやって行われているのか
  • いろいろなファイルが登場してくるけどそれぞれの役割はなんなのか

を理解したくて調べたのでまとめます。

この記事にはこう思えば自分は理解しやすいなと思った内容を書いたので、有識者から見ると単純化しすぎていたりそもそも間違っているものが含まれているかもしれません。その場合はコメントで教えていただけると助かります。

参考リンク

コード署名がなぜ必要なのか

コード署名は、iOSが信頼できるアプリしかインストールしないようにするための仕組みです。ここで信頼できるアプリとは何かというと

  • 開発者を特定できる
  • 開発者が署名してからアプリが改変されていない

という2つの条件を満たすものを指しています。この仕組みがないと、その辺の適当な人が作った適当なアプリや、ちゃんとした開発者が作ったがその後別の誰かによって改変されたアプリをインストールすることが可能になります。これを防ぐため、iOSでは署名されたアプリしか実行できないようになっており、開発者はアプリに署名をする必要があるというわけです。

当然、悪意を持った開発者がAppleに開発者登録して正常にリリースしたアプリはインストールできてしまいますが、これはコード署名のスコープ外です。

コード署名でやっていること

以上の目的を達するため、コード署名でどういうことをしているのかを大まかに書いておきます。

  • アプリの開発者は、公開鍵/秘密鍵のペアを用意する
    • 公開鍵はデジタル証明書の形でインストール先のiOSに渡される
    • 秘密鍵は鍵ペアを生成したマシンに保持され、署名に使われる
  • アプリをアーカイブする際、決まった手順でアプリからハッシュ値を計算し、これを秘密鍵で暗号化してアプリに同梱する。このプロセスが署名
  • アプリをインストールしたiOSは、同じ手順でアプリからハッシュ値を計算する。一方で、アーカイブ時に計算されて暗号化されたハッシュ値を証明書に含まれている開発者の公開鍵で復号する。これら2つの方法で得られたハッシュ値が一致することを確認する

ここで、まず暗号化された正しく復号できたことで、コード署名の1つ目の目的である「開発者の特定」ができます。ハッシュ値が公開鍵で復号できるということはそれを暗号化したのは開発者の秘密鍵であり、秘密鍵を持っているのは開発者本人しかいないからです。
次に、2つのハッシュ値が一致したということから、コード署名の2つ目の目的である「開発者が署名した後アプリが改変されていないことの確認」ができます。もし第三者がアプリの内容をいじっていたらiOSで計算されるハッシュ値が変わってしまい一致しなくなるためです。ハッシュ値の方も一緒に変えられたらまずいんですが、第三者は開発者の秘密鍵を持っておらずハッシュ値の暗号化ができないため、改変したハッシュ値をばれないように差し替えることができないことになります。もちろんその辺の適当な秘密鍵で暗号化することは可能ですが、そうすると開発者の公開鍵では復号できず、そこでiOSでの検証に引っかかるので大丈夫ですね。

コード署名の登場人物

大まか流れがわかったところで、コード署名に登場するいろいろな概念をまとめておきます。それぞれの概念について、何者なのか、コード署名による役割は何か、どうやって作るかは必ず書くようにします。

証明書署名要求(CSR): .certSigningRequest ファイル

  • 何:開発者の公開鍵+開発者の情報
  • 役割:Appleから証明書をもらうための引換券
  • 作り方:ローカルのキーチェーンアプリで作成する

コード署名のプロセスの始まりがCSRの作成です。なんかすごそうな名前なので作り方も難しいのかと思いきや、Macを使っている人なら馴染み深いであろうキーチェーンアプリで簡単に作ることができます。具体的な手順は検索すればすぐ出てきます。
CSRを作るとき、裏側では秘密鍵・公開鍵のペアが生成されます。公開鍵はCSRの一部として含まれ、秘密鍵はローカルに保存されます。前の項に書いたように、コード署名というのは大雑把に言うとこのとき生成された秘密鍵で開発者がアプリに署名をし、それをiOSが公開鍵で検証するというものなので、ここで生成される鍵ペアは非常に重要です。
CSRは開発者の公開鍵に開発者情報などを付加したもので、これををAppleに渡すことで、Appleから証明書をもらうことができます。CSR作成にあたって一緒に作られる秘密鍵と公開鍵は重要ですが、CSR自体はその後のプロセスでは使われることはないので、コード署名の概要を理解する上では証明書をもらうための引換券くらいに思っておけば良さそうです。

証明書(Cert): .cerファイル

  • 何:「開発者の公開鍵+開発者の情報」にAppleが署名したもの
  • 役割:アプリのインストール先のiOSに信頼できる公開鍵を提供する
  • 作り方:CSRをDeveloper Centerにアップロードすると生成され、ダウンロードできるようになる

アプリがインストールされたiOSは署名の検証のために公開鍵を使いますが、その公開鍵が第三者にすり替えられた信用できないものである可能性があるという問題があります。Apple先生のお墨付きがあれば信頼できるよね...ということで開発者の公開鍵にAppleがApple自身の秘密鍵で署名したのが証明書です。iOSはまずAppleの証明書の署名を検証することで、公開鍵が証明書に書いてある開発者自身のものであり改変もされていないことを確認できるというわけです。つまり、コード署名を検証するために必要な、信頼できる公開鍵をiOSに提供することが証明書の主要な役割かと思います。
証明書には有効期限がついており、定期的に作り直す必要があります。また、証明書の元となる公開鍵が1つであったとしても証明書は役割ごとに複数作ることが多く、アプリのコード署名についてだけでもXCodeでビルドしたアプリを実機で走らせるためのDevelopment用とアーカイブしたアプリをApp StoreやAd-hocで配布するためのDistribution用があります。

Signing Identity: .p12ファイル

  • 何:秘密鍵と証明書のペア
  • 役割:実際にコード署名を行う。秘密鍵を作成したPC以外で署名したい場合にもこのファイルを受け渡す
  • 作り方: キーチェーンアプリからエクスポートする

署名を実際に行うためには、秘密鍵を証明書とペアにしてSigning Identity(.p12ファイル)という形式にしておく必要があるようです。キーチェーンアプリは証明書に含まれる公開鍵がどの秘密鍵とペアかを認識してくれるため、キーチェーン上で証明書を右クリックしてp12ファイルをエクスポートすることができます。
例えばあるマシンで鍵ペアを作成してアプリ開発を始めたけど新しいマシンに買い換えたというとき、新しいマシンには秘密鍵がないので署名ができず、アプリを実機で走らせることもApp Storeへのリリースもできません。このような場合、古いマシンでp12ファイルをエクスポートして新しいマシンになんらかの形で渡すことで引き続き新しいマシンで開発を続けることが可能になります。当然、p12ファイルには秘密鍵が含まれるので扱いには注意する必要があるでしょう。

Provisioning Profile: .mobileprovisionファイル

  • 何:App ID+証明書+インストールを許可したい端末のデバイスIDのリスト
  • 役割:特定のデバイス上でアプリを実行して大丈夫か判断する
  • 作り方:事前に作成した証明書やApp ID、デバイス情報を、Developer Center上で指定して作成

リリース前にアプリをテスト的に社内配布したい時など、App Store以外の手段でアプリをiOSにインストールさせたいときに必要なのがProvisioning Profileです。Profileはアプリ本体と一緒にiOS端末に渡され、コード署名を正当性を検証するために必要です。原理的には検証に使うのは公開鍵なのですが、公開鍵は証明書に含まれ、証明書はProfileに含まれるので結局ファイルとして何が必要かというとProfileということになります。

ここまではコード署名の話だけをしてきましたが、実際にアプリをiOS端末で実行するにはそのほかにも検証しなければいけない内容があり、Profileはそれらの検証にも使われます。具体的には、以下の3つの検証がパスしたときにアプリ時の実行が許可されます。

  • Profileに含まれる証明書の公開鍵を使ってコード署名が正しいか
  • Profileに含まれるApp IDがアプリのBundleIDと合致するか
  • Profileに含まれるデバイスIDのリストの中にアプリを実行しようとしている含まれるか

コード署名の流れ

登場人物それぞれ役割を踏まえて、アプリをAd-hoc配布する際にコード署名でやらなければいけないこと、起こることの流れを簡単にまとめます。

  • キーチェーンアプリでCSRを作成。このときに裏で鍵ペアが生成される
  • CSRをDeveloper CenterにアップロードしてDistribution用の証明書を取得する
  • キーチェーンアプリで証明書からp12ファイルをエクスポートしておく
  • Developer Center上でProvisioning Profileを取得する
    • App IDを登録
    • アプリをインストールしたいデバイスのIDを登録
    • 以上の情報からProvisioning Profileを作成し、ダウンロード
  • XCodeにProvisioning Profileをimportする
  • アプリをアーカイブする。ここで、XCodeがProvisioning Profileに含まれる証明書と結びつく秘密鍵を取得し、署名もしてくれていると思われる
  • なんらかの手段でアプリを配布する。ダウンロードしたiOS端末ではProvisioning Profileを使ってアプリが検証され、OKならばインストール/実行される

まとめ

  • コード署名はiOSが信頼できるアプリのみをインストールするための仕組み
  • 開発者の秘密鍵でアプリに署名し、アプリインストール先のiOS端末において公開鍵を使った署名の検証がなされる
    • 秘密鍵はSigning Identity(.p12ファイル)に、公開鍵はProvisioning Profileのなかの証明書に含まれている
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む