- 投稿日:2019-12-19T18:28:26+09:00
Kubernetesクラスタ構築ツールのkindを使って見る
Kubernetesの挙動を確認したくなるたびに毎回kubeadmでクラスタを立ち上げるのがめんどくさかったので気軽に構築できるツールがないか探していたところ,kindという構築ツールを見つけたので試して行こうと思います.
環境
Mac OS :Catalina version 10.15.2
Kubernetesとは
Kubernetesについて軽く説明すると,オープンソースのコンテナオーケストレーションエンジンです.そしてコンテナはプログラム実行環境の仮想化方法の1つです.
Kubernetesを用いる事で複数台のコンピュータ上のコンテナを管理できます.
Kubernetesクラスタの基本的な構成は以下になります.KubernetesクラスタはMaster NodeとWorkerノードの2つのグループに別れます.Master Nodeでユーザからのコンテナ配置命令の受付,スケジューリングなどを行い,Worker Nodeで実際にコンテナを実行します.
コンテナのスケジューリングはPodという単位で行います.
Pod間通信を行いたい場合やクラスタ外からPodと通信したい場合はServiceを通して通信します.kindとは
kind(kubernetes in docker)は,1台のコンピュータ上でのKubernetes構築ツールです.Kubernetes自身をテストすることを目的に作成されています.
Dockerコンテナを1つのノードとしてみなし,そのノードコンテナ上でDockerコンテナを実行することで,1台のコンピュータ上でマルチノードクラスタを構築できます.
インストール
kindを動かすには,dockerが必要です.
$brew install dockerでインストールできます.
Kubernetesクラスタにコンテナを配置するため,Kuberenetesコマンドラインインタフェースであるkubectlをインストールします.$brew install kubectl最後にkind自身をインストールします.
$brew install kindkindでクラスタ作成
kindでクラスタを作成して見ます.Master Nodeの構成とWorker Nodeの構成をyamlファイルで記述し,--configオプションで渡します.渡さなかった場合はMaster Node1台のクラスタが作成されます.
$kind create cluster --config a-controller-a-worker-mapping.yaml Creating cluster "kind" ... ✓ Ensuring node image (kindest/node:v1.16.3) ? ✓ Preparing nodes ? ✓ Writing configuration ? ✓ Starting control-plane ?️ ✓ Installing CNI ? ✓ Installing StorageClass ? ✓ Joining worker nodes ? Set kubectl context to "kind-kind" You can now use your cluster with: kubectl cluster-info --context kind-kind Thanks for using kind! ?使用ファイルは以下の通りです.今回は1台のMaster Nodeと1台の Worker Nodeの構成で作成します.
- role: control-plane
がMaster Nodeの設定で,
- role: worker
がWorker Nodeの設定です.a-controller-a-worker-mapping.yamlkind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 nodes: - role: control-plane - role: worker extraPortMappings: - containerPort: 30000 hostPort: 30000 listenAddress: "0.0.0.0" #オプション defaultは "0.0.0.0" protocol: tcp # オプション defaultはtcpextraPortMappingsでクラスタ外からコンテナへ通信できるようにします.
コンテナのデプロイ
nginxをデプロイして見ます.
$kubectl apply -f nginx.yaml pod/nginx created service/nginx-svc created使用ファイルは以下の通りです.
nginx.yamlapiVersion : v1 kind : Pod metadata : name : nginx labels : app : nginx spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: nginx-svc spec: selector: app: nginx type: NodePort ports: - protocol: TCP targetPort: 80 port: 80 nodePort: 30000Podに対応するServiceを作成する事で,クラスタ外からの通信が可能になります.
無事デプロイできました.簡単ですね.
後片付け
nginxの削除
$kubectl delete -f nginx.yaml pod "nginx" deleted service "nginx-svc" deletedクラスタの削除
$kind delete cluster Deleting cluster "kind" ...kubeadmとの比較
Kubernetesクラスタ構築ツールとして有名なものにkubeadmがあります.kindとは違い複数のコンピュータ上でKubernetesクラスタを構築でき,本番環境にも使用できます.しかし,kindは1台分の作業でクラスタ構築できるのに対し,kubeadmは立ち上げるノード分の作業が発生するのでめんどくさいです.ですのでKubernetesの挙動を確かめたい時はkindの方が良いと思います.
minikubeとの比較
kindと同じ1台のコンピュータ上でのKubernetes構築ツールとして有名なものにminikubeがあります.クラスタ構築に必要な作業量はkindとほぼ変わりませんが1台の仮想マシンがMaster NodeとWorker Nodeの両方の役割を担っています.さらにノード数を増やす事ができず,挙動を試せる幅が狭くなります.例えばKubernetesの機能を使って複数台あるWorker Nodeの内のある特定のWorker Nodeにコンテナをデプロイするということを試せません.なのでKubernetesの挙動を確かめたい時はkindの方が良いと思います.
まとめ
kindを使用することで気軽にKubernetesの挙動を確認できます.なのでこれからKubernetesの挙動を確認したいときはkindを使っていこうと思います.
参考
kind
kubeadm
minikube
Kubernetes ドキュメント
Kubernetes実践ガイド クラウドネイティブアプリケーションを支える技術 (impress top gear)
- 投稿日:2019-12-19T18:28:26+09:00
Kubernetes構築ツールのkindを使って見る
Kubernetesの挙動を確認したくなるたびに毎回kubeadmでクラスタ立ち上げるのめんどくさかったので気軽に構築できるツールがないか探していたところ,kindという構築ツールを見つけたので試して行こうと思います.
環境
Mac OS :Catalina version 10.15.2
Kubernetesとは
Kubernetesについて軽く説明すると,オープンソースのコンテナオーケストレーションエンジンです.そしてコンテナはプログラム実行環境の仮想化方法の1つです.
Kubernetesを用いる事で複数台のコンピュータ上のコンテナを管理できます.
Kubernetesクラスタの基本的な構成は以下になります.
KubernetesクラスタはMaster NodeとWorkerノードの2つのグループに別れます.
Master Nodeでユーザからのコンテナ配置命令の受付,スケジューリングなどを行い,
Worker Nodeで実際にコンテナを実行します.
コンテナのスケジューリングはPodという単位で行います.
Pod間通信を行いたい場合やクラスタ外からPodと通信したい場合はServiceを通して通信します.kindとは
kind(kubernetes in docker)は,1台のコンピュータ上でのKubernetes構築ツールです.Kubernetes自身をテストすることをを目的に作成されています.
Dockerコンテナを1つのノードとしてみなし,そのノードコンテナ上でDockerコンテナを実行することで,1台のコンピュータ上でマルチノードクラスタを構築できます.
インストール
kindを動かすには,dockerが必要です.
$brew install dockerでインストールできます.
Kubernetesクラスタにコンテナを配置するため,Kuberenetesコマンドラインインタフェースであるkubectlをインストールします.$brew install kubectl最後にkind自身をインストールします.
$brew install kindkindでクラスタ作成
kindでクラスタを作成して見ます.Master Nodeの構成と,Worker Nodeの構成をyamlファイルで記述し,--configオプションで渡します.渡さなかった場合はMaster Node1台のクラスタが作成されます.
$kind create cluster --config a-controller-a-worker-mapping.yaml Creating cluster "kind" ... ✓ Ensuring node image (kindest/node:v1.16.3) ? ✓ Preparing nodes ? ✓ Writing configuration ? ✓ Starting control-plane ?️ ✓ Installing CNI ? ✓ Installing StorageClass ? ✓ Joining worker nodes ? Set kubectl context to "kind-kind" You can now use your cluster with: kubectl cluster-info --context kind-kind Thanks for using kind! ?使用ファイルは以下の通りです.今回は1台のMaster Nodeと1台の Worker Nodeの構成で作成します.
- role: control-plane
がMaster Nodeの設定で,
- role: worker
がWorker Nodeの設定です.a-controller-a-worker-mapping.yamlkind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 nodes: - role: control-plane - role: worker extraPortMappings: - containerPort: 30000 hostPort: 30000 listenAddress: "0.0.0.0" #オプション defaultは "0.0.0.0" protocol: tcp # オプション defaultはtcpextraPortMappingsでクラスタ外からコンテナへ通信できるようにします.
コンテナのデプロイ
nginxをデプロイして見ます.
$kubectl apply -f nginx.yaml pod/nginx created service/nginx-svc created使用ファイルは以下の通りです.
nginx.yamlapiVersion : v1 kind : Pod metadata : name : nginx labels : app : nginx spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: nginx-svc spec: selector: app: nginx type: NodePort ports: - protocol: TCP targetPort: 80 port: 80 nodePort: 30000Podと同時にServiceを作成する事で,クラスタ外からの通信が可能になります.
無事デプロイできました.簡単ですね.
後片付け
nginxの削除
$kubectl delete -f nginx.yaml pod "nginx" deleted service "nginx-svc" deletedクラスタの削除
$kind delete cluster Deleting cluster "kind" ...kubeadmとの比較
Kubernetesクラスタ構築ツールとして有名なものにkubeadmがあります.kindとは違い複数のコンピュータ上でKubernetesクラスタを構築でき,本番環境にも使用できます.しかし,kindは1台分の作業でクラスタ構築できるのに対し,kubeadmは立ち上げるノード分の作業が発生するのでめんどくさいです.ですのでKubernetesの挙動を確かめたい時はkindの方が良いと思います.
minikubeとの比較
kindと同じ1台のコンピュータ上でのKubernetes構築ツールとして有名なものにminikubeがあります.クラスタ構築に必要な作業量はkindとほぼ変わりませんが1台の仮想マシンがMaster NodeとWorker Nodeの両方の役割を担っています.さらにノード数を増やす事ができず,挙動を試せる幅が狭くなります.例えばKubernetesの機能を使って複数台あるWorker Nodeの内のある特定のWorker Nodeにコンテナをデプロイするということを試せません.なのでKubernetesの挙動を確かめたい時はkindの方が良いと思います.
まとめ
kindを使用することで気軽にKubernetesの挙動を確認できます.なのでこれからKubernetesの挙動を確認したいときはkindを使っていこうと思います.
参考
kind
kubeadm
minikube
Kubernetes ドキュメント
Kubernetes実践ガイド クラウドネイティブアプリケーションを支える技術 (impress top gear)
- 投稿日:2019-12-19T17:09:58+09:00
メモリ不足でXcodeのValidateができない
Validateできない問題
XcodeでApp StoreにiPhoneアプリをアップロードした時のことです。
Archiveまではスムーズだったのに、「Validate App」を実行したらめちゃくちゃ待たされた上にPCがクラッシュしました。
Validateを無視してDistributeしてもクラッシュ。
ipaファイルをコマンドで作成してもクラッシュ。
何度繰り返してもクラッシュ。エラーじゃないので調べても出てこず、めちゃくちゃ困ったので誰かの役にたつことを願って展開します。
環境
iMac (21.5-inch, Late 2012)
OS Mojave 10.14.5
Xcode 11.3
メモリ 8GB
外付けSSDで起動 (APFSでフォーマット)原因はメモリ不足
アクティビティモニタを起動しながら再度実行。
メモリタブを確認すると、見事にメモリプレッシャーが真っ赤。どうやら私のiMacのメモリ8GBでは不足している模様。
(物理メモリ8GBに対し使用メモリが7.8GB付近をうろうろした挙句勝手に再起動する)どうするか
メモリを増設すれば解決。
ただし、私のiMac2012はがっつり分解しないと増設できないため最終手段とします。そもそも8GBでも普通にみなさんリリースできているため、何か別に原因があると推測。
アクティビティモニタを眺めていると妙なことに気づく。
どれだけ高負荷になってもスワップ使用領域が0Bから変わらない。
そして8GBに達した瞬間にフリーズからのクラッシュ。
メモリスワップしてなくない?解決策
どうやら、
APFSでフォーマットしたSSDを起動に使用しているとスワップ領域がマウントされない不具合があるらしい。
とりあえずValidateの間メモリが足りてくれればよいので、手動でマウントします。ターミナルを起動し以下コマンドを実行。
diskutil list一覧で出たVolumeのうち、Volume VMとなっているdiskを探す。
(私の場合は、disk1s4でした)diskutil info diskXXX(diskの名前)diskutil infoで対象のdiskの状態を確認します。
Volume Name: VM
Mounted: No
こんな感じでMountedがNoになっていたらマウントされていないためスワップできないので、以下コマンドでマウントします。
(su権限のパスワード入力を求められます)sudo diskutil mount -mountPoint /private/var/vm diskXXXVolume VM on disk1s4 mounted
こんな感じになればOK
再度diskutil infoするとmountedがYesになっていることがわかります。結果
マウント完了後、再度Validateを実行するとスワップ使用領域が適切に使用され無事アップロードすることができました。
1つ注意点として、PCを再起動するとまたアンマウントされてしまいます。
重い処理を実施する前にはマウントされてるか確認したほうが良いかと思います。
再起動してもマウントを続ける手段は不明です。
フォーマット方式を変えるべきか…。追記
Automator使って再起動するたびにマウントするようにしました。
起動時にパスワード入力を求められるのがちょっとうざいですが快適です。
(詳細な手順は参考リンクをご覧ください)osascript -e 'do shell script "sudo diskutil mount -mountPoint /private/var/vm disk1s4" with administrator privileges'参考
スワップ領域が確保されない - Apple コミュニティ
Macの起動ディスクをAPFSフォーマットのUSB外付けSSDにするとハマる罠
- 投稿日:2019-12-19T16:08:01+09:00
nodebrewのセットアップ&操作方法(Mac)
「nodebrew」とは?
Node.jsのバージョンを管理するツールです。
rbenvやpyenvのNode.js版と考えるとわかりやすいです。環境
- OS:macOS Mojave 10.14.6
- nodebrew:1.0.1
セットアップ
nodebrewのインストール
Homebrewからインストールします。
$ brew install nodebrewnodebrewの初期設定
nodebrew setupを実行するのみです。$ nodebrew setup Fetching nodebrew... Installed nodebrew in $HOME/.nodebrew ======================================== Export a path to nodebrew: export PATH=$HOME/.nodebrew/current/bin:$PATH ========================================出力内容に従って
~/.bash_profileに以下を追記し、パスを通します。.bash_profileexport NODEBREW_DIR="${HOME}/.nodebrew" if [ -d "${NODEBREW_DIR}" ]; then export PATH=$NODEBREW_DIR/current/bin:$PATH fi
PATHを$PATH:$NODEBREW_DIR/current/binのように書くと、MacにプリインストールされているNode.jsが優先されることがあるようです。
~/.bash_profileをリフレッシュします。$ source ~/.bash_profile操作方法
Node.jsのインストール
以下のコマンドを実行し、任意のバージョンのNode.jsをインストールします。
# インストールできるNode.jsのバージョンを確認する $ nodebrew ls-remote v0.0.1 v0.0.2 v0.0.3 v0.0.4 v0.0.5 v0.0.6 … v12.0.0 v12.1.0 v12.2.0 v12.3.0 v12.3.1 v12.4.0 v12.5.0 v12.6.0 v12.7.0 v12.8.0 v12.8.1 v12.9.0 v12.9.1 v12.10.0 v12.11.0 v12.11.1 v12.12.0 v12.13.0 v12.13.1 v12.14.0 v13.0.0 v13.0.1 v13.1.0 v13.2.0 v13.3.0 v13.4.0 v13.5.0 … # 最新版をインストールする $ nodebrew install-binary v13.5.0
nodebrew setupを実行していないと、以下のエラーが発生します。$ nodebrew install-binary latest Fetching: https://nodejs.org/dist/v11.12.0/node-v11.12.0-darwin-x64.tar.gz Warning: Failed to create the file Warning: /Users/{ユーザー名}/.nodebrew/src/v11.12.0/node-v11.12.0-darwin-x64.tar Warning: .gz: No such file or directory 0.0% curl: (23) Failed writing body (0 != 1057) download failed: https://nodejs.org/dist/v11.12.0/node-v11.12.0-darwin-x64.tar.gzNode.jsのバージョン切替
nodebrew useコマンドでNode.jsのバージョンを設定します。$ nodebrew use v13.5.0Node.jsのバージョン確認
nodebrew listコマンドでインストールされているNode.jsの全バージョンを確認できます。
「current」が設定しているバージョンです。$ nodebrew list v13.5.0 current: v13.5.0実際のNode.jsのバージョンも確認します。
currentとバージョンが異なる場合、nodebrewでインストールしているNode.jsが使われていない可能性があります。$ node -v v13.5.0コラム:LTSとCurrent
Node.jsにはLTS(Long-term support)とCurrentがあります。
基本的に奇数バージョン(v11, 13など)はLTSにならないため、業務では偶数バージョン(v10, 12など)を使うことが多いと思います。
https://nodejs.org/ja/about/releases/2019/12/19現在、LTSの最新バージョンが12.14.0、Currentの最新バージョンが13.5.0です。
https://nodejs.org/ja/nodebrewでは
stableでLTSの最新バージョン、latestでCurrentの最新バージョンを指定できます。
つまり、業務でバージョンの細かい指定が不要ならnodebrew install-binary stableでインストールすればOKです。参考リンク
- 投稿日:2019-12-19T15:57:13+09:00
Chromeの標準機能でWebページ全体をわずか3コマンドでスクリーンショットする
- 投稿日:2019-12-19T15:57:13+09:00
Chromeの標準機能でWebページ全体をスクリーンショットする
スクロールが必要な画面の全体をスクリーンショットする方法です。
拡張機能や設定は不要でデフォルトのまま使えます。スクリーンショット
Command+option+i(デベロッパーツール)Command+shift+p(Run command)fullを入力してEnter (Capture full size screenshot)スクリーンショットはダウンロードフォルダに保存されます。
お試し
https://www.apple.com/jp/mac ココのサイトのキャプチャを試します。
Screenshot Capture full size screenshotを選択します。www.apple.com_jp_mac_-min.png こんな感じのファイル名でスクリーンショットされてダウンロードされます。
- 投稿日:2019-12-19T13:45:36+09:00
iOS Simulator(iPhone Simulator)をMacにインストールする方法
この記事は
![]()
MacではXcodeをインストールするとiPhoneやApple WatchのSimulatorを使うことができます。
この記事はその設定方法を書きました。手順
① Xcodeのインストール
App Store、またはDevelopperサイトからXcodeをインストールします。
・App Store
・Developerサイト② Simulatorのインストール
(※結構時間かかります & 1つあたり2.0 ~ 3.5GBぐらいあります)
1. Xcodeを起動する
2. メニューバー > Xcode > Preferenceをクリックする
3. Componentsタブを開く
4. 必要なiOSバージョンをクリックしてインストールする③ Simulatorの起動
- Xcodeを起動する
- メニューバー > Xcode > Open Developer Tool > Simulatorをクリックする
※ SimulatorはDockに追加できるので、入れておくと便利かも。![]()
④ 複数のSimulatorや、デバイス/OSを切り替えたい場合。
- Simulatorを起動する
- メニューバー > Hardware > Device 選ぶ
- iOS一覧とHardware一覧が表示されるので、使いたいものをクリックする
・ PCのSpecにもよりますが、同時起動は2 ~ 3台がよいかと。
・表示言語を日本語にしたりも可能です。Client証明書のインストールもできるし、もちろんSafariも使えます。![]()
合わせて
・iOS バージョンと Safariバージョンの対応表
https://qiita.com/yoshitake_1201/items/05a13fd77c18ff380eb6
Safariをつかいたけど、どのiOSを入れるか迷う方は↑を参考に入れてみると良いかもです。
WebKitが変わったり、SafariのMajor Versionが変わっているやつを入れたほうが容量的に優しいですね。
- 投稿日:2019-12-19T08:49:14+09:00
Macで '/Library/Developer/CommandLineTools' is a command line tools instance って言われた
- macOSMojave
- バージョン10.14.6
雑メモ。
ターミナルで下記のように言われた。
$ opendiff
xcode-select: error: tool 'opendiff' requires Xcode, but active developer directory '/Library/Developer/CommandLineTools' is a command line tools instanceそこで
$ ls -la /Library/Developer/CommandLineTools/
total 0
drwxr-xr-x 6 root admin 192 8 15 11:16 .
drwxr-xr-x 4 root admin 128 9 8 17:43 ..
drwxr-xr-x 5 root admin 160 7 13 13:44 Library
drwxr-xr-x 3 root admin 96 7 13 13:11 Packages
drwxr-xr-x 4 root wheel 128 8 15 11:16 SDKs
drwxr-xr-x 7 root admin 224 7 13 13:44 usr
$ xcode-select -p
/Library/Developer/CommandLineToolsと環境を見てみたが、何が問題なのかよくわからんかった。
ググって出てきたこの記事
http://shinriyo.hateblo.jp/entry/2018/02/17/IonicのiOSのビルドのエラーを参考にして
$ sudo xcode-select -switch /Library/Developer/CommandLineTools
$ sudo xcode-select -rとやってみたら、
$ opendiff
2019-12-19 08:37:14.020 opendiff[53779:3245168] too few arguments
2019-12-19 08:37:14.024 opendiff[53779:3245168] usage: opendiff file1 file2 [-ancestor ancestorFile] [-merge mergeFile]使えるようになった。
なお、どういう原理で問題が解消したかは把握してない。今回の記事は以上です。
- 投稿日:2019-12-19T08:08:32+09:00
GoでインストールからHello Golang!!
概要
MacにGoをインストールして、「Hello Golang!!」を表示させるところまでを解説していきます。
また、僕はシェルにfishを使っているので、fish向けの環境構築についても記載します。環境
macOS Catalina 10.15.1
Homebrew 2.1.15
fish 3.0.2Goインストール
$ brew install go$ go version go version go1.13.3 darwin/amd64Homebrew使うとコマンド一つでインストールできるので、とても便利です。
GOPATHの設定
GOPATHとは、Goのソースをまとめているディレクトリを指定している環境変数のことであり、
GOPATHを利用することで、外部ライブラリの導入やビルド作業を非常に簡単に行うことができます。bashでGOPATHを指定するには、
$ vim ~/.bash_profileで設定ファイルに以下追記をします。
export GOPATH=$HOME/go(←ワークスペースにしたい場所) export PATH=$PATH:$GOPATH/bin僕のようにシェルにfishを使っている人は、
vim ~/.config/fish/config.fishで設定ファイルに以下追記をします。
set -x GOPATH $HOME/go set -x PATH $PATH $GOPATH/binこれでGOPATHの設定は完了です。
Hello Golang!!
ではいよいよ「Hello Golang!!」を表示させていきます。
ディレクトリ構成
最終的に以下構成になります。
go/ ┣ bin/ ┣ hellogolang ┣ pkg/ ┣ src/ ┣ hellogolang/ ┣ main.go以下ファイルのそれぞれの役割に関して記載します。
bin: 実行ファイルが格納されるディレクトリ
pkg: ビルドしたパッケージオブジェクトが格納されるディレクトリ
src: パッケージごとのソースが格納されるディレクトリmain.goを作成する
go/src/hellogolang/main.goを作成したら、以下内容を記載する。go/src/hellogolang/main.gopackage main import "fmt" func main() { fmt.Println("Hello Golang!!") }実行ファイルを作成する
go installで実行ファイルを作成することができます。$ go install hellogolang実行をすると、
bin/hellogolangが作成されます。Hello Golang!!を表示する
go/ディレクトリで以下コマンドを実行します。$ bin/hellogolang Hello Golang!!Hello Golang!!が表示されました。
まとめ
Goは環境構築が簡単で勉強を始めやすくていいですね!!
あと、個人的にはfishはデフォルトでいい感じだし、コマンド履歴から自動で補完候補を表示させてくれたりと便利なので、ぜひ使ってみてください!!
- 投稿日:2019-12-19T03:23:58+09:00
iOSが無くなってもエンジニアを続けるためのSwiftUI+MacCatalyst入門
メリークリスマス(イヴ)!!??
この記事は、第二のドワンゴ Advent Calendar 2019 の24日目の記事です。
そして、今日は12/24、クリスマスイヴです。みなさん、いかがお過ごしでしょうか。この記事で伝えたい事
この記事は、以下についてまとめています。
SwiftUIを使って、動画プレーヤー上にコメントを表示、アニメーションさせる方法Mac Catalystで、 iOS/iPadOSアプリをMacアプリとして動作させる方法(ここはちょっとだけです?)対象読者は、iOS開発の経験があり、SwiftUIをこれから触っていこうとしている人です。
自己紹介
お仕事では、iOSアプリ開発を担当しています。
現在の私の役割としては、プロジェクトの進行管理や、調整ごとがメインなため、どうしても自分で手を動かすことが減ってしまっています。
しかし、その状況に甘える事なく、継続して新しい技術に触れ、技術的な視野・知識を広げていくことがエンジニアとして必要不可欠だと感じています。
というわけで、今回、エンジニアとしてのお勉強の一環として、最近学習したSwiftUIの基本事項をまとめるという形で、アドベントカレンダーに参加させていただこうと思います。よろしくお願いします ??♂️ちなみに…
自己紹介ついでに、これまで社内LTで発表したものやAppStoreに公開したものについて触れておくと、
顔認識を使って降り注ぐ自分の顔を避けながら進む、ほうれいせん予防の弾幕ゲームアプリとか、きれいなジャイアンからヒントを得たARKitを使ってイケメンになれるアプリとか、とてもここでは説明できないようなちょっとおかしなアプリばかりです…
が、社内のエンジニアの皆さんはそんなふざけたテーマでも暖かく受け止めてくれていましたw
…… 大丈夫です。今回は真面目なテーマで書きますので ?2019年のiOS開発トピックス
さて、iOSの今年のトピックスといえば、なんといっても、
WWDC2019でMac CatalystとSwiftUIが発表されたことです。
Mac Catalystは、iPad AppをMac Appでも動かせるようにするという画期的な取り組みです。これに伴い、UIKitやAppKitなどのフレームワークが拡張されました。
SwiftUIは、これまでのStoryBoardやXIBを使わずにswiftコードだけでUIが作れるという仕組みです。iOSやMacなどのUIを一括で作成する際にもSwiftUIが使われます。
Mac Catalystの理由について、Appleは、iOS/iPadOSのAppStoreには多くのアプリが存在しており、それらの多くのアプリがMacでも使えるようになることで、Macユーザがより便利になり、また、デベロッパにも新たな機会を提供できるとしています。1
この取り組みがうまくいけば、閑散としているMacAppStoreは活性化して、私たちスマホエンジニアは、デスクトップアプリ開発へと、活躍の幅を広げていけることでしょう。そう!これを自在に使いこなすようになれれば、もしiOSが無くなっても、Macアプリ開発者としてやっていける(はず)です!ということで、今回は、この
SwiftUIの基本事項とMac Catalystでの開発の導入までをまとめていきたいと思います。動作環境
以下の環境で動作確認しました。
- macOS Catalina(10.15.2 Beta)
- xcode11.3 beta
※ ベータ版である理由は特にありません。ベータ版じゃなくても動作するはずです。
開発の準備
SwiftUIで開発を進めるために、まずは、xcodeの新規プロジェクトを作成します。
話を簡単にするために今回は、iOS > SinglePageApplicationを選択します。ツリーの中から、
ContentView.swiftを選択します。このファイルがSwiftUIのエントリポイントになります。Sampleアプリのお題
Sampleプログラムを組みながら、SwiftUIの基礎を把握していきたいと思います。
ドワンゴといえば、niconico 2 です。niconicoは映像の上にコメントが流れることが特徴的な動画/生放送サービスなので、サンプルプログラムもそれにちなんだものにしたいと思います。今回は、以下のような動画の上にSwiftUIでコメントに見立てたテキストを並べて動かすというものを作ってみたいと思います。
映像は、サンタが機械(MACHINE)に溶け込み、光速に飛び込んでいく様子を撮影したものです。
重ねて表示されるテキストは、お寿司です?
ハマチがちょっと痛んでしまっています。SwiftUIの基礎を把握する
UI部分のサンプルコードは次の通りです。
ContentView.swiftimport SwiftUI struct ContentView: View { var body: some View { PlayerVCView() .overlay(GECommentView()) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }まず、SwiftUIの要点について、サンプルコードにも触れつつ、順に説明していきます。
SwiftUIではView構造は構造体(struct)で表現する
上記コードのメインとなるのは、ここです。
strict ContentView: View { //... var body: some View { // ここにViewの構造を定義する PlayerVCView() .overlay(GECommentView()) } //... }ひと目見てわかるとおり、SwiftUIではViewの構造は、構造体(struct)で表現します。
そして、viewは定義するだけでよく、 addSubview() や removeFromSuperview() などの呼び出しは不要です。viewを組みたい構造に合わせて、structを入れ子の構造にしてあげるだけでレイアウトが簡単に組み上がってしまいます。とってもシンプル!
View#overlay()は、view同士を重ね合わせるメソッドです。
たったこれだけで、プレーヤーの上にコメントを載せることができてしまいます。SwiftUIは、Viewから座標やsizeの取得ができない
なんと、SwiftUIでは、Viewのstructから直接、xy座標やsize(width,height)を取得することができません。
極力、座標指定せずにVStackなどのContainerやalignment、paddingなどを駆使して、座標やサイズ指定せずにレイアウトを組めるのが理想的な形だと思いますが、ときには、座標やサイズが必要なケースもあります。
そういうときのために用意されているのが、GeometryReaderです。(これについては、後述します。)また、UIViewのように、viewのxy座標とsizeを指定して、配置するということもできません。唯一指定できるのは、
offset(x,y)ですが、これは、そのviewの中心の座標をx:0,y:0として指定します。
それと、viewを配置する際のデフォルトは、親Viewの中心(center)に合わせて配置されるようです。宣言的なコーディング
SwiftUIでは、各viewのふるまいや見た目などについて、宣言的なコーディングを行います。前述の
.overlay()、.animation()など、.hoge()の形式で記述していきます。最初はとっつきにくいかもしれませんが、慣れてくるときっと読みやすいコードが書けるようになるんじゃないかなと思います。特徴的なview構造のルール
前述のコードの場合、登場するviewとその親子関係は次のようになります。
親;ルートのview(土台になっているview) 子:ContentView(bodyのviewと同じsize) 孫:PlayerVCView(プレーヤーのview) ひ孫:GECommentView(コメントのview)そして、SwiftUIでは、Viewの位置やsizeは、親ではなく子のViewが決めることになっているようです。
では、ここから
PlayerVCViewとGCCommentViewの中を見ていきたいと思います。UIViewControllerをSwiftUIから呼び出す方法
一般的な
SwiftUIのチュートリアルでは、もっと後のほうに出てくる内容ですが、UIKitを触った事がある人だったら、これを先に見たほうが、SwiftUIの理解が早まるかなと思うので、先に説明してしまいます。
PlayerVCView(自作struct)は動画を再生してくれるプレーヤーのviewです。iOSで動画再生する場合、AVKitのAVPlayerViewControllerを使うのが簡単です。シークバーなどのコントローラもよしなに提供してくれます。
もうちょっと複雑な制御をしたい場合、プレーヤーを作り込みたい場合は、AVPlayerを利用します。今回は、サンプルプログラムということで、
AVPlayerViewControllerを使いたいと思います。コードは以下のとおりです。PlayerVCView.swiftimport SwiftUI import UIKit import AVKit struct PlayerVCView: UIViewControllerRepresentable { typealias UIViewControllerType = AVPlayerViewController func makeUIViewController(context: UIViewControllerRepresentableContext<PlayerVCView>) -> AVPlayerViewController { let vc = AVPlayerViewController() let asset = NSDataAsset(name:"movie") let url = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("movie.mp4") try! asset!.data.write(to: url) let item = AVPlayerItem(url: url) vc.player = AVPlayer(playerItem: item) vc.player!.play() // auto play return vc } func updateUIViewController(_ uiViewController: AVPlayerViewController, context: UIViewControllerRepresentableContext<PlayerVCView>) { // NOP } }ViewControllerを
SwiftUIから利用する場合、UIViewControllerRepresentableprotocol を実装します。
実装が必要なのは、typealias1つと、次の2つのメソッドです。
typealias UIViewControllerType
- 対象のViewControllerクラスの型を指定します。今回の例では、
AVPlayerViewControllerになります。makeUIViewController(context: Self.Context) -> Self.UIViewControllerType
- ViewControllerでいうところの
viewDidLoadにあたるメソッドです。ViewControllerの初期化などはここで行います。updateUIViewController(Self.UIViewControllerType, context: Self.Context)
viewDidAppear的なもの? ViewControllerの設定変更などの処理はここで行う。ちなみに、特別な処理がない場合は、空っぽでも問題ありません。あとは、前述のように、このstructのイニシャライザを、表示したいところで呼び出すだけです。
PlayerVCView()UIViewをSwiftUIから呼び出す方法
UIViewを呼び出す方法も紹介しておきましょう。
AVPlayerを利用するケースだと、以下のようなコードになります。PlayerView.swiftimport SwiftUI import UIKit import AVKit struct PlayerView: UIViewRepresentable { typealias UIViewType = PlayerUIView func makeUIView(context: UIViewRepresentableContext<PlayerView>) -> PlayerView.UIViewType { let player = AVPlayer(url: URL(string: "<<動画のURL>>")!) player.play() let playerView = PlayerUIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100), player: player) return playerView } func updateUIView(_ uiView: PlayerUIView, context: UIViewRepresentableContext<PlayerView>) { // NOP } } class PlayerUIView: UIView { private let playerLayer = AVPlayerLayer() init(frame: CGRect, player:AVPlayer) { super.init(frame: frame) self.playerLayer.player = player layer.addSublayer(playerLayer) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func layoutSubviews() { super.layoutSubviews() playerLayer.frame = bounds } }AVPlayerはUIViewを直接持っていないので、別途UIViewのクラスを作って、AVPlayerのlayerをaddSublayerし、そのveiwを返すようにします。
UIViewRepresentableprotocol を利用しますが、UIViewControllerRepresentableとほとんどやることは変わりません。レイアウトを決める
ここからは、
GECommentViewの中を解説していきます。
プレーヤー上にコメントを表示し、アニメーションさせるviewです。
実装は次のとおりです。GECommentView.swiftimport SwiftUI struct GECommentView: View { @State private var moveIt = false @State private var commentStrs = [ "マグロ", "サバ", "ハマチ", "たまご" ] var animation: Animation { return Animation.default } var body: some View { let animation = Animation.easeInOut(duration: 1.0) return VStack { CommentLabelView(text: commentStrs[0], offset: moveIt ? 120 : -120, backgroundColor: .red) { view in DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { self.moveIt.toggle() } } .animation(animation) CommentLabelView(text: commentStrs[1], offset: moveIt ? 120 : -120, backgroundColor: .blue) { view in } .animation(animation.delay(0.1)) CommentLabelView(text: commentStrs[2], offset: moveIt ? 120 : -120, backgroundColor: .green) { view in } .animation(animation.delay(0.2)) CommentLabelView(text: commentStrs[3], offset: moveIt ? 120 : -120, backgroundColor: .yellow) { view in } .animation(animation.delay(0.3)) } .onTapGesture { self.moveIt.toggle() } } } struct CommentLabelView: View { let text: String var offset: CGFloat let backgroundColor: Color var completion: (CommentLabelView) -> Void var body: some View { Text(text) .font(.headline) .padding(5) .background(BackgroundLabelView(cornerRedius: 5, color:backgroundColor)) .foregroundColor(Color.black) .modifier(SlidingEffect(offset: offset, completion: { effect in self.completion(self) })) } } struct SlidingEffect: GeometryEffect { var offset: CGFloat let goalOffset: CGFloat var completion: (SlidingEffect) -> Void init(offset: CGFloat, completion: @escaping (SlidingEffect) -> Void) { self.offset = offset self.goalOffset = self.offset self.completion = completion } var animatableData: CGFloat { get { return CGFloat(offset) } set { offset = newValue if offset == self.goalOffset { self.completion(self) } } } func effectValue(size: CGSize) -> ProjectionTransform { return ProjectionTransform(CGAffineTransform(a: 1, b: 0, c: 0, d: 1, tx: offset, ty: 0)) } } struct BackgroundLabelView: View { var cornerRedius: CGFloat var color: Color var body: some View { GeometryReader { geometry in VStack(spacing:0) { RoundedRectangle(cornerRadius: self.cornerRedius).foregroundColor(self.color) .frame(width: geometry.size.width, height: geometry.size.height / 2) RoundedRectangle(cornerRadius: self.cornerRedius).foregroundColor(Color.white) .frame(width: geometry.size.width, height: geometry.size.height / 2) } } } } struct GECommentView_Previews: PreviewProvider { static var previews: some View { GECommentView() } }まず注目してほしいのが、bodyの中の、大枠の構造です。
var body: some View { //... return VStack { //... } .onTapGesture { self.moveIt.toggle() } }
VStackというのは、V = verticalなstack、つまり垂直に要素を並べるためのstructです。このようなstructはいくつか種類があり、
HStack… 水平に要素を並べられるZStack… overlayで要素を並べられるScrollView… スクロールさせられるSpacer… スペースを作り出すなどがあります。3
ジェスチャーを定義する
これも簡単です。
先ほどあった以下の部分は、.onTapGesture { self.moveIt.toggle() }該当のviewをタップした際に、カッコ内の処理をさせるという事を示しています。
この例では、viewをタップする度に、moveItプロパティのbool値をtrue/falseとトグルで切り替えます。アニメーションを定義する
アニメーションもジェスチャーのように簡単に定義できます。
先ほどのコードの以下の部分、let animation = Animation.easeInOut(duration: 1.0) CommentLabelView(...) { view in } .animation(animation.delay(0.1)) CommentLabelView(...) { view in } .animation(animation.delay(0.2))このように書くだけで、イージングでアニメーションできます。 4
よく見ると、下のCommentLabelViewには、delayをかけています。
こうする事で、同時にアニメーションを効かせたときに、ちょっとずつアニメーションをずらしていくことが可能です。他にも、
linearや、springなど、様々なアニメーションが用意されています。5viewの状態を管理する
viewの状態管理には、いくつか種類がありますが、今回は、
StateとBindingについて紹介します。State - viewの内部でのみ状態を持たせたい場合
viewの内部でのみ状態を持ちたい場合は、
@Stateプロパティラッパーを使います。struct GECommentView: View { @State private var moveIt = false @State private var commentStrs = { //... } //... var body: some View { //... CommentLabelView(text: commentStrs[0], offset: moveIt ? 120 : -120, backgroundColor: .red) { view in //... } }途中、省略しまくりなコードで若干わかりにくいですが、注目すべき箇所は、body内で
moveItがCommentLabelViewを生成する際の条件判定に利用されているということです。
このように、@Stateなプロパティをbody内で参照している場合、そのプロパティ値を変更するたびに、body内のviewが再計算されるという仕組みになっています。
これによって、Viewの状態に合わせてアニメーションさせたり表示を変更することが可能になります。たとえば、以下のように書くと、
var body: some View { if moveIt { HogeView() } }moveItがtrueのときだけHogeViewを表示し、falseのときは非表示(viewが存在しない状態)という動きを作ることができます。
Binding - viewの外から状態を指定したい場合
viewの外から状態を指定したい場合は、
@Bindingを使います。
今回のサンプルでは、Bindingを使っていないので書き方だけ紹介します。struct HogeView: View { @Binding var moveIt: Bool = false //... }このように定義し、
HogeView(moveIt: true)外から状態を渡します。
GeometryReaderで座標やsizeを取得する
最初のほうで少し触れましたが、viewから座標やsizeを取得する方法がありません。
そういうときは、GeometryReaderを利用します。struct BackgroundLabelView: View { //... var body: some View { GeometryReader { geometry in VStack(spacing:0) { RoundedRectangle(cornerRedius: self.cornerRedius).foregroundColor(self.color).frame(width: geometry.size.width, height: geometry.size.height /2) RoundedRectangle(cornerRedius: self.cornerRedius).foregroundColor(Color.white).frame(width: geometry.size.width, height: geometry.size.height /2) } } }このように、GeometryReader のクロージャの引数の
geometry(GeometryProxy型) を使うと、sizeやwidth、heightなどが取得できます。
この場合、BackgroundLabelViewを貼り付けたCommentLabelViewのTextのサイズをとることができます。GeometryEffectで複雑なアニメーションを実現する
シンプルなアニメーションの場合は、
.animation()やwithAnimation { }で十分ですが、複雑なアニメーションを行う場合は、それだけでは難しいです。
そういうときは、GeometryEffectを使うとよいです。struct CommentLabelView: View { //... var body: some View { Text(text) //... .modifier(SlidingEffect(offset: offset, completion: { effect in self.completion(self) })) } } struct SlidingEffect: GeometryEffect { var offset: CGFloat let goalOffset: CGFloat var completion: (SlidingEffect) -> Void init(offset: CGFloat, completion: @escaping (SlidingEffect) -> Void) { self.offset = offset self.goalOffset = self.offset self.completion = completion } var animatableData: CGFloat { get { return CGFloat(offset) } set { offset = newValue if offset == self.goalOffset { self.completion(self) } } } func effectValue(size: CGSize) -> ProjectionTransform { return ProjectionTransform(CGAffineTransform(a: 1, b: 0, c: 0, d: 1, tx: offset, ty: 0)) } }
CGAffineTransformを使って、色々とviewの形状を変更することができそうですが、今回はシンプルにviewを左右に移動させることに利用しています。サンプルコードで工夫したところは、アニメーションによってviewが左右の端まで来たことを親view(この場合、GECommentView)に通知するために、completionのクロージャを利用しているところです。
Macアプリとして実行する
ここまでSwiftUIの基礎を駆け足で紹介してきました。
最後にMacCatalystを利用して、macアプリとして動かしてみましょう。実行は非常に簡単です。
プロジェクトファイル>General>Deployment Info>Device>「Mac」のチェックをつける実行すると、このような表示になります。
おわりに
やや駆け足でしたが、SwiftUIの使い方の概要を紹介しました。
(Mac用に最適化する部分については全く触れられなかったので、もっと勉強していきたいと思います。)viewを組み立てるときの概念がこれまでのUIKitとは異なっていて、最初はとっつきにいくい印象を受けますが、慣れればもっと簡単に、複雑なレイアウトをシンプルなコードで実現できるのではないかと思います。
viewまわりのコーディングをGUIを使わずに開発できるようになることは、今後のアプリ開発スタイルにも様々な影響を与えるのではないかなと思います。今後は、そのあたりも模索してみたいと思っています。形になったらまた記事にしたいと思います。最後まで読んでいただき、ありがとうございました!
明日は、アドベントカレンダーも最終日です。
しかし… おかわり版である 第二のドワンゴ Advent Calendar 2019 の最終枠はまだ空いている模様。果たしていったい誰が書くのか!? いや、書かないのか!?乞うご期待です!それではみなさま、素敵なクリスマスをお過ごしください?
お役立ちリンク
SwiftUI Tutorials
まずは一次情報を。チュートリアルがしっかり用意されているので学習しやすいです。
APIドキュメントはまだ説明が足りていない箇所も見受けられます。https://developer.apple.com/documentation/swiftui
https://developer.apple.com/tutorials/swiftui/Mac Catalyst
こちらも一次情報。
しっかりドキュメントがまとまっています(まだあまり読めていませんが…)https://developer.apple.com/design/human-interface-guidelines/ios/overview/mac-catalyst/
https://developer.apple.com/documentation/uikit/mac_catalystThe SwiftUI Lab
SwiftUIの細かいところまで具体的なサンプルコードで解説されていてすごく参考になります。https://swiftui-lab.com
https://swiftui-lab.com/swiftui-animations-part2Personal-Factory
WWDCのセッションや、SwiftUIの基礎について、詳しくわかりやすく書かれています。
最初に一読したほうがその後の学習が楽になると思います。https://blog.personal-factory.com/2019/08/20/building-custom-views-with-swiftui/
https://blog.personal-factory.com/2019/12/08/how-to-know-coorginate-space-by-geometryreader/AVPlayerをSwiftUIで利用する方法
AVPlayerをSwiftUIで利用する際の実装方法の参考になります。
https://medium.com/@chris.mash/avplayer-swiftui-b87af6d0553
Introducing iPad Apps for Mac https://developer.apple.com/wwdc19/205 ↩
niconico http://www.nicovideo.jp/ ↩
View Layout and Presentation https://developer.apple.com/documentation/swiftui/view_layout_and_presentation ↩
イージングの基本 https://developers.google.com/web/fundamentals/design-and-ux/animations/the-basics-of-easing?hl=ja ↩
Drawing and Animation https://developer.apple.com/documentation/swiftui/drawing_and_animation ↩
Mac Catalyst (Human Interface Guidelines) https://developer.apple.com/design/human-interface-guidelines/ios/overview/mac-catalyst/ ↩
Mac Catalyst (Documentation) https://developer.apple.com/documentation/uikit/mac_catalyst ↩

















