Xcode の自作 Template / Theme を置いておくためのレポジトリ

Xcode の Template や Theme を自作しておくととても便利なのだが、PC 買い替え時などに移行が面倒でいつの間にか使わなくなってしまうということがある。面倒は

  • 新しい PC に Template / Theme を移動する
  • 新しい PC で Xcode が Template / Theme を読み込むパスを調べてそれぞれ配置する

という2段階がある。実際にやってみるとどちらも1分くらいで終わるが、1分くらいで終わる作業が面倒に感じてやらないまま時が過ぎてしまうことがよく知られている。

この問題をなんとかするため、以下のように Template / Theme を置いておくレポジトリを作った。レポジトリにはセットアップ用のスクリプトが置いてあり、それを実行するだけで適切なパスにファイルが配置されるようになっている。これで PC を移行しても5秒くらいでいつもの Template / Theme が使える状態になった。

github.com

問題はこのレポジトリを作ってから一回も PC 移行をしていないので、この記事を書いている時点で将来の PC 移行時にこのレポジトリを本当に自分が使ってくれるのかがわからないということだ。一番可能性が高いのはこの仕組みの存在自体を忘れてしまうことなので、忘れないようにこの記事を書きました。

多摩川の多摩川駅周辺

川見てる Advent Calendar 2021 の16日目です。

自分がよく見てる川は多摩川なんですが、一口に多摩川と言っても場所によってけっこう印象が変わります。今日は多摩川多摩川駅周辺を紹介します。

多摩川駅多摩川の中では二子玉川より少し下流、川崎よりも少し上流にあります。完全に多摩川を代表しているような名前の割にとくに何か大したものがあるわけではありません。ただ、大きな橋もあるし、河川敷の雰囲気がよくて人もあまり多くないので多摩川を見るのには適した場所だと思います。

多摩川駅近くの丸子橋から川を見た様子です。

f:id:muijp:20211216085118j:plain

f:id:muijp:20211216090253j:plain


丸子橋の周辺は土手から河川敷までが階段のようになってて座ることができます。ここでたまに通る電車の音を聞きながら夜になるのを待っていると精神が落ち着いてきて瞑想と同じ効果があると言われています(個人の感想)。

f:id:muijp:20211216085231j:plain


多摩川駅の対岸には武蔵小杉のビル群が見えるのもよいです。多摩川駅周辺の河川敷はかなり草が生えていたり適当な感じなので、多摩川駅側から武蔵小杉側を見ると手前から草、川、草、ビルという順に並んで昔の近未来 SF みたいな風景になります。

f:id:muijp:20211216085919j:plain

ちょっと高台の公園から見てもいいでしょう。

f:id:muijp:20211216090115j:plain

夕方から夜はこんな感じ。

f:id:muijp:20211216090015j:plain

f:id:muijp:20211216090042j:plain


以上です。

聖蹟桜ヶ丘の川

これは 川見てる Advent Calendar 2021 の 13 日目です。

自分が住んでいる聖蹟桜ヶ丘にはいろいろな川が流れていて日頃からありがたく思っているのでこの記事で紹介します。

多摩川

まずは多摩川です。多摩川京王線聖蹟桜ヶ丘駅から5分ほど歩いたところにあります。川が大きいのはもちろん河川敷が広くていい感じで、夕方になるとまったく必要がないのについ多摩川に行ってしまうということが週4日くらいは起こっています。

土手から見た河川敷の様子です。広さが伝わるでしょうか。

f:id:muijp:20211210185746j:plain

川に近づくとこうなります。

f:id:muijp:20211210185323j:plain

f:id:muijp:20211210185325j:plain

鉄塔がいっぱい立っているのが見られるのもありがたいです。

f:id:muijp:20211210185436j:plain

電車が走ってる様子がいろんな距離から見られます。まずは遠くから。

f:id:muijp:20211210185535j:plain

近くから。

f:id:muijp:20211210185628j:plain

乞田川

乞田川多摩川に流れ込む川で、多摩センター駅のあたりが起点です。

始まりはこんな感じです。謎なオブジェから流れ出しているんですね。余談ですが多摩センター近辺にはこういうかっこいい謎のオブジェがけっこうあります。

f:id:muijp:20211211100853j:plain

多摩センター駅の近くでは、緑も適度にありつつちゃんと整備されていて歩きやすいです。

f:id:muijp:20211211100926j:plain

f:id:muijp:20211211100939j:plain

下流では川幅が広くなってきます。

