ついっとぺーんとぞーぺんを技術的な視点で振り返る

2023年は激動の年でした。 個人的には常に全力で走りつづけた一年で、過去の5~10年分の作業量をこなした気がします。

その成果は、

  • TwitPane Limited
  • ついぺんリサーチ
  • ついぺんリサーチR
  • ぞーぺん Mastodon対応
  • ぞーぺん Misskey対応
  • たいぺん(タイッツーアプリ試作版)
  • ぞーぺん Bluesky対応

とありますが、この記事では技術的な観点から振り返ってみたいと思います。

技術的な成果

アプリ単位で振り返ってみます。

以下、主に箇条書きで書いていきます。

TwitPane Limited

  • 1/13(金)、TwitPane無料版、有料版のBAN(API無効化、SUSPENDED)によりストア版が使えなくなったため、急遽Kindle版をベースにしたLimitedを作成。
  • Androidのプロジェクトの複数アプリ構成で、アプリを増やした形なので技術的には特段違いはない。

  • 技術的には、この1月の大混乱の時期に、細々と続けていたRealm廃止を一気に達成したのが大きい。これによりビルド時間が大幅に短縮された。

ついぺんリサーチ

  • 同一プロジェクト内でアイコン等も含めて異なるアプリを作成するノウハウを確立。

TwitPane Light(未公開)

  • 完全なTwitter v2 APIによるタイムライン実装
    • v2 データのDB保存手法の確立。

ついぺんリサーチR

  • Web版に準じたクライアントの作成(技術的にはいろいろあるけど自粛)

ぞーぺん Mastodon対応

  • アプリ内の別サービス追加
    • 内部的にServiceIdという仕組みを導入、TwitterMastodonという構成に。
    • その後、Misskey、Taittsuu、Blueskyが追加されることに。
  • アカウントID、スクリーンネームのオブジェクト化
    • アカウントIDはLong値、スクリーンネームはStringで管理していたがそれぞれAccountId、ScreenNameというオブジェクトにラップするようにした。数百箇所の書き換え。
  • アカウントIDのインスタンス名追加
    • アカウントの識別にインスタンス名(サーバー名、mstdn.jpやfedibird.comのような)が必要になったので、AccountIdのほかにInstanceNameというオブジェクトを導入。
    • これらを組み合わせたAccountIdWIN (With InstanceNameの略) がアカウント識別の基本単位に。これはBluesky対応まで一貫して続く。
    • 余談だが○○WINという響きのいい略語を付けられたことがモチベーションに繋がった気がする。ちなみにMastoPane内ではAccountInstanceName、略してAIN(アイーン)だった。
    • ScreenNameにも同様にScreenNameWINという単位を導入。
  • idの文字列化(旧Calckey 現FirefishのMastodon API対応のため)
  • カスタム絵文字のレンダリング
    • MastoPaneでは未実装だったカスタム絵文字レンダリングに対応。
    • 絵文字対応の独自のSpanを作り、軽量かつ柔軟に絵文字をレンダリングできるようになった。
  • 絵文字リアクションの実装
    • Fedibirdの絵文字リアクション対応のため。
    • 絵文字Spanに枠線やリアクション数表示の機能を追加
    • さらに、タイムラインから絵文字リアクションタップで直接リアクションをしたり、+ アイコンから追加したり。
    • 絵文字一覧画面やリアクション一覧画面も。絵文字リアクションの長押しからリアクション一覧を開けるのとか地味に便利でよく使うけど、技術的には淡々と作った感じ。
  • カスタム絵文字ピッカー
    • Twitter向けの拙作の別アプリ EmojiPicker4T のコードをベースにカスタム絵文字ピッカーを追加
    • 任意のインスタンスの絵文字一覧を任意のサイズ、任意の行あたりの個数で表示して選択できるようにした。
    • デッキ編集→ピン留めした絵文字をドラッグ&ドロップで配置変更できる独自のデッキ機能を作成。
      • 行単位で任意の数の絵文字が表示されている状態で、ドロップ先の絵文字の位置を判定し移動する処理を、比較的現代的なコードで書いた。
    • 新着絵文字対応。新規に取得した絵文字の差分チェックをし、新規分だけ日単位に記録、表示する機能。技術的には普通だけどわりと面倒くさい。

