コンテンツへスキップ

SwiftUIアプリを2時間でvisionOSに移行した方法

SwiftUIアプリ「CrossCraft」をApple Vision Proの発売日に間に合うようにvisionOS対応した方法をご紹介します。実質約2時間で完了した移行作業の中で得た重要な学びをまとめました。

SwiftUIアプリを2時間でvisionOSに移行した方法

数か月前にCrossCraft: Custom Crosswordsというアプリをリリースしました。SwiftUIで完全に構築された、iOS、iPadOS、macOS対応のアプリです。Vision Proの発売に合わせて、新しいvisionOSプラットフォームへの移行に挑戦することにしました。ただし、移行を始めたのは発売日のわずか3日前でした!

こんなに短い時間で間に合うのかが問題でしたが、幸いなことに思ったより簡単で、発売初日にアプリを公開できました!

Appleからの公式メール。Day 1アプリ開発者への感謝状。

Appleからの公式メール。Day 1アプリ開発者への感謝状。

以下では、皆さんのアプリの移行にも役立つ学びをすべてまとめています!

サードパーティフレームワーク

プロジェクトに「Apple Vision」デスティネーションを追加したあと、まず「Apple Vision Pro」シミュレーターを選択してビルドしてみました。

Step 2: Package.swiftの調整が最も重要なステップです。

予想通りビルドは失敗しました。すべてのフレームワークがvisionOSプラットフォームに対応していたわけではないからです。ただし、基本的な対応を追加するのは簡単でした。手順は次の4ステップです:

Step 2: Package.swiftの調整が最も重要なステップです。

Step 2: Package.swiftの調整が最も重要なステップです。

  1. 依存ライブラリをフォークし、プロジェクトから元のものを削除して、mainブランチ指定でフォークを追加します。

  2. フォークのPackage.swiftを開き、ファイル先頭のSwift toolsバージョンを5.9に上げ、サポート対象のplatforms配列に.visionOS(.v1)を追加します。

  3. #if os(iOS)という記述を検索し、#if os(iOS) || os(visionOS)に変更して、macOSのコードパスではなくiOSのコードパスが使われるようにします。

  4. 「Apple Vision Pro」シミュレーターを選択し、ビルドして確認します。

APIが見つからないエラーが出る場合は、適切な箇所に#if !os(visionOS)チェックを追加してください。Appleが公式に確認しているように、visionOSはiPadOSのフォークなので、ほとんどのAPIは利用可能なはずです。もし機能がなければ、近いうちに追加されるか、そのプラットフォームでは意味をなさないものでしょう。

私の場合、自作のReviewKitライブラリだけ、visionOSではまだ利用できないSKReviewController周りのコードを無効化する必要がありました。結果的にvisionOSビルドではライブラリは何もしませんが、iOSとMacのアプリはユーザーにレビューを依頼し続けます。カスタムUIで対応することもできましたが、今年のWWDCを待つことにしました。

フォークした場合は、元のリポジトリにプルリクエストを送ることを忘れないでください。コミュニティの他の開発者もその修正を活用できます。これをする人が多ければ多いほど、自分でプラットフォーム対応を追加しなければならない依存ライブラリが減ります。

シミュレーターでのテスト

すべての依存関係を修正した後、ビルドを実行すると成功しました!

Xcodeでのアプリアイコンプレビュー。

残念ながら、まだ完了ではありませんでした。まず、アプリの起動時にプロジェクトにアイコンがあるにもかかわらず表示されていませんでした。さらに起動後すぐにいくつかの問題を発見しました。概要は次の通りです:

  • アプリアイコンが表示されない

  • カーソル(=視線)を移動すると、一部のコントロールでホバー形状がずれている

  • 多くのウィンドウ、モーダル、UI要素のレイアウトとサイズがおかしい

  • アクセントカラーがガラス風の背景に対して十分なコントラストがない

これらの問題は、visionOSに移行するすべてのアプリに影響します。ただし、小さな調整で修正できました。1つずつ説明していきます。

アプリアイコン

visionOSには独自のアプリアイコンスタイルがあることがわかりました。watchOSと同様に円形ですが、tvOSのように奥行き感を出すため複数レイヤーで構成されています。visionOSアプリアイコンを追加するには、+ボタンを押して「visionOS App Icon」を選択します。その後、最低でも「Front」と「Back」レイヤーの1024 x 1024画像を用意する必要があります。「Middle」レイヤーはオプションです。

