↔️

Raycast で Android Studio ⇄ Xcode を同じ行のまま切り替える

に公開

はじめに

生成 AI の登場により、担当領域を越えて開発に挑戦しやすくなってきました。もともと Android メインのエンジニアでしたが、最近は iOS 側のコードを書くことも増えています。

iOS 開発と言えば Xcode が定番ですが、Android Studio に Kotlin Multiplatform プラグインを入れておくと、意外にも iOS プロジェクトを開くことができ、コード補完やコードジャンプ、ビルドして実行などをすることが可能です。普段使い慣れた IDE のまま作業できるため、Android エンジニアにとっては入りやすい環境だと感じています。

ただし、SwiftUI の Preview が表示できないなど、Xcode でなければできないこともあります。これを解消すべく、Raycast を使って Android Studio と Xcode で「開いているファイル」と「カーソルのある行」を引き継いで相互に切り替える仕組みを作りました。

作ったもの

Raycast でコマンドを実行すると、いま開いているファイルをもう一方の IDE で開き、カーソルの行も引き継ぎます。

仕組みとしては Raycast を起点にして、それぞれの IDE に合わせた方法で行番号を受け渡しています。


Android Studio から Xcode へ


Xcode から Android Studio へ

準備:Raycast Script Commands 用フォルダの作成

Mac 上の適当な場所にスクリプト保存用フォルダを作成しておきます。

mkdir -p ~/raycast-scripts

Raycast の Settings > Extensions > Script Commands > Add Directories から、作成したフォルダを登録します。

Android Studio から Xcode を開く

macOS には AppleScript というアプリを外部から操作する仕組みがあり、osascript コマンドで実行できます。今回はこれを使って相手の IDE を操作します。

ただし Android Studio は AppleScript に対して開いているファイルや行番号を返せません。代わりに External Tools(外部コマンドを登録して実行できる機能)を使うと、引数に書いた $FilePath$ $LineNumber$ が、実行した瞬間に開いているファイルのパスとカーソル行の行番号へ置き換わります。これを利用して「$FilePath$ $LineNumber$ を渡して osascript を呼ぶ External Tool」を登録し、Raycast からはそのショートカットキーを送るだけにします。

  1. Raycast:Android Studio を前面に出してショートカットを送信します
  2. Android Studio:ショートカットで External Tool が起動し、開いているファイルと行番号を引数に埋め込んで osascript を実行します
  3. (Android Studio の) osascript:Xcode で受け取ったファイルを開き、行番号へジャンプします

以降は、この流れを実現するために External Tools の登録 → ショートカットキーの割り当て → Raycast スクリプトの作成 の順で設定していきます。

External Tools の登録

Settings > Tools > External Tools を開き、+(追加)から以下のように入力します。

項目 設定値
Name Open in Xcode with Line(名前は任意です)
Program osascript
Arguments (下記参照)

Arguments には次の文字列を貼り付けます(1行で記述してください)。

-e "tell application \"Xcode\" to activate" -e "tell application \"Xcode\" to open \"$FilePath$\"" -e "delay 0.5" -e "tell application \"System Events\"" -e "tell process \"Xcode\"" -e "keystroke \"l\" using {command down}" -e "delay 0.2" -e "keystroke \"$LineNumber$\"" -e "keystroke return" -e "end tell" -e "end tell"

展開すると次のような内容です。

tell application "Xcode" to activate
tell application "Xcode" to open "$FilePath$"
delay 0.5

tell application "System Events"
    tell process "Xcode"
        keystroke "l" using {command down}
        delay 0.2
        keystroke "$LineNumber$"
        keystroke return
    end tell
end tell

Xcode をアクティブにしてファイルを開き、Command + L で行番号へジャンプするだけのシンプルな内容です。$FilePath$ $LineNumber$ の部分は実行時に /path/to/ContentView.swift 42 のような実際の値に変わるので、osascript 側は渡されたファイルと行番号を開くだけで済みます。なお delay の待ち時間は、マシンの性能やプロジェクトの規模によって調整が必要になる場合があります。

ショートカットキーの割り当て

External Tool は Raycast から直接は呼び出せないため、キー入力を送って起動させます。その起動用のキーを決めておきます。

Settings > Keymap を開き、作成した Open in Xcode with Line にショートカットキーを割り当てます。今回は Ctrl + Option + Command + Shift + X にしました。

