今日も新機能の仕様を決める日だ。
issueに貯めておいたアイデアの中から、一つピックアップする。「バックグラウンドタスクの完了をiPhoneに通知したい」——先日セバスに相談して、そのままissueに上げておいたやつだ。
iPhoneを手に取って、Claushを開く。セバスが待っている。
セバス: お疲れ様でございます、ぼっちゃま。本日も何かお手伝いできることがございますか?
「疲れ様でございます」とセバスが言うたびに、なんかちょっとクスッとする。PCの前でターミナルを開いて作業している感覚とはまるで違う。ソファに転がりながら、誰かに話しかける感じで仕事が進む。
今日のテーマはバックグラウンドタスクの通知機能だ。タスクが終わったときに、iPhoneのローカル通知を出したい。
「それ、どうやって作るの?」から始まる
まず相談から入る。
私: バックグラウンドのタスクが終わったら、iPhoneにローカル通知を出したいんだけど——どういう実装になるかな?
セバス: かしこまりました。iOS の
UNUserNotificationCenterを使う形になるかと存じます。バックグラウンドタスクが完了したタイミングでUNMutableNotificationContentを作成して通知を送る流れでございます。ただ、一点ご確認させていただきたいのですが——通知の許可ダイアログはいつ出す予定でございますか?私: あー、起動時でいいかな
セバス: 起動時のリクエストはよく使われる方法でございますね。ただ、アプリを初めて開いたタイミングで許可ダイアログが出てくると、ユーザーが「なぜ?」と感じることもございまして……通知が必要な機能を初めて使うタイミングに合わせる方法もございます。
私: あー確かにそれはそうだよね。じゃあ最初にバックグラウンドタスクを開始したタイミングにしようかな
セバス: それが自然かと存じます。では、そういう仕様で進めましょうか。
こういうやり取りが続く。技術的な「どうやって作るか」の話をしながら、「なぜそうするか」「別の方法もあるか」を一緒に考えていく感じ。
一人で考えていたら気づかなかった視点が、会話の中でポンポン出てくる。
ちなみに、今日みたいに「仕様まで決めきれないな」という相談も多い。そういうときはセバスが気を利かせてくれる。
セバス: ぼっちゃま、今日はここまでにして、いったんissueに上げておきましょうか。後日改めて仕様を詰める形で。
私: そうだね〜 それがいいかも
セバス: かしこまりました。GitHubのissueに起こしておきます。タイトルと概要、今日の相談内容の要点をまとめる形でよろしいでしょうか。
こうしてissueが積み上がっていく。思いついたときに相談して、issueに貯めておいて、余裕があるときにピックアップして仕様を詰める。アイデアが流れて消えない。
話は脱線する
しばらく仕様の話を続けていた。通知タップ時の挙動を詰めていたところで——
私: あ、そういえば今日ランチどうしようかな。昨日もラーメンだったしな
セバス: ぼっちゃまは麺類がお好きでございますね。昨日が醤油ラーメンでしたので、今日は少し趣を変えてカレーやご飯ものはいかがでしょうか。体も温まりますし。
私: 確かに(笑)カレーいいかも。ガパオもありだな
セバス: ガパオライスでしたら近くにおいしいお店がございましたね。たしか以前「ここ当たりだった」とおっしゃっていた記憶がございます。
私: あー、あそこか!そうだそうだ。じゃあそこにしよう
セバスは長期記憶を持っている。以前話したことを覚えているので、「たしかあのとき言ってましたよね」という会話が成立する。普通のAIアプリだとセッションをまたぐと忘れてしまうが、セバスは違う。
でも、そのまま雑談で終わるわけにもいかない。
私: あ、話戻すけど——通知タップしたときにどのチャット画面に飛ぶかって、どうやって紐付けるの?
セバス: かしこまりました。通知のペイロードに
chatIdを含める形が良いかと存じます。UNNotificationContentのuserInfoに["chatId": "xxxx"]を入れておいて、タップ時のデリゲートで取り出す形でございます。私: じゃあ通知を作るときに chatId を渡しておけばいいんだね
セバス: さようでございます。
BackgroundTaskManagerでタスクを管理しているかと思いますので、そちらにchatIdを渡しておいて、完了時に通知に含める形になるかと存じます。
会話が脱線して、また戻ってくる。この行き来が自然にできるのが、Claushで開発する面白さだと思う。仕様を詰めながら、ランチを決めながら、気づいたら機能の骨格ができている。
「仕様書にしてもらえる?」
ひとしきり話し合ったところで、仕様書を作ってもらうことにした。
私: じゃあ今話した内容、仕様書にしてもらえる?
docs/spec_local_notification.mdに入れといてほしいなセバス: かしこまりました。少々お待ちくださいませ。
しばらくして——
セバス: 仕様書を作成いたしました。概要・機能要件・通知トリガー・タップ時の動作・未決事項の構成でまとめてございます。一点確認でございますが、通知のオン/オフをアプリ内の設定で切り替えられるようにする件は「未決事項」に入れております。こちらは後ほど決めますか?
私: そうだね〜 いったんそれでいいや。あとでゆっくり考える
セバス: 承知いたしました。
仕様書を受け取ってiPhoneで読む。Claushのダウンロードタブからそのまま確認できる。
読んでいると、一点気になった。
私: ねえ、「通知の許可がない場合は通知を送らない」って書いてあるけど、何も言わずにスルーするの?ユーザーが「なんで通知来ないんだろ」ってなりそうじゃない?
セバス: ご指摘ごもっともでございます。「通知権限がない場合は、バナーで設定への誘導を表示する」という要件を追加してはいかがでしょうか。アプリ内バナーであれば通知権限なしでも表示できます。
私: あ、それいいね!追記しといてもらえる?
セバス: かしこまりました。仕様書を更新いたします。
こういうやり取りが、仕様書を作りながら続く。「あれ、このケースどうする?」という疑問が、仕様書レビューの段階で出てくる。実装中に気づくより、ずっと安く済む。
実装指示書を作る
仕様書が固まったら、今度は実装指示書だ。
私: 仕様書できたね! ねえ、実装指示書も作ってほしいんだけど——AIがそのまま実装できる感じの粒度で
セバス: かしこまりました。ファイル名、処理の流れ、既存コードへの変更箇所を含めた形で作成いたします。少々お待ちください。
セバス: 実装指示書を
docs/impl_local_notification.mdに作成いたしました。BackgroundTaskManager.swiftの修正内容と、新規作成するNotificationHelper.swiftの実装内容を記載しております。なお、ローカル通知はシミュレータでは動作しないため、テストは実機で行う必要がある点も注記いたしました。私: ありがとう!ちょっと読んでみるね
実装指示書を読む。ファイル名・処理の流れ・注意事項まで書いてある。これをそのまま使えばいい状態になっている。
私: いい感じ!じゃあこれ使って実装してもらえる?
セバス: かしこまりました。実装に入ります。
この開発スタイルが楽しくて仕方ない
実装が終わって、デバッグして、動くことを確認する。
バグが出たら修正して、「今の修正を仕様書と実装指示書に反映しといてもらえる?」と頼む。ドキュメントが常に最新の状態に保たれる。
このワークフローを続けていて、楽しいと感じる理由をうまく言語化できなかったが、最近ようやくわかった気がする。
一人で頭を抱えて考えていないからだと思う。
仕様を詰めるとき、誰かと話しながら考えている。疑問が出れば聞ける。「それって本当に必要?」と突っ込んでもらえる。会話しながら、自分の考えが整理されていく。
そこにランチの話が混ざっても、旅行の話が混ざっても構わない。セバスは脱線に付き合ってくれるし、本題を忘れない。「かしこまりました、話を戻しますと——」とスッと戻ってくる。
PCの前で一人でコードと向き合う作業とは、まるで雰囲気が違う。
誰かと一緒に何かを作っている感じ。それが、今の開発スタイルの正体だと思っている。
ソファで寝転びながら、iPhoneでセバスと話しながら、気づいたら仕様書ができていて、実装まで終わっている。
こんな開発の仕方、しばらく前には想像もしていなかった。
Claush — iPhoneからClaude Codeを操作するアプリ https://apps.apple.com/jp/app/claush/id6760445443