数か月前、DIALØGUEのiOSアプリを、Web製品の移植としてリリースしました。そのあと、ほぼ全部をネイティブアプリとして作り直しました。
今はApp Storeで公開中です。気になる方はぜひ触ってみてください。この先は、何が変わったのか、そしてなぜ変えたのか、という話です。
言葉のあやみたいに聞こえるかもしれません。でも2つのバージョンを手に持って比べると、はっきり違いがわかります。最初のバージョンは、スマホのサイズに縮めただけのWebアプリでした。同じタブ、同じ作成ウィザード、同じダッシュボード。コンパイルも通るし、動くし、審査も通った。それでも、Webサイトがアプリの着ぐるみを着ているような感じだったんです。
スマホのサイズに縮めただけのWebアプリは、結局まだWebアプリです。ネイティブとは、Webのレイアウトの上に塗ったペンキの一層ではありません。デバイスとの、まったく別の「契約」です。 その契約こそ、僕が飛ばしてしまっていた部分でした。そして、その契約を守るために作り直したことで、タブも、音声も、聴く画面も、オフラインの挙動も、アプリの起動の仕方も、すべて変わりました。それが具体的にどういうことだったのか、画面ごとに見ていきます。
移植版は「ダッシュボード」だった
最初のバージョンは、アプリの仕事は「ジェネレーターを露出させること」だと暗黙のうちに前提していました。DIALØGUEはAIでポッドキャストを作るので、こう考えたわけです。Webの画面を全部のせれば完成だろう、と。
そこから出てくるのは、ダッシュボードです。5つのタブ、何段階もあるウィザード、あらゆるもののためのパネル。ポッドキャストアプリを開く人は、ダッシュボードを操作したいわけではありません。アイデアを音声に変えたい、自分が作ったものを聴きたい、前にやった番組をまた始めたい。そのために開くんです。移植したインターフェースが守っていたのは、Webアプリのレイアウトでした。その3つの仕事は守っていなかった。
だから作り直しは、最初に目に入るものから始めました。
3つのタブにした。スマホはタブの多さを罰するから
古いアプリは5つのタブで開きました。Library、Studio、Create、Credits、Profile。Webアプリを移植するとこうなります。Webの画面の一つひとつが、それぞれタブを獲得してしまう。
作り直したアプリは3つです。Listen、Create、You。
2つのタブは生き残りませんでした。Creditsは「行き先」であることをやめました。残高を眺めるためにアプリを開く人なんていません。Creditsは「You」の中に移し、本当に重要な瞬間——生成しようとしたまさにその時に残高が足りない——には、その場で購入シートが出るようにしました。Studioは「場所」であることをやめました。それはSeriesになりました。保存した設定(ホスト、トーン、フォーマット、言語、ソースのパターン)で、Createの中に住み、Listenではコレクションとして現れます。コントロールルームのふりをしたタブではなく。LibraryとProfileは、もっと素朴な「Listen」と「You」になりました。
デスクトップでは、タブが1つ増えてもタダです。スマホでは、タブの1つひとつが注意力への課税です。作り直しは、その税金を返済したわけです。
iOSが期待するとおりの音声
「ネイティブは契約だ」という話が、抽象論じゃなくなるのがここです。
移植版だって、音声に無知だったわけではありません。ロック画面に再生・一時停止・スキップは出ていたし、電話がかかってくれば止まったし、イヤホンを抜けば停止しました。これらは最低限の作法で、ちゃんとありました。
できなかったのは、画面がロックされたあとに「本物の音声アプリ」として振る舞うことです。ロック画面から再生位置をドラッグできない。カバーアートもない——テキストだけ。AirPlayボタンも、スリープタイマーもない。スキップは好みに関係なく15秒固定でした。
作り直しは、その隙間をプラットフォームを「迂回して」ではなく、プラットフォームと「一緒に」埋めました。ロック画面は今、エピソードごとに変わるアートワークと、好きな位置までドラッグできるスクラバーを持っています。AirPlayがあり、音をぶつ切りにせず徐々にフェードアウトさせるスリープタイマーがあり、10秒から60秒まで自分で設定できるスキップ間隔があります——その値はロック画面のボタンも動かします。システムのコントロールはアプリと一致するべきだからです。音声セッションは自分を「spoken audio(話し声)」として宣言するので、OSはそれを音楽ではなく話し声として扱います。
どれも華やかな機能ではありません。それが要点なんです。スマホでは、ロック画面とAirPlayを無視する音声は「ミニマル」ではない。人が実際に聴いているまさにその瞬間——歩きながら、運転しながら、スマホをポケットに入れたまま——に壊れている、ということです。
AIポッドキャストにしか作れない文字起こし
ここが、ただのiOSの作法ではなく、本当に僕たちならではの部分です。
エピソードを再生している間、アプリは同期した文字起こしを表示します。今読まれている行がハイライトされ、それが中央に来るよう自動でスクロールし、どの行をタップしてもその瞬間へジャンプできます。普通のポッドキャストアプリには、これは本当の意味ではできません。何が、いつ話されたかを知らないからです。DIALØGUEは知っています——スクリプトを生成したのは自分なので、エピソードの構造を最初から把握しているんです。
正直な技術的ディテールとしては、行をタップできるのは、音声にセグメントごとの正確なタイミングがあるときだけです。タイミングが大まかなときは、行は表示されますが、シーク(その位置へ飛ぶこと)はできません。そして、できるふりはしません。同じセグメントのタイミングが、スクラバーの上にチャプターの目盛りをそのまま描きます。だから、エピソードを音声として聴きながらも、システムが作ったものの「地図」を見ながらざっと流すことができます。スクリプトは2人のホストの対話として書かれているので、文字起こしはどの行をどちらのホストが言ったのかも表示します。
これが、プレイヤーに文字起こしを後付けで「ねじ込む」ことと、生成されたスクリプトを聴く体験全体の「真実の源(source of truth)」として扱うことの違いです。
これを成り立たせているものが2つあって、どちらも画面には出てきません。バックエンドはすべてのセグメントに開始時刻と終了時刻のスタンプを打ち、そのタイミングが正確なのか推定にすぎないのかを記録します——だからアプリは、正確なタイミングの行はタップさせ、推定の行ではそっと「ごまかさない」という判断ができます。そしてあなたの再生位置はデバイスだけでなくサーバーにも保存されるので、「続きから」がちゃんと効きます。Webでエピソードを始めて、スマホで聴き終える、といったときも。
本物の通勤を生き延びるオフライン
アプリは最初からエピソードをダウンロードできました。これは新しいことではないし、そこは正確に言いたいところです。作り直しが足したのは「しぶとさ」です——ダウンロード機能と、信頼できるオフラインとの違いです。
中断したダウンロードは今、最初からやり直すのではなく、止まったところから再開します。システムのレジューム用データをエピソードごとに保存して、そこから再起動するからです。Wi-Fiのみのオプションは本当にセルラーを禁止するので、キューに入ったダウンロードは、こっそりデータ通信量を食いつぶす代わりにWi-Fiを待ちます。ダウンロードはネットワークに殺到する代わりに、FIFOのキューで一度に最大3つまで。端末に何があるか見て削除できるストレージ画面もあります。そして不安定な接続で失敗する一時的なfetchは、バックオフ付きでリトライします——3回まで、0.5秒から上限まで伸びていき、ユーザーがキャンセルしたものは決してリトライしません。
リトライは簡単です。難しいのは、キャンセルを押したユーザーと喧嘩せずにリトライすること。そこが、ぐるぐる回り続ける代わりに、ひどい地下鉄の電波でもオフラインを「ちゃんとしている」と感じさせる部分なんです。
一瞬で。スピナーは「壊れている」と読まれるから
スマホでは、レイテンシは数字ではなく「感覚」です。コールドスタートのスピナーは、何も問題がなくても「このアプリは壊れている」と読まれます。
移植版はコールドスタートのたびにカバー画像を全部ダウンロードし直していたので、ライブラリはスピナーの壁として開きました。作り直しは、カバーアート用にメモリとディスクの共有キャッシュを足しました——ディスク層は再起動を生き延び、メモリ層はスクロールを滑らかに保ち、ロック画面はアートワークに同じキャッシュを再利用します。エピソードを開き直すのは以前はネットワークの往復を待っていましたが、今はセグメントと文字起こしがエピソードごとにキャッシュされていて、すぐに表示され、そのあと裏でそっとリフレッシュされます。0.5秒ごとの再生tickもリストの行から引きはがしました。タイマーがライブラリ全体を再描画させ続けるのをやめさせるためです。
これはスクリーンショットを撮れる機能ではありません。アプリとWebサイトの「あいだの隙間」です。
エピソードができたら通知してくれる
ポッドキャストの生成には、数秒ではなく数分かかります——リサーチがあり、アウトラインがあり、スクリプトがあり、それから音声。移植版は、その間ずっとプログレスバーを見せ続けました。ネイティブアプリはそうしません。
あなたの許可があれば、エピソードができた瞬間にプッシュ通知を送ります。だからスマホをロックして、別のことをして、震えたら戻ってくればいい。デバイストークンはサーバー側に保存され、通知ジョブだけが読めて、設定で全部オフにできます。テーブルが1つ、ワーカーが1つ、Appleのプッシュサービス——小さな配管です。でもそれは、製品の体感の形を「この画面で待つ」から「できたら知らせます」へ変えます。
ワークフローはアプリの外からも始められる
ネイティブアプリは、自分のウィンドウの中だけに住むわけではありません。作り直しはApp Intentsを通じてSiriとショートカットのサポートを足しました。だから「ポッドキャストを作る」「続きを聴く」「自分のポッドキャストを開く」が、話しかける言葉として、ショートカットアプリの中で、そしてSpotlightで動きます——特別なエンタイトルメントは不要です。「続きを聴く」が何をすべきか(今のエピソードを続けるのか、何も読み込まれていなければライブラリを開くのか)という判断は、それ単体で単体テストできる小さな純粋関数にしました。こういうのが、Siriの挙動がいつの間にかずれていくのを防ぎます。
それから、初回起動時に控えめな3ページのウェルカム(create、voices、どこでも聴ける)が一度だけ出ます。そしてディープリンクのおかげで、タップされたリンクはホームタブに放り出す代わりに、正しい画面を開きます。小さなことです。でもそれが、スマホの上に「載っているだけ」のアプリと、そのスマホに「属している」アプリの違いなんです。
どこで止めたかについて一言。ホーム画面ウィジェットも、Live Activitiesも、CarPlayも、まだありません——それぞれ専用のextensionか、Appleが付与するエンタイトルメントが必要で、僕は聴く体験のコアを先にリリースすることを選びました。ここでの「ネイティブ」は方向であって、終わったチェックリストではありません。
ここから僕が持ち帰る教訓
何かを新しいプラットフォームに移植しているとき、誘惑されるのは、そこで動くようにして「完了」と呼ぶことです。動くでしょう。でも同時に、「借り物」みたいに感じられるはずです。
ネイティブとは、デバイスとの契約です。ロック画面を、音声出力の切り替わりを、オフラインという現実を、人がもう使っているシステムの画面を、尊重すること。移植はあなたの古いレイアウトを尊重します。ネイティブアプリはプラットフォームの作法を尊重します——たとえそれが、すでにリリースしたタブを消し、画面を書き直すことを意味しても。
派手に振り回せるインストール数や継続率の数字はありません——今のバージョンはApp Storeで公開中で、それが正直な現状です。本当のテストは、スクリーンショットがネイティブに見えるかどうかでは決してありませんでした。誰かがエピソードを作り、スマホをロックしたまま散歩で聴き、もう1本作りに戻ってくるかどうか。それがテストです。
モバイル向けに作っている人に聞いてみたいです。あなたはどこに線を引きますか。「スマホで動く」で十分なのはいつで、プラットフォームの契約が作り直しを迫るのはいつでしょう?
よくある質問
DIALØGUEのiOS作り直しで何が変わったのですか?
アプリはWeb製品の移植から、ネイティブの作り直しになりました。情報設計は5つのタブから3つ(Listen、Create、You)へ減りました。聴く体験には、タップでシークできる同期文字起こし、チャプターマーカー、ロック画面でのスクラブとアートワーク、AirPlay、スリープタイマー、設定可能なスキップ間隔が加わりました。オフラインのダウンロードはしぶとくなり、アプリはカバーアートとセグメントをキャッシュして一瞬で表示し、Siri/ショートカットでコアの操作をアプリの外から始められ、エピソードができるとプッシュ通知が知らせます。
なぜ5つのタブが3つになったのですか?
スマホが、タブが1つ増えるごとに罰を与えるからです。Creditsは誰も行きたい場所ではなかったので、「You」と、実際に残高が少なくなったときに出る購入シートにたたみ込みました。StudioはSeriesになりました——独立したタブではなく、Createの中に保存された設定です。LibraryとProfileは、もっと素朴な「Listen」と「You」になりました。
作り直しで加わったネイティブiOS機能は?
ロック画面でのスクラブと動的なアートワーク、AirPlay、音量フェード付きのスリープタイマー、ロック画面のコントロールも動かす設定可能なスキップ間隔(10〜60秒)、スクラバー上のチャプター目盛り、タップでシークできる同期文字起こし、しぶといオフラインダウンロード(再開、Wi-Fiのみ、ストレージ管理、上限付きキュー)、一瞬で表示するためのカバーアートとセグメントの共有キャッシュ、Siri/ショートカットのインテント、「エピソード完成」のプッシュ通知、Webとスマホをまたいで続きから再生できるサーバー側の再生位置、ディープリンク、そして初回起動オンボーディングです。
同期文字起こしはどのエピソードでも動きますか?
文字起こしの行をタップしてシークできるのは、音声にセグメントごとの正確なタイミングがあるときだけです。タイミングが大まかなときは、行は表示されますがシークはできず、できるふりもしません。文字起こしが可能なのは、DIALØGUEがスクリプトを生成したからで、だから構造も、誰が何を言ったかも知っています。
StudioとSeriesの違いは何ですか?
Studioは、つまみだらけのコントロールルームを連想させました。Seriesは保存された設定にすぎません——ホスト、トーン、フォーマット、言語、ソースのパターン——で、全部を設定し直さずに次のエピソードを始められます。独立したタブではなく、Createの中のプリセットです。
NotebookLMのオーディオ概要とはどう違うのですか?
NotebookLMは、ソースを手早い音声概要に変えるのに本当に役立ちますし、無料です。DIALØGUEは、生成の上に「ネイティブで完結した聴く製品」になろうとしています。音声を作る前のアウトラインとスクリプトのレビュー、音声の選択、タップでシークできる同期文字起こし、チャプター、ロック画面とAirPlayのコントロール、オフラインダウンロード、Siri、そして繰り返し作る番組のためのSeries。正直な違いは「どちらが良い音声を生成するか」よりも、「エピソードが存在したあと、それに何が起きるか」のほうにあります。
作り直しでオフライン再生が加わったのですか?
オフラインのダウンロードはすでにありました。作り直しはそれをしぶとくしました。中断したダウンロードは最初からではなく再開し、Wi-Fiのみのオプションとストレージ画面があり、ダウンロードは一度に最大3つまで、そして一時的なネットワークの失敗は、キャンセルしたユーザーと喧嘩せずにバックオフ付きでリトライします。
僕からは、今のところこんなところです。
それでは。 Chandler