このキーを手で押すことはなく、次に作る Raycast スクリプトから自動で送信されるだけなので、他のショートカットと衝突しない押しにくい組み合わせを選んでいます。

なお、この External Tool のショートカットを直接押しても同じように動作します。

Raycast スクリプトの作成

起点となるスクリプトです。Android Studio を前面に出し、割り当てたショートカットを自動で送信します。

open-in-xcode.sh
#!/bin/bash

# Required parameters:
# @raycast.schemaVersion 1
# @raycast.title Android Studio to Xcode
# @raycast.mode silent
# @raycast.icon 🍏

osascript <<EOD
-- 1. Android Studio を一瞬前面に出す
tell application "Android Studio" to activate
delay 0.2

-- 2. Android Studio で設定したショートカットを自動タイピング
tell application "System Events"
    keystroke "x" using {command down, option down, control down, shift down}
end tell
EOD

作成したスクリプトは、準備で Raycast に登録した ~/raycast-scripts に保存し、実行権限を付与します。

chmod +x ~/raycast-scripts/open-in-xcode.sh

注意点:Xcode はあらかじめ開いておく

Xcode が起動していない状態で Android Studio to Xcode を実行すると、ワークスペースから独立した簡易エディタとしてファイルが開いてしまいます。そのため、対象のプロジェクトをあらかじめ Xcode で開いておくようにして対処しています。

Xcode から Android Studio を開く

Xcode は AppleScript で開いているファイルとカーソル位置を返せるため、Raycast のスクリプトから直接読み取れます。行番号を計算して Android Studio を開くだけなので、Xcode 側の設定変更は必要ありません。

Raycast スクリプトの作成

以下を記述します。

open-in-android-studio.sh
#!/bin/bash

# Required parameters:
# @raycast.schemaVersion 1
# @raycast.title Xcode to Android Studio
# @raycast.mode silent
# @raycast.icon 🤖

osascript <<EOD
tell application "Xcode"
    try
        -- 1. 現在開いているファイルのフルパスを取得
        set current_document to last source document
        set current_document_path to path of current_document

        -- 2. 現在のカーソル位置(文字数)を取得
        set sel to selected character range of current_document
        set cursor_pos to item 1 of sel

        -- 3. ファイルの先頭からカーソル位置までの改行を数えて行番号を計算
        set doc_text to text of current_document
        set text_before_cursor to text 1 through cursor_pos of doc_text
        set line_num to count paragraphs of text_before_cursor

        -- 4. macOS の open コマンドではなく Android Studio の内部コマンドを直接叩く
        set as_path to POSIX path of (path to application "Android Studio")
        set studio_bin to as_path & "Contents/MacOS/studio"

        do shell script quoted form of studio_bin & " --line " & line_num & " " & quoted form of current_document_path

    on error
        display dialog "Xcode からファイル情報、または行数を取得できませんでした。"
    end try
end tell
EOD

カーソル位置は文字数で返ってくるため、先頭からカーソル位置までのテキストを切り出し、その段落数を数えることで行番号を求めています。文字数からの逆算なので、ファイルの先頭などでは意図した行にならないこともあります。

なお、ファイルを開く際に macOS の open コマンドへ引数を渡す方法も試しましたが、Android Studio が起動済みの場合はパラメーターが無視されてしまいました。そのため、アプリケーションバンドル内の Contents/MacOS/studio を直接呼び出し、--line オプションで行番号を指定しています。

こちらも実行権限を付与します。

chmod +x ~/raycast-scripts/open-in-android-studio.sh

Tips:エイリアスを登録する

作成した 2 つのコマンドは、Raycast の Settings > Extensions から選択し、Alias に短い文字列を登録できます。今回は Android Studio to XcodetoxXcode to Android Studiotoa を設定しました。

tox toa と打つだけで IDE を切り替えることができるので、エイリアス設定はおすすめです。

まとめ

Android Studio と Xcode を、開いているファイルと行番号を保ったまま相互に切り替える方法をまとめました。

ロジックは Android Studio で書き、SwiftUI の Preview を確認したいときだけシュッと Xcode へ移る、といった進め方ができるようになりました。IDE 間の移動を最低限に抑えつつ、慣れ親しんだ Android Studio をメインに iOS 開発を進められています。

この記事が Android Studio と Xcode を併用している方の参考になれば幸いです。

GENDA

Discussion