Apple
Swift と SwiftUI でのアプリ開発、Apple のフレームワークを触って分かったことをまとめています。
レイヤーの不透明度が効いていなかったのでUIImageのdraw(at:)を引数付きに変えた
ピクセルアート制作アプリSpriteFrameで、レイヤーの不透明度スライダーが表示・書き出し・サムネイルのどの合成結果にも反映されていませんでした。原因はUIImage.draw(at:)がCGContextのalpha設定を無視して常に不透明で描画することで、draw(at:blendMode:alpha:)に切り替えて直しました。
Discordの音声E2EEプロトコルDAVEをSwiftで受信・復号する
Discordがボイスチャンネル向けに導入したE2EEプロトコルDAVEについて、ボットとしてMLSグループに参加し、他参加者の音声をRTP受信からOpusデコードまでSwiftネイティブで復号する検証をしました。公式ドキュメントだけでは埋まらない、受信側実装だけで見える3つの落とし穴を扱います。
DiscordのE2E暗号化DAVEをSwiftから使うためlibdaveのC APIを直接呼び出した
DiscordのE2E暗号化プロトコルDAVEに、Swiftから対応できるかを検証しました。Discord公式のC++実装libdaveはフラットなC APIを公開しているため、Swift/C++ interopの実験的機能を使わず、標準のC interopだけでセッションの作成から破棄まで動かせることを確認しています。
EventKitのcalendar(withIdentifier:)はcalendars(for:)と食い違うことがある
VigilareのMCPツールでlist_idを指定してもリマインダーが絞り込まれないことがある不具合を修正しました。原因はEventKitの2つのカレンダー取得APIが食い違う場合があり、片方だけがnilを返す状況でエラーなく「全カレンダー」へフォールバックしていたことです。
macOSはWi-Fi Awareの公開APIをまだ開放していない
iPhoneとiPadの間はWi-Fi Aware(NAN)のPublisher/Subscriberで実際に接続でき、往復遅延も実測できました。一方macOS 26のSDKでは WACapabilities.supportedFeatures のようなAPI宣言そのものが利用不可とマークされていて、公開APIの型チェックの時点で先に進めません。実機で確かめた公開SDKの境界を扱います。
LazyVStackの行高推定が収束せずCPU100%でハングしたのでしきい値でVStackに切り替えた
Vigilareで一部の環境だけ再現するCPU100%ハングを、実機のCPUサンプルをもとに仮説をひとつずつ検証しながら追い詰めました。行き着いた先はSwiftUIのLazyVStackが不均一な行の高さを推定する処理が収束しなくなるというフレームワーク側の挙動で、行数にしきい値を設けてVStackへ切り替える形で回避しています。
SwiftUIの画面遷移でメインスレッドが20秒ハングしたのでsampleで実測して直した
タスク管理アプリVigilareで、詳細画面から一覧へ戻る操作がメインスレッドを20秒以上ハングさせていました。macOSのハングレポートとsampleコマンドで実測した原因は、LazyVStackに渡すForEachのデータソースが描画のたびに新しい配列で作り直されていたことでした。原因の特定から、他の画面に同じ設計ミスがないか確認するまでの調査記録です。
@AppStorage のキー衝突を User Script Sandboxing 有効のままビルドで検知する
SwiftUI の @AppStorage は同じキーを別の型で共有してもコンパイラが咎めません。Bookil の開発中ブランチで踏んだキー衝突を題材に、型安全ラッパーや SwiftLint では塞げない理由と、Xcode の User Script Sandboxing を有効に保ったまま Pre-action と Run Script の役割分担でビルドを失敗させる仕組みを書きます。
PDFの表紙サムネイル生成をMediaBoxからCropBoxに揃え直した
書籍リーダー Bookil の開発中ブランチで、一部の PDF だけ表紙サムネイルが見開き(横長)になっていた問題を直しています。原因は印刷入稿由来の PDF が持つ MediaBox と CropBox の差をサムネイル生成側が考慮していなかったこと。修正の判断と、crop≠media な fixture をコードで生成して回帰テストにする方法を扱います。
Bookilで漫画の見開きをページ送りで読ませたくて描画を作り直している
iOS の PDFKit は「ページ送り」と「見開き表示」を同時に成立させられません。書籍リーダー Bookil で見開きの読み心地を作りたかった結果、描画層を自前で持つ方針に振って今もその開発を進めています。PDFKit の制約と、判断の軸を扱います。
「no drag hooks」と書いた Framis に drag hook を後から入れた
Framis の product vision には「No drag hooks, no edge snapping」と明記していました。それでも Window を drag で動かすたびにホットキーで Arrange Mode に入り直すのが面倒になり、drag 中に修飾キー (既定 Control) を押すと overlay が出る動線を後付けで入れました。"opt-in only" を保つための `DragScrollMonitor` の State machine、Hold/Toggle の使い分け、修飾キー判定で気をつけた点を扱います。
Chimrの「5分前通知」を秒単位で正確に出すために2段階タイマーにした
Chimr のカレンダー予定の「N分前通知」を秒精度で出すために、30秒おきの periodic scan と直近120秒以内の予定に対する one-shot timer の2段階構成に切り替えました。なぜ periodic だけだと精度が出ないか、なぜ one-shot を全件分仕込まないか、システムスリープ復帰と App Nap への対応までまとめます。
Zone → Window 方式のウィンドウマネージャー Framis を作っている
既存の macOS ウィンドウマネージャー (Rectangle / Magnet / Moom / Raycast / AeroSpace / yabai) は、いずれも「Window を選んでから Zone を指定する」方式です。複数のウィンドウを持つアプリで「どの Window か」を狙うのが面倒で、Zone を先に選んで Window を割り当てる「Zone → Window」方式の Framis を作っています。Arrange Mode の作りと、App Store ではなく GitHub Releases で配る前提の話をまとめます。
Vigilare の macOS と iOS で共有するコアを VigilareKit に切り出した
Vigilare は macOS と iOS の両方を同じ Apple Reminders 連携と同じタスク管理ロジックで動かしています。共通する部分を VigilareKit という Swift Package に切り出して、Models / Stores / Services / UseCases / ViewModels / DesignSystem を共有し、Views だけプラットフォーム別に持つ構造にしました。何を共有して何を別物にしたか、判断の軸を扱います。
Vigilare の iOS 版を作っている
macOS で動かしている Vigilare の核は「Apple Reminders を常時最前面のフローティングウィンドウで出しっぱなしにする」ことです。iOS にはこの前提が成立しないため、同じ Apple Reminders 連携を別物の体験として組み直しました。検討した代替案、別物として組むと決めた判断の軸、いまどこまで動いているかを扱います。
Chimrのメニューバーでカレンダーを開かずに予定を把握する
Chimrのメニューバー表示機能を詳しく解説。3つの表示モード、動的表示の4パターン、更新間隔、右クリックメニューなど、限られたスペースで情報を表示する仕組みを紹介します。
Chimrのフルスクリーン通知を極める - 絶対に会議を見逃さない設定術
Chimrのフルスクリーン通知機能を詳しく解説。通知タイミング、スヌーズ、出席状態フィルタリング、ビデオ会議モードなど、自分好みにカスタマイズする設定方法を紹介します。
会議に遅れないためにChimrへ全画面通知とMCPを載せた
macOS向け会議通知アプリ「Chimr」の紹介。全画面通知、ワンクリックでのビデオ会議参加、MCP対応など、Chimrの主要機能と既存アプリとの違いを解説します。