Xcodeでのアプリアイコンプレビュー。

私の場合、アプリアイコンはもともと背景レイヤーと前景のアイコンで構成されていたので、それぞれ別に書き出すのは大した手間ではありませんでした。右ペインで「Middle」レイヤーを削除するだけで済みました。ただし、前景アイコンにシャドウを適用していたため、Human Interface Guidelinesが背景以外のレイヤーには「ソフトなエッジやぼかしのあるエッジを避ける」と述べていることに従い、シャドウを削除する必要がありました。システムがホバー時に自動的に軽いシャドウを追加してくれます。

ホバーといえば、Xcodeはアプリアイコンのプレビューを上部に表示しており、マウスオーバーするとVision Proでユーザーがアイコンを見たときの3Dホバー効果をシミュレートしてくれます。これはとても便利です!

Xcodeでのアプリアイコンプレビュー。

Xcodeでのアプリアイコンプレビュー。

ホバーエフェクト

visionOSでは、シミュレーターでは見落としがちですが実機では非常に重要なのが、適切なホバーエフェクトです。デバイス上では目で要素を選択するため、現在どの要素が選択されているかのフィードバックが重要になります。Button("Click me") { ... }のような文字列ベースのコントロールAPIでは、これは標準で問題なく動作します。

しかし、ボタンにカスタムのlabelパラメーターを指定したり、完全にカスタムのコントロールを使用している場合は、コントロールの正確な形状をシステムに伝える必要があります。たとえば、私は選択肢が2〜4個の場合にデフォルトのドロップダウンPickerの代わりに使うカスタムコントロールHPickerを使用しています。オプションにホバーした際、次のように表示されました:

カスタムHPickerビュー。ホバーエフェクトの調整なし。

カスタムHPickerビュー。ホバーエフェクトの調整なし。

オプションの形状に合わせた調整は簡単でした:

Button {
   // ...
} label: {
   Label(option.description, systemImage: option.symbolSystemName)
      // ...
      .clipShape(.rect(cornerRadius: 12.5))
      #if !os(macOS)
      .contentShape(.hoverEffect, .rect(cornerRadius: 12.5))
      .hoverEffect()
      #endif
}

HPickerコンポーネント内のボタンの簡略版。

.contentShape.hoverEffectモディファイアが、適切なホバーエフェクトのために追加した部分です。.rect(cornerRadius: 12.5)をカスタムコントロールの形状に合わせて置き換えてください。macOSでは.hoverEffectが利用できないため#if !os(macOS)チェックで囲んでいます。また、これらのモディファイアはButtonの外側ではなく、label定義の内部に配置する必要があります。

場合によっては、予期しない場所にホバーエフェクトが表示されることもあります。私の場合、すでにコントロールとして認識されているビュー内にButtonを配置したDisclosureGroupでこの問題が発生しました:

「Show Clues」行全体がボタンで、その中にさらに別のボタンがある。

「Show Clues」行全体がボタンで、その中にさらに別のボタンがある。

ホバーエフェクトを無効にするには、.hoverEffectDisabled()モディファイアを追加するだけです。上記の場合、内部のボタンはmacOS用にのみ追加していました(macOSではDisclosureGroupがラベルのクリックではトグルしないため)。そこで、macOSでのみButtonを使用し、それ以外では単純なLabelを使用するように修正しました。

すべてのコントロールに適切なホバーエフェクトを実装するのが、この移行作業で最も時間がかかった部分で、約40分かかりました。SwiftUIプレビューがプロジェクトで動作していれば、もっと早く済んだでしょう。なぜかプレビューがビルドできず、試すとMacがハングしてしまいました。

レイアウトシステム

visionOSはiPadOSがベースなのでFormビューなどはiPadと同様にレンダリングされますが、iOS/iPadOSと比較してレイアウトシステムには重要な違いがあることを理解しておく必要があります:

Apple Vision Pro内での視野。出典: HIG

Apple Vision Pro内での視野。出典: HIG