f:id:muijp:20211211101007j:plain

聖蹟桜ヶ丘付近で次に紹介する大栗川に合流するのですが、その合流地点もダイナミックでいい感じです。左が乞田川、右が大栗川です。

f:id:muijp:20211211101022j:plain

大栗川

大栗川は乞田川の近くを流れている川です。乞田川と比べると自然が残っている様子を楽しめます。

こんな感じで緑が多く見られます。

f:id:muijp:20211211101412j:plain

生き物も多く、鳥や魚、亀などを見ることができます。

f:id:muijp:20211211101458j:plain

f:id:muijp:20211211101508j:plain

f:id:muijp:20211211101517j:plain

と言いつつ、遊歩道が整備されている区間もあります。

f:id:muijp:20211211101632j:plain

多摩川に合流する直前のあたりもゆったりしていていいです。

f:id:muijp:20211211101725j:plain

謎の小川

大栗川が多摩川に合流するあたりでは、並行して小川が流れています。名前とかどこから来ているかとかはよくわかっていないのですが、かかっている橋や街灯などのオブジェクトがかっこいいので気に入っています。

f:id:muijp:20211211102048j:plain

f:id:muijp:20211211102103j:plain

f:id:muijp:20211211102139j:plain

程久保

聖蹟桜ヶ丘から高幡不動の方に多摩川沿いを歩いて行くと程久保川に当たります。程久保川はここまでの比較的きれいに整備された川と比べると人間のリアルを感じさせる川で、年季の入ったアパートなどが川沿いに並んでいるところなどがよいです。

これといった特徴はないのですが、歩いていると懐かしい感じがしてとても落ち着く川です。

f:id:muijp:20211211102756j:plain

f:id:muijp:20211211102837j:plain

秋にはコスモスが咲くのも注目したいポイントです。ちなみになんとなくコスモスだと判断しているんですが、花に詳しくなさすぎるのでコスモスじゃなかったらすみません。

f:id:muijp:20211211102903j:plain

多摩川に合流するあたりでは煙突も見えて調子がよいです。

f:id:muijp:20211211102938j:plain

浅川

浅川は程久保川の近くを走る川で、そこそこの広さがあります。

川幅があるだけあって空も開けているので川沿いの遊歩道からきれいに夕焼けが見えます。

f:id:muijp:20211211104027j:plain

浅川で一番気に入っている場所は万願寺歩道橋です。橋の形がよく、見るだけで心が落ち着く効果があります。

f:id:muijp:20211211104242j:plain

多摩都市モノレールと交差する地点もポイントです。ちょうどモノレールが通るところを見られるとありがたい気分になります。

f:id:muijp:20211211104725j:plain

モノレールの下から見た夕焼けです。

f:id:muijp:20211211104923j:plain

まとめ

聖蹟桜ヶ丘周辺にはよい川がいくつか流れています。家の近くにいろいろな川があるというだけで精神が安定しますね。

みなさんの街の川についてもぜひ教えてください。

あるぴんの泣き顔カット修正における木下監督の落ち着き

この記事は SHIROBAKO Advent Calendar 2021 の 5日目です。

今回 Advent Calendar のために SHIROBAKO を見直していたんですが、今まで自分の中では唐揚げや牢屋の印象しかなかった木下監督が急に気になってきました。この記事では、監督のよいところが最もよく現れているあるぴんの泣き顔のカットの修正について書こうと思います。


えくそだす4話のアフレコにて、あるぴんが泣き顔を見せるシーンの芝居を見た監督はなにかが違うと感じます。効果音や劇伴を変えてみても消えない違和感に、監督は気づいてしまいます。

山田「じゃあ何が違うんですか」

監督「絵かな...」

ここからこのカットの作業を一からやり直すという話になるのですが、この「絵かな...」という台詞、冷静に考えるとめちゃくちゃ言いづらくないですか?

えくそだすのスケジュールはこのアフレコ時点でかなり遅れていて、SHIROBAKO 2話の冒頭で本田さんが「3話目にして3日前納品なんて普通ないから」と言っています。えくそだす全体だけではなく監督個人の進捗も悪く、アフレコに入った瞬間に稲浪さんに「それでいつ上がるんですか?絵コンテ」と聞かれていることから関係者の多くが認識しているほど絵コンテが遅れていることがわかります。なんとかしてスケジュールを巻き返したい状況だと思います。