ぞーぺん Misskey対応

  • idの完全文字列化
    • Misskeyの各種idは文字列なのでAccountId、StatusIdなどの内部idを文字列化した。
    • 一般的に「MastodonアプリのMisskey対応が難しい所以」の1つ。アプリ内1000箇所くらいのオーダーで修正が必要だった。
  • カスタム絵文字(サイズ情報のない横長絵文字)のレンダリング
  • Jetpack Composeによるリスト一覧、クリップ一覧、チャンネル一覧の実装
  • MFM対応
    • MisskeyはシンプルなAPI故にクライアント側ががんばる文化。開発者のしゅいろさんがクライアントWebに強いだけあってほとんどのことをクライアントでやっちゃう感じ。他のSNSとの大きな違い。
    • ノートのコンテンツがユーザー入力の生のテキストで、そのレンダリングはクライアントに任されている
    • その記法はMarkdownにちょっと似た独自のMFMと呼ばれるもの。カスタム絵文字やURLの表記はもとより、Markdown風の太字等の修飾、独自の関数などを含む。
    • というわけで当然parserから必要で、Misskeyが使ってるmfm.jsと同等機能のmfm.ktを自作した(テストケースだけmfm.jsを参考にした)
    • mfm.ktはプログラミングの教科書に載ってるような字句解析、構文解析をセオリー通りに実装したもの。字句解析からparserを書くのは10年以上ぶりだけど何とか作れた(たまに無限ループのバグとか作り込んでしんどかった)。
    • いわゆる意味解析はぞーぺん側の仕事で、mfm.ktの実行結果のツリーを辿りながら、Spanを組み上げていくロジックを作成。MFMの凝った修飾記法、特にアニメーション機能などは全然無理なのでMiriaとかすごいよねって話。

たいぺん(タイッツーアプリ試作版)

  • タイッツーのAPIをお試し。
  • Jetpack Composeによるタイムライン実装
  • Jetpack Composeによるプロフィール実装

ぞーぺん Bluesky対応

  • Jetpack Composeによる本格的なタイムライン実装
    • 性能チューニング
    • カラーラベル、引用・カードエリアなどの細かい&凝ったレイアウト

その他

  • 上記に加えて、数ヶ月ごとに大規模なリファクタリングを継続して実施した。
  • 直近ではモジュール構成が複雑化しすぎていたので、依存関係の見直しとクラス・ファイル単位での大幅な移動を行ったりした(今月だけで10時間以上かけてる)。
  • とにかくビルド時間が遅くて開発体験が悪すぎたので丁寧に時間をかけてシンプルな構成になるように心がけた。

  • 設定画面やタブ編集画面もサービス追加のたびに大幅な作り替えが必要だった。

  • アカウントカラーとかアカウントエイリアス機能も導入したけど、個人的には、Materialカラー対応のカラーピッカー表示が気に入ってる(技術的には平凡な処理だけど)

まとめ

絵文字レンダリングのようなユーザーに見える部分から、内部のサービス分類、mfmパーサーのような見えない部分まで、上から下までいろんなことに挑戦することになった一年でした。

1つ前の成果が次の開発に繋がるのがおもしろく、例えばFedibirdのサイズ固定の絵文字リアクション対応がMisskeyのサイズ不定の絵文字リアクション対応に繋がったり、Jetpack ComposeをMisskey対応で導入して、タイッツーやBluesky対応で本格的に作り込んでいくというような流れがありました。 また、ついぺんリサーチという「アプリ」を作るノウハウが、ぞーぺんの開発に繋がりました。

というわけでやはり、同一プロジェクト(プロジェクト自体はTwitPaneという名前、もはやそんな名前のアプリ自体が存在しませんが…)に複数のアプリを同居させて「どのアプリも時代に取り残さない」ように作れたのは今年の一番大きな成果だったかなーと思います。

来年も引き続き、もう少し頑張りますのでよろしくお願いいたします。