Xcode の自作 Template / Theme を置いておくためのレポジトリ
Xcode の Template や Theme を自作しておくととても便利なのだが、PC 買い替え時などに移行が面倒でいつの間にか使わなくなってしまうということがある。面倒は
- 新しい PC に Template / Theme を移動する
- 新しい PC で Xcode が Template / Theme を読み込むパスを調べてそれぞれ配置する
という2段階がある。実際にやってみるとどちらも1分くらいで終わるが、1分くらいで終わる作業が面倒に感じてやらないまま年単位の時が経ち、そんなことばかりで人生が過ぎていってしまう...。
この問題をなんとかするため、以下のように Template / Theme を置いておくレポジトリを作った。レポジトリにはセットアップ用のスクリプトが置いてあるので、レポジトリを close してスクリプトを実行するだけで適切なパスにファイルが配置されるようになっている。これで PC を移行しても5秒くらいでいつもの Template / Theme が使える状態になった。
問題はこのレポジトリを作ってから一回も PC 移行をしていないので、この記事を書いている時点で将来の PC 移行時にこのレポジトリを本当に自分が使ってくれるのかがわからないということだ。一番可能性が高いのはこの仕組みの存在自体を忘れてしまうことなので、忘れないようにこの記事を書きました。
多摩川の多摩川駅周辺
川見てる Advent Calendar 2021 の16日目です。
自分がよく見てる川は多摩川なんですが、一口に多摩川と言っても場所によってけっこう印象が変わります。今日は多摩川の多摩川駅周辺を紹介します。
多摩川駅は多摩川の中では二子玉川より少し下流、川崎よりも少し上流にあります。完全に多摩川を代表しているような名前の割にとくに何か大したものがあるわけではありません。ただ、大きな橋もあるし、河川敷の雰囲気がよくて人もあまり多くないので多摩川を見るのには適した場所だと思います。
多摩川駅近くの丸子橋から川を見た様子です。
丸子橋の周辺は土手から河川敷までが階段のようになってて座ることができます。ここでたまに通る電車の音を聞きながら夜になるのを待っていると精神が落ち着いてきて瞑想と同じ効果があると言われています(個人の感想)。
多摩川駅の対岸には武蔵小杉のビル群が見えるのもよいです。多摩川駅周辺の河川敷はかなり草が生えていたり適当な感じなので、多摩川駅側から武蔵小杉側を見ると手前から草、川、草、ビルという順に並んで昔の近未来 SF みたいな風景になります。
ちょっと高台の公園から見てもいいでしょう。
夕方から夜はこんな感じ。
以上です。
聖蹟桜ヶ丘の川
これは 川見てる Advent Calendar 2021 の 13 日目です。
自分が住んでいる聖蹟桜ヶ丘にはいろいろな川が流れていて日頃からありがたく思っているのでこの記事で紹介します。
多摩川
まずは多摩川です。多摩川は京王線の聖蹟桜ヶ丘駅から5分ほど歩いたところにあります。川が大きいのはもちろん河川敷が広くていい感じで、夕方になるとまったく必要がないのについ多摩川に行ってしまうということが週4日くらいは起こっています。
土手から見た河川敷の様子です。広さが伝わるでしょうか。
川に近づくとこうなります。
鉄塔がいっぱい立っているのが見られるのもありがたいです。
電車が走ってる様子がいろんな距離から見られます。まずは遠くから。
近くから。
乞田川
乞田川は多摩川に流れ込む川で、多摩センター駅のあたりが起点です。
始まりはこんな感じです。謎なオブジェから流れ出しているんですね。余談ですが多摩センター近辺にはこういうかっこいい謎のオブジェがけっこうあります。
多摩センター駅の近くでは、緑も適度にありつつちゃんと整備されていて歩きやすいです。
下流では川幅が広くなってきます。
聖蹟桜ヶ丘付近で次に紹介する大栗川に合流するのですが、その合流地点もダイナミックでいい感じです。左が乞田川、右が大栗川です。
大栗川
大栗川は乞田川の近くを流れている川です。乞田川と比べると自然が残っている様子を楽しめます。
こんな感じで緑が多く見られます。
生き物も多く、鳥や魚、亀などを見ることができます。
と言いつつ、遊歩道が整備されている区間もあります。
多摩川に合流する直前のあたりもゆったりしていていいです。
謎の小川
大栗川が多摩川に合流するあたりでは、並行して小川が流れています。名前とかどこから来ているかとかはよくわかっていないのですが、かかっている橋や街灯などのオブジェクトがかっこいいので気に入っています。
程久保川
聖蹟桜ヶ丘から高幡不動の方に多摩川沿いを歩いて行くと程久保川に当たります。程久保川はここまでの比較的きれいに整備された川と比べると人間のリアルを感じさせる川で、年季の入ったアパートなどが川沿いに並んでいるところなどがよいです。
これといった特徴はないのですが、歩いていると懐かしい感じがしてとても落ち着く川です。
秋にはコスモスが咲くのも注目したいポイントです。ちなみになんとなくコスモスだと判断しているんですが、花に詳しくなさすぎるのでコスモスじゃなかったらすみません。
多摩川に合流するあたりでは煙突も見えて調子がよいです。
浅川
浅川は程久保川の近くを走る川で、そこそこの広さがあります。
川幅があるだけあって空も開けているので川沿いの遊歩道からきれいに夕焼けが見えます。
浅川で一番気に入っている場所は万願寺歩道橋です。橋の形がよく、見るだけで心が落ち着く効果があります。
多摩都市モノレールと交差する地点もポイントです。ちょうどモノレールが通るところを見られるとありがたい気分になります。
モノレールの下から見た夕焼けです。
まとめ
聖蹟桜ヶ丘周辺にはよい川がいくつか流れています。家の近くにいろいろな川があるというだけで精神が安定しますね。
みなさんの街の川についてもぜひ教えてください。
あるぴんの泣き顔カット修正における木下監督の落ち着き
この記事は SHIROBAKO Advent Calendar 2021 の 5日目です。
今回 Advent Calendar のために SHIROBAKO を見直していたんですが、今まで自分の中では唐揚げや牢屋の印象しかなかった木下監督が急に気になってきました。この記事では、監督のよいところが最もよく現れているあるぴんの泣き顔のカットの修正について書こうと思います。
えくそだす4話のアフレコにて、あるぴんが泣き顔を見せるシーンの芝居を見た監督はなにかが違うと感じます。効果音や劇伴を変えてみても消えない違和感に、監督は気づいてしまいます。
山田「じゃあ何が違うんですか」
監督「絵かな...」
ここからこのカットの作業を一からやり直すという話になるのですが、この「絵かな...」という台詞、冷静に考えるとめちゃくちゃ言いづらくないですか?
えくそだすのスケジュールはこのアフレコ時点でかなり遅れていて、SHIROBAKO 2話の冒頭で本田さんが「3話目にして3日前納品なんて普通ないから」と言っています。えくそだす全体だけではなく監督個人の進捗も悪く、アフレコに入った瞬間に稲浪さんに「それでいつ上がるんですか?絵コンテ」と聞かれていることから関係者の多くが認識しているほど絵コンテが遅れていることがわかります。なんとかしてスケジュールを巻き返したい状況だと思います。
また、現状のカットの演出をした山田さんが横に座っているというのもポイントです。演出としてのキャリアがあるであろう山田さんがこれでいいと思って出しているものを否定するのは永年の仕事仲間とはいえちょっと躊躇してしまうのではないかと思います。
そんな中で作業の一からのやり直しを要求できるところに監督のすごさを感じたのですが、さらにすごいのは普通に考えると言いづらそうなこの話を監督は当たり前のように言い出しているということです。その後の会話の内容やトーンを見ても、周りが慌ててるのに対して監督だけは異常なほど落ち着いてるんですよね。
宮森「すみません、私その設定確認できてなかったです!」
監督「ああ、ごめん。これ俺の中の話」
(中略)
稲浪「で、音楽はままでいいんだよね?変える?」
監督「いやー、絵の方を変えるんで」
宮森「え、あの、それって表情修正でしょうか?」
監督「いや、演技から全部かな!」
(中略)
宮森「あの、つまり原画から全部新作ってことですか?」
監督「え、あ...うん、そういうことになるかな」
山田「まじですか監督!崩れた動画より綺麗な止め絵ですよ、そんなとこまで直してたら納品間に合いませんよ!」
監督「いや、そもそも演技があるぴんとはずれてるので」
これは、監督は作品をよくするということをとにかく大切にしていて、そのためならやり直しによってスケジュールがさらに押すことや、山田さんの演出を否定することくらいは必要だと自然に考えているためでしょう。また、具体的な修正方針も自分で立て、その内容にもやる価値があるという自信を持っていることが伺えます。それらが監督の仕事だと言われるとそうかもしれませんが、自分はこういう振る舞いを当たり前のようにできるのが木下監督のよいところだと思いました。
作品をよくするための監督の姿勢は実は SHIROBAKO の他のシーンからも見てとれて、現場のメンバーとの打ち合わせではいつもとても細かく自分のやりたいことを伝えているし、えくそだす最終話の納品間際に電話で細かい撮影の指示を出している場面も印象的です。また、そもそもえくそだすの絵コンテを遅らせてしまったり、さらにそれをひっくり返したりする様子もこだわりの強さを感じさせます。
あるぴんのカットをやり直しがえくそだすに対して与えた影響については作中では描かれていません。あるぴんの芝居によって作品の評価が高まったり、ファンが増えたりしたかもしれません。一方で、やり直し前のカットに関わっていたメンバーのモチベーションを下げたり、スケジュールに無理が出てその後の話数のクオリティが落ちた可能性もあるのではないかと思います。全体としてやるべきだったとかやるべきでなかったと言えるような簡単な話ではないと思いますが、木下監督はやる側の人間だということでしょう。個人的には、自分がスタッフで大変な目にあっても、本田さんから「監督、V編で泣いてたってさ。あるぴんのカット」と伝えられ、実際に修正後のカットの素晴らしさを見たら、やってよかったし監督はやっぱりすごいなと思えるのではないかという気がします。
Heart of Swift を読んだ
前から気になっていた Heart of Swift を読んだ。とても面白かったのでメモしておく。
Heart of Swift は、値型が中心になっているという特徴を持つ Swift でどのように抽象化を行っていくべきかが書かれている文章だ。
まず第1章の「Value Semantics」でそもそも値型とは何でどういうところが良いのかや、なぜ Swift が値型を中心にできたのかということが説明される。第2章の「Protocol-oriented Programming」では第1章の内容を前提にして、
- Swift では参照型が中心の言語とは異なる抽象化の方法をとる必要があること
- protocol には型としての利用と制約としての利用の2種類があること
- リバースジェネリクスの考え方やOpaque Result Type が解決する問題
などについて書かれている。
二部構成になっているのが良くて、自分がとくに面白いなと思ったのは2章の protocol とか Opaque Result Type の話で、そこは Value Semantics の話がなくても面白く読めそうなんだけど、1章があることによってそれらの抽象化の方法は値型が中心の言語であるからこそ必要なんだと納得できて Swift の思想が理解できた気になり満足感がある。
以下とくに勉強になったことのメモ。
型として・制約としての protocol
protocol は型として使われる場合と制約として使われる場合がある。わかりやすい例は以下。
protocol Animal { func foo() -> Int } struct Cat: Animal { func foo() -> Int { 1 } } struct Dog: Animal { func foo() -> Int { 2 } } // Animal は型として使われている func useAnimal(_ animal: Animal) { print(animal.foo()) } // Animal は制約として使われている(ジェネリクス) func useAnimal<A: Animal>(_ animal: A) { print(animal.foo()) }
制約としての protocol はジェネリクスと一緒に使われる。もちろん型としての protocol でしか実現できないことはあるが、どちらでもよい場合は protocol は制約として用いるべきだと書かれている。これは、 protocol を型として使う場合は実行時にどんな型が入ってくるのかわからないため、どんな型でも入る Existential Container というものに包む必要があるからだ。これにより実行時に Existential Container の分のメモリを使ってしまうことや、Existential Container に包んだり剥がしたりするオーバーヘッドがかかることが問題になる。
var dog: Dog = Dog() var animal: Animal = Dog() print(MemoryLayout.size(ofValue: dog)) // 0 print(MemoryLayout.size(ofValue: animal)) // 40。Existential Container のサイズ
この理由で、protocol は型ではなく制約として使うべき。また、(これは本質的な問題はないのでそのうち解決するかもと書かれているが)assosiatedtype をもつ protocol は型として使うことができないのでそもそも制約として使うしかない。
リバースジェネリクスと Opaque Result Type
protocol を引数として使う場合は制約として使うのが良いことはわかった。返り値に protocol を使う場合についても同じように制約として使いたいが、今の Swift では完全にそれを実現することはできない。ジェネリクスの逆のリバースジェネリクスという仕様が Swift に入ってないからだ。
// Animal は型として使われている。パフォーマンスの観点から望ましくない func makeAnimal() -> Animal { Cat() } // Animal は制約として使われている(リバースジェネリクス)。現状の Swift の仕様にはない func makeAnimal<A: Animal>() -> A { Cat() // コンパイルエラー }
しかし、実はリバースジェネリクスで実現したいことは部分的には Swift 5.1 で入った Opaque Result Type で実現できるようになっている。SwiftUI でよく使うやつ。
struct MyView: View { var body: some View { // Opaque Result Type // ... } }
上の例では、 body
は View
protocol に従う何らかの具体的な型であるということを言っている。つまり、 View
は制約として使われている!
比較のために View
protocol を型として使うことを考えてみると var body: View {}
という定義になりそうだが、このようにしてしまうと開発者が body
は View
に従っているということだけ知っておけばよくて具体的な型を意識しなくてよいという点で protocol のメリットを享受できる一方でパフォーマンスの問題がある。もちろん、実質的な問題として現状 assosiatedtype をもつ View
は型として使えないので var body: View {}
の表記はコンパイルエラーになるという話もある。
Opaque Result Type の var body: some View{}
では開発者は body
の具体的な型を意識しなくてよいというメリットは残しつつコンパイラは具体的な型を知っているので実行時のオーバヘッドがない。
iOS Widget 対応でやったこと
SwiftUI を使って個人開発しているアプリで Widget 対応をしたので、やったことを忘れないようにメモしておきます。
ログの整備
ふつうのアプリ開発を趣味でやっている範囲だとデバッガと print
で不自由なく開発できてしまうんですが、 widget 開発する上ではちゃんとログを出力しないと思い通りに動かないときそれだと何もわからんということがあるんですよね。具体的には、アプリと widget 両方の動作を同時に追いたいときや widget をタップして deeplink でアプリに遷移するときなどは デバッガも print
も役に立ちません。
そこで、 Unified Logging を使って要所でログを出すようにしました。以下のビデオにログの概念、出力の方法、参照の方法などとりあえず必要なこと全てがまとまってます。
deeplink 対応
続いて widget をタップしたときの DeepLink の対応です。今回 widget 対応したのが SwiftUI ライフサイクルのアプリだったので、以下のように関連するいくつか View で onOpenURL
を使って対処しました。
UIKit だと DeepLink をハンドリングする1つのコンポーネントがいて、そいつが DeepLink が指示するように ViewController を重ねていくのがよくある DeepLink 対応だと思うんですが、SwiftUI では View 自身が DeepLink をハンドリングしないといけないのでかなりマインドセットが違って苦労しました。
SwiftUI でも以下の記事のように View の階層自体をグローバルな EnvironmentObject
で持っていれば、その object の状態を変えることでアプリの表示を一箇所から完全にコントロールできるので UIKit と同様のハンドリングが可能になるのでそれも試してみたのですが、DeepLink に関係なくルーティング自体を追加・改修するコストが高すぎてつらくなったのでやめました。
keychain access group の設定
ログインさせるアプリの widget では、アプリ本体と widget でクレデンシャルを共有する必要があると思います。そのために keychain access group を設定しました。以下のドキュメントを見れば基本的なことはわかります。なんとなく難しそうなイメージがあったけど単に entitlements を追加してグループ名を決めるだけだった。
細かい点で以下の記事に助けられました。
widget 追加
widget を追加します。以下の一連の WWDC セッションを見れば何をやるかはだいたいわかります。
レイアウトでは普通に SwiftUI を書けばいいだけなので一瞬なんですが、いくつかはまりどころがありました。
まず、画像を非同期で読み込めないので以下の記事のように Data(contentsOf: url)
を使う必要があります。
また、widget と アプリから共有の embedded framework を参照することになったのですが contains disallowed nested bundles
というエラーになってしまいました。Build Phases に以下のスクリプトを追加することで回避できましたが、正直なんで直ったのかは雰囲気でしか理解してません。iOS のこの辺の話難しいんですよね...ちゃんとキャッチアップしたい。
まとめ
widget 対応は意外と簡単なところも意外と大変なところもある。人生と同じですね(?)
ミーティング中かどうかを自動で妻に伝える
これは Kyash Advent Calendar 2020 12日目の記事です。今日は箸休め回です。
リモートワークをしていて急にミーティングが入ることがありますよね。自分は妻と一緒に暮らしているので、ミーティングが始まる前にその旨を伝えておかないと、妻がいきなりビデオチャットに登場してしまうリスクや、ミーティング中にいつの間にかご飯の時間を過ぎているなどで家庭内の雰囲気が引き締まるリスクがあります。これまでは都度口頭で伝えていたのですが、伝え忘れてしまうことやタイミングが悪くて伝えられないことがあるので自動で伝わってほしいなと感じ、ちょっと思いついた方法を試してみたら1時間くらいでできたので書き残しておきます。環境はMacです。
まず、伝達手段を決めます。最初、ミーティングが始まったらなんらかの方法でLEDを光らせたり音を出したりして伝えるか...?と思っていたのですが、冷静に考えると家庭内slackがあるのでslackで連絡すればいいということに気付きました。Incoming Webhookを使って適当なチャンネルにミーティング開始時/終了時にそれぞれ投稿することにします。Incoming Webhookについてはネット上にいくらでも情報がありますが、とりあえず以下のドキュメントに従っておけば大丈夫です。
続いて、ミーティングの開始/終了を検知する方法を考えます。まずミーティング中かどうかですが、基本的にchromeを使ってgoogle meetでミーティングをすることが多いので *1 、chromeの開いているタブをすべて見て、その中にmeetが存在したらミーティング中と見なすのが良さそうです。調べてみたところ、これには chrome-cli
というコマンドラインツールが使えそうでした。使い方はREADMEに載っていますが、とりあえず chrome-cli list tabs
で開いているタブのタイトルが列挙されるのでその中に Meet
を含むタイトルが存在するかでミーティング中かを判定できます。
それでミーティング中かどうかはわかるとして、ミーティング開始/終了を検知するためには、「ミーティング中かどうか」の状態が切り替わったことを知る必要があります。それには、「ミーティング中かどうか」を調べるスクリプトを定期実行して、
- 前回実行時にミーティング中でなく、今回はミーティング中だったらミーティングが開始した
- 前回実行時にミーティング中で、今回はミーティング中でなかったらミーティングが終了した
と考えれば大丈夫そうです。これを実現するためには前回のスクリプト実行時の結果を覚えている必要があります。いくつか方法が考えられますが、スクリプト実行時にミーティング中だった場合は決まったpathに一時ファイルを作ることにして、その次のスクリプト実行時には一時ファイルがあれば前回はミーティング中だったんだなと判断するのが簡単そうです。
ここまでの流れをスクリプトにしたものが以下です。簡単な処理なのでシェルスクリプトで書きました。
#!/bin/sh flagFile="/path/to/flag_file" slackWebhookURL="https://url.of/slack/webhook" # slack()は引数として受け取ったメッセージをslack通知する slack() { curl \ -X POST \ -d "{\"text\": \"$1\"}" \ $slackWebhookURL } # 今ミーティング中かどうか isOnMeeting=false if [ $(/usr/local/bin/chrome-cli list tabs | grep -c 'Meet -') -gt 0 ]; then isOnMeeting=true fi # 前回のスクリプト実行時にミーティング中だったかどうか wasOnMeeting=false if [ -e $flagFile ]; then wasOnMeeting=true fi if "$isOnMeeting" && ! "$wasOnMeeting"; then slack ミーティングが始まりました:baby_chick: if [ ! -e $flagFile ]; then # ここで一時ファイルを作成しミーティング中であることが次回のスクリプト実行時にわかるようにする touch $flagFile fi elif ! "$isOnMeeting" && "$wasOnMeeting"; then slack ミーティングが終わりました:chicken: if [ -e $flagFile ]; then rm $flagFile fi fi
あとはこのスクリプトを適当な間隔で実行すればOKです。Macで何らかのスクリプトを定期実行するには launchd を使うのが一番簡単だと思います。自分は以下の記事を参考に設定し、30秒間隔でスクリプトを実行しています。
エンジニア間でのちょっとした相談では突発的に短いミーティングが発生することがあると思いますが、そんなときもただミーティングをしているだけで自動的に家庭内slackに以下のような投稿が飛び、平和が維持されています。
*1:2020年11月くらいまで。現在のKyashではzoomへの移行が進んでおり、この記事の方法は引退が近づいています