また、現状のカットの演出をした山田さんが横に座っているというのもポイントです。演出としてのキャリアがあるであろう山田さんがこれでいいと思って出しているものを否定するのは永年の仕事仲間とはいえちょっと躊躇してしまうのではないかと思います。

そんな中で作業の一からのやり直しを要求できるところに監督のすごさを感じたのですが、さらにすごいのは普通に考えると言いづらそうなこの話を監督は当たり前のように言い出しているということです。その後の会話の内容やトーンを見ても、周りが慌ててるのに対して監督だけは異常なほど落ち着いてるんですよね。

宮森「すみません、私その設定確認できてなかったです!」

監督「ああ、ごめん。これ俺の中の話」

(中略)

稲浪「で、音楽はままでいいんだよね?変える?」

監督「いやー、絵の方を変えるんで」

宮森「え、あの、それって表情修正でしょうか?」

監督「いや、演技から全部かな!」

(中略)

宮森「あの、つまり原画から全部新作ってことですか?」

監督「え、あ...うん、そういうことになるかな」

山田「まじですか監督!崩れた動画より綺麗な止め絵ですよ、そんなとこまで直してたら納品間に合いませんよ!」

監督「いや、そもそも演技があるぴんとはずれてるので」

f:id:muijp:20211205140624p:plain
いきりたっている山田さんの横で終始のんき顔

これは、監督は作品をよくするということをとにかく大切にしていて、そのためならやり直しによってスケジュールがさらに押すことや、山田さんの演出を否定することくらいは必要だと自然に考えているためでしょう。また、具体的な修正方針も自分で立て、その内容にもやる価値があるという自信を持っていることが伺えます。それらが監督の仕事だと言われるとそうかもしれませんが、自分はこういう振る舞いを当たり前のようにできるのが木下監督のよいところだと思いました。

作品をよくするための監督の姿勢は実は SHIROBAKO の他のシーンからも見てとれて、現場のメンバーとの打ち合わせではいつもとても細かく自分のやりたいことを伝えているし、えくそだす最終話の納品間際に電話で細かい撮影の指示を出している場面も印象的です。また、そもそもえくそだすの絵コンテを遅らせてしまったり、さらにそれをひっくり返したりする様子もこだわりの強さを感じさせます。


あるぴんのカットをやり直しがえくそだすに対して与えた影響については作中では描かれていません。あるぴんの芝居によって作品の評価が高まったり、ファンが増えたりしたかもしれません。一方で、やり直し前のカットに関わっていたメンバーのモチベーションを下げたり、スケジュールに無理が出てその後の話数のクオリティが落ちた可能性もあるのではないかと思います。全体としてやるべきだったとかやるべきでなかったと言えるような簡単な話ではないと思いますが、木下監督はやる側の人間だということでしょう。個人的には、自分がスタッフで大変な目にあっても、本田さんから「監督、V編で泣いてたってさ。あるぴんのカット」と伝えられ、実際に修正後のカットの素晴らしさを見たら、やってよかったし監督はやっぱりすごいなと思えるのではないかという気がします。

Heart of Swift を読んだ

前から気になっていた Heart of Swift を読んだ。とても面白かったのでメモしておく。

heart-of-swift.github.io

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
        // ...
    }
}

上の例では、 bodyView protocol に従う何らかの具体的な型であるということを言っている。つまり、 View は制約として使われている! 比較のために View protocol を型として使うことを考えてみると var body: View {} という定義になりそうだが、このようにしてしまうと開発者が bodyView に従っているということだけ知っておけばよくて具体的な型を意識しなくてよいという点で 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 を使って要所でログを出すようにしました。以下のビデオにログの概念、出力の方法、参照の方法などとりあえず必要なこと全てがまとまってます。

developer.apple.com

deeplink 対応

続いて widget をタップしたときの DeepLink の対応です。今回 widget 対応したのが SwiftUI ライフサイクルのアプリだったので、以下のように関連するいくつか View で onOpenURL を使って対処しました。

www.donnywals.com

UIKit だと DeepLink をハンドリングする1つのコンポーネントがいて、そいつが DeepLink が指示するように ViewController を重ねていくのがよくある DeepLink 対応だと思うんですが、SwiftUI では View 自身が DeepLink をハンドリングしないといけないのでかなりマインドセットが違って苦労しました。

SwiftUI でも以下の記事のように View の階層自体をグローバルな EnvironmentObject で持っていれば、その object の状態を変えることでアプリの表示を一箇所から完全にコントロールできるので UIKit と同様のハンドリングが可能になるのでそれも試してみたのですが、DeepLink に関係なくルーティング自体を追加・改修するコストが高すぎてつらくなったのでやめました。

nalexn.github.io

keychain access group の設定

ログインさせるアプリの widget では、アプリ本体と widget でクレデンシャルを共有する必要があると思います。そのために keychain access group を設定しました。以下のドキュメントを見れば基本的なことはわかります。なんとなく難しそうなイメージがあったけど単に entitlements を追加してグループ名を決めるだけだった。

developer.apple.com

細かい点で以下の記事に助けられました。

dev.classmethod.jp

widget 追加

widget を追加します。以下の一連の WWDC セッションを見れば何をやるかはだいたいわかります。

developer.apple.com

developer.apple.com

developer.apple.com

レイアウトでは普通に SwiftUI を書けばいいだけなので一瞬なんですが、いくつかはまりどころがありました。

まず、画像を非同期で読み込めないので以下の記事のように Data(contentsOf: url) を使う必要があります。

note.com

また、widget と アプリから共有の embedded framework を参照することになったのですが contains disallowed nested bundles というエラーになってしまいました。Build Phases に以下のスクリプトを追加することで回避できましたが、正直なんで直ったのかは雰囲気でしか理解してません。iOS のこの辺の話難しいんですよね...ちゃんとキャッチアップしたい。

stackoverflow.com

まとめ

widget 対応は意外と簡単なところも意外と大変なところもある。人生と同じですね(?)

ミーティング中かどうかを自動で妻に伝える

これは Kyash Advent Calendar 2020 12日目の記事です。今日は箸休め回です。

リモートワークをしていて急にミーティングが入ることがありますよね。自分は妻と一緒に暮らしているので、ミーティングが始まる前にその旨を伝えておかないと、妻がいきなりビデオチャットに登場してしまうリスクや、ミーティング中にいつの間にかご飯の時間を過ぎているなどで家庭内の雰囲気が引き締まるリスクがあります。これまでは都度口頭で伝えていたのですが、伝え忘れてしまうことやタイミングが悪くて伝えられないことがあるので自動で伝わってほしいなと感じ、ちょっと思いついた方法を試してみたら1時間くらいでできたので書き残しておきます。環境はMacです。

まず、伝達手段を決めます。最初、ミーティングが始まったらなんらかの方法でLEDを光らせたり音を出したりして伝えるか...?と思っていたのですが、冷静に考えると家庭内slackがあるのでslackで連絡すればいいということに気付きました。Incoming Webhookを使って適当なチャンネルにミーティング開始時/終了時にそれぞれ投稿することにします。Incoming Webhookについてはネット上にいくらでも情報がありますが、とりあえず以下のドキュメントに従っておけば大丈夫です。

api.slack.com

続いて、ミーティングの開始/終了を検知する方法を考えます。まずミーティング中かどうかですが、基本的にchromeを使ってgoogle meetでミーティングをすることが多いので *1chromeの開いているタブをすべて見て、その中にmeetが存在したらミーティング中と見なすのが良さそうです。調べてみたところ、これには chrome-cli というコマンドラインツールが使えそうでした。使い方はREADMEに載っていますが、とりあえず chrome-cli list tabs で開いているタブのタイトルが列挙されるのでその中に Meet を含むタイトルが存在するかでミーティング中かを判定できます。

github.com

それでミーティング中かどうかはわかるとして、ミーティング開始/終了を検知するためには、「ミーティング中かどうか」の状態が切り替わったことを知る必要があります。それには、「ミーティング中かどうか」を調べるスクリプトを定期実行して、

  • 前回実行時にミーティング中でなく、今回はミーティング中だったらミーティングが開始した
  • 前回実行時にミーティング中で、今回はミーティング中でなかったらミーティングが終了した

と考えれば大丈夫そうです。これを実現するためには前回のスクリプト実行時の結果を覚えている必要があります。いくつか方法が考えられますが、スクリプト実行時にミーティング中だった場合は決まった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秒間隔でスクリプトを実行しています。

shikiyura.com

エンジニア間でのちょっとした相談では突発的に短いミーティングが発生することがあると思いますが、そんなときもただミーティングをしているだけで自動的に家庭内slackに以下のような投稿が飛び、平和が維持されています。

f:id:muijp:20201209095814p:plain

*1:2020年11月くらいまで。現在のKyashではzoomへの移行が進んでおり、この記事の方法は引退が近づいています