Apple Visionではアプリは無限のキャンバスで開かれ、ビューがサイズを導出できる固定の画面幅や高さがありません。これは理解しておくべき重要な違いです。macOS向けのアプリを開発したことがある方なら、この違いにはすでに馴染みがあるでしょう。多くの点で、レイアウトシステムはmacOSに近いものです。macOSでもモニターサイズは様々で、iOS/iPadOSのようにフルスクリーンでウィンドウを開くことはほとんどありません。

つまり、アプリがすでにmacOSに対応しているなら、サイジングやウィンドウ管理に関してすでに多くの#if os(macOS)分岐があるはずです。それを#if os(macOS) || os(visionOS)に置き換えるだけで対応できます。

macOSとの主な違いは、ウィンドウが大きな角丸の角を持つことです。そのため、ウィンドウのルートビューの上下に追加のパディングが必要になりました。例えば.padding(.vertical, 10)を使用しました。

macOS対応がまだの場合に役立つポイントをいくつか挙げます:

  • ビューに.frame(minWidth: 400, minHeight: 300)を指定する必要があります。指定しないと、ウィンドウやモーダルがUIに適さないサイズになる可能性があります。すべてを確認し、適切な値を設定してください。

  • minWidthminHeightを指定してユーザーがコンテンツに対して小さすぎるサイズにリサイズできないようにしつつ、WindowGroupシーンに.defaultSize(width: 800, height: 600)で最小サイズより大きなデフォルトサイズを指定することも検討してください。

  • 画面全体を覆うモーダルビューがあり、そのスペースが必要な場合は、モーダルを独立したウィンドウに移行することを検討してください。@Environment(\.openWindow) var openWindowプロパティを使用して、visionOS(およびmacOS)で新しいウィンドウを開き、追加のWindowGroupビューを指定します。詳しくはSwiftUI 4でのウィンドウ管理の記事をご覧ください。

  • 初回の移行ではモーダルをそのまま使い続けることもできます。ただし、macOSと異なり、visionOSのモーダルシートはリサイズできません。そのため、あらゆる種類の動的データに対応できるサイズを指定してください。CrossCraftの「パズルをプレイする」画面では、私はこの方法を採用しました。

カラー

白い背景のコントロールはホバーエフェクトとうまく機能しないことに注意してください。ホバーエフェクトは白いオーバーレイを使用するため、白の上に白では見えません。クロスワードパズルのゲームモードでこの問題に遭遇しました。タイルにカーソルがあるのにホバーが見えない状態です:

白い背景のボタンではホバーが見えない。

白い背景のボタンではホバーが見えない。

私の応急処置は.opacity(0.85)モディファイアを追加して白い背景を15%透明にすることでした。もっと透明にした方がいいのですが、白はクロスワードとして期待される色なので、できるだけ白に近い状態を維持しました。

iOSで読みやすい色がvisionOSでは機能しないことがある。

iOSで読みやすい色がvisionOSでは機能しないことがある。

また、システムデフォルトの「ブルー」アクセントカラーを含む多くの中間コントラストの色が、デフォルトのウィンドウガラス背景に対して非常にコントラストが悪いことに気づきました。visionOS向けにはこれらの色をより明るくしてください。Attributes inspectorでカラーを選択した状態で「Apple Vision」用のバリアントを追加できます。

Colors

HSBシステムで色を明るくするには、彩度をわずかに下げ、明度を大幅に上げる必要があります。さらに、色相を最も近い明るいRGB値に向かって少し移動させることもできます。HSBを活用した色の明暗調整について詳しくはこちらの記事をご参照ください。

まとめ

iPadOSとmacOSにすでに対応しているアプリがあれば、visionOSへの対応は非常にやりやすい立場にいます。SwiftUIのコードはすべて再利用できます。ウィンドウ管理に関してはmacOS版のコードを、それ以外はiPadOS版のコードを採用してください。そのうえで、すべてのカスタムコントロールに適切なホバーエフェクトがあることを確認し、パディングの追加、色の明るさ調整、アプリアイコンのフロント・バックへの分割などのレイアウト・UI調整を行います。

この全プロセスは実質2時間で完了しました。作業全体をライブ配信し、ビルド待ちやチャットの部分を除いた録画を以下の2つのYouTube動画で公開しています。それぞれ約1時間です。上記で説明した各ステップのタイムコードも追加してあるので、特定の部分を確認できます:

この記事が参考になりましたか?BlueskyMastodonでフォローして、Swiftのヒントやインディー開発の最新情報をチェックしてください。