【iOS】GitHub Copilot for Xcode の Agent modeを使ってみた

概要

弥生でiOSアプリ開発を担当している市毛です。

2025年5月19日、GitHub Copilot for Xcode の「Agent mode」機能がパブリックプレビューとして公開されました 🎉

github.blog

早速使ってみたので、弥生のiOS開発に活用できるかどうかをレポートします。

前提

弥生ではUIKitを用いてiOSアプリを作っています。

また、UIKitのViewを扱いやすくするために、独自のViewクラスでラップしています。

今後紹介するコードでは、そういった独自のViewクラスや実装が登場しますが、今回の記事の主旨から外れるため、詳細な説明は割愛させていただきます 🙇‍♂️

同様に、独自に定義したViewクラスを使っていたり、標準的でない実装スタイルでアプリを開発されている方にとっては、参考になる点が多いと思います。

  • 環境
    • macOS Sequoia 15.4.1
    • Xcode 16.3
    • GitHub Copilot for Xcode 0.35.0
      • モデル GPT-4o

Agent Modeの概要

Agent Modeの説明を見ると下記の記載があります。 ​

  • Get intelligent code edits applied directly to your files
  • Run terminal commands and view their output without leaving the interface
  • Search through your codebase to find relevant files and code snippets
  • Create new files and directories as needed for your project
  • Get assistance with enhanced context awareness across multiple files and folders
  • Run Model Context Protocol (MCP) tools you configured to extend the capabilities

日本語訳

  • ファイルに直接適用されるインテリジェントなコード編集
  • ターミナル・コマンドを実行し、インターフェイスを離れることなくその出力を表示
  • コードベース内を検索して、関連するファイルやコード・スニペットを見つける
  • プロジェクトに必要な新しいファイルやディレクトリを作成
  • 複数のファイルやフォルダにまたがる強化されたコンテキスト認識による支援
  • 機能を拡張するために設定したモデル・コンテキスト・プロトコル(MCP)ツールの実行

今回の検証について

以下の観点で、実際に弥生の開発現場において活用可能か検証していきます。

1. ファイルに直接適用されるインテリジェントなコード編集

  • タスク: バグ修正の依頼
  • 評価ポイント: 修正の正確性と安全性

2. ターミナル・コマンドの実行と出力確認

  • タスク: ブランチ変更、ライブラリのインストール
  • 評価ポイント: コマンドの理解力と正確な実行可否

3. コードベースの検索機能

  • タスク: 呼び出し先の一覧化 、仕様の説明
  • 評価ポイント: 検索の網羅性、わかりやすさ

4. 新しいファイルの作成

  • タスク: 新機能用の ViewController を新規作成
  • 評価ポイント: 社内コーディングスタイルへの準拠

5. コンテキストをまたぐ支援機能

  • タスク: モデルの特定プロパティの型変更
  • 評価ポイント: 型変更が複数ファイルに正しく伝搬されるか

※各機能は上記の評価ポイントから筆者の主観をもとに5段階で評価しています。

検証に使うデモアプリ

一覧画面と、セルタップで詳細画面が開くシンプルな構成のアプリを使用します。

検証結果

1. ファイルに直接適用されるインテリジェントなコード編集

1-1. セルをタップすると毎回同じ詳細画面が開く

埋め込んだバグ
for document in self.documents {
    // セルの追加
    sections.add(LabelTableCell.self) {
        // 見た目の実装
    } select: { _ in
        // タップ処理  
        guard let firstDocument = self.documents.first else { return }
        self.navigationController?.pushViewController(
              DocumentDetailViewController(document: firstDocument)
        )
    }
}

for文でループしているにも関わらず、firstDocument で常に同じdocumentの情報が表示されるようになっています。

想定の修正
for document in self.documents {
    // セルの追加
    sections.add(LabelTableCell.self) {
        // 見た目の実装
    } select: { _ in
        // タップ処理  
        self.navigationController?.pushViewController(
              DocumentDetailViewController(document: document)
        )
    }
}

for文で回しているdocument を使用するような提案を期待します。

使用したプロンプト

セルをタップすると、常に同じドキュメントの詳細画面が表示されてしまいます。リストの各セルに対応した正しいドキュメント詳細が開くように修正してください。

AIの提案内容
for (index, document) in self.documents.enumerated() {
    // セルの追加
    sections.add(LabelTableCell.self) {
        // 見た目の実装
    } select: { _ in
        // タップ処理  
        self.navigationController?.pushViewController(
              DocumentDetailViewController(document: self.documents[index])
        )
    }
}
評価

★★☆☆☆(2/5)

方向性は良いが、今回はindexの概念は不要であるため、よりシンプルなfor文での提案をしてほしい。

1-2.ドキュメントが更新されても画面が更新されない

埋め込んだバグ
var documents: [Document] = []
想定の修正
var documents: [Document] = [] {
    didSet {
        self.reloadViews()
    }
}

ViewController で保持している documents プロパティが更新されたタイミングで、画面を再描画する処理を実装しています。 バグの埋め込み段階ではこの実装を削除します。

使用したプロンプト

ドキュメントのリスト(documents)を更新しても、UI が更新されません。画面を自動的に再描画するよう修正してください。

AIの提案内容
var documents: [Document] = [] {
    didSet {
        self.reloadViews()
    }
}
// 更新処理をメインスレッドにラップされるような修正もされた
DispatchQueue.main.async { [weak self] in

}
評価

★★★☆☆(3/5)

必要な修正は含まれていたが、副作用が大きそうな修正も併せて提案されたため、そのまま採用するのは難しいと判断しました。

1-3.タイトルと内容が逆に表示される

埋め込んだバグ
cell.labelTitle.text = document.content
cell.labelDetail.text = document.title

cellのlabelに設定する値をそれぞれ title content と逆に設定される状況にします。

想定の修正
cell.labelTitle.text = document.title
cell.labelDetail.text = document.content
使用したプロンプト

リストのセルに表示されるタイトルと詳細が逆になっているようです。正しい内容が表示されるよう修正してください。

AIの提案内容
cell.labelTitle.text = document.title // 修正: タイトルを表示
cell.labelDetail.text = document.content // 修正: 詳細を表示
評価

★★★★★(5/5)

完璧な修正。コメントをそのまま採用するかはさておき、親切で変更点が明確になっていて助かります。 ​

1-4.メモリリークが発生しているように見える

埋め込んだバグ
// TableViewの見た目更新
self.tableView.beginUpdates { 
    // 内部でselfを強参照している
}

上記のコメントにもありますが、クロージャ内部でselfのViewControllerを強参照するような実装にします。

想定の修正
// TableViewの見た目更新
self.tableView.beginUpdates { [weak self] in
    // 内部でselfを弱参照になるように修正
}
使用したプロンプト

この画面を表示したあとにメモリリークが発生しているようです。selfの強参照が原因かもしれません。適切に weak self を使って修正してください。

AIの提案内容
// TableViewの見た目更新
self.tableView.beginUpdates { [weak self] in
    guard let self = self else { return }
}
評価

★★★★★(5/5)

ViewController全体の中から、selfを強参照しているクロージャを見つけ、修正までしてくれました。 修正内容も適切だったため、今回はそのまま採用することができました。

2. ターミナル・コマンドの実行と出力確認

以下の操作をCopilot Agentで実行してみます

  • ブランチの作成、checkout
  • CocoaPodsでのライブラリインストール

ブランチの作成、checkout

プロンプト

issue/branchChangeTest というブランチを作って、checkout したいです。ターミナルでその操作をお願いします。

AIの提案内容

「Continue」を押下

評価

★★★★★(5/5)

Copilotのターミナル上からブランチを作成することができました。

Podライブラリのインストール

プロンプト

CocoaPods のインストールを行いたいです。bundle exec pod install を実行してもらえますか?

返答

「Continue」を押下

今回の環境では rbenv によって Ruby のバージョンを管理しており、Copilot Agent が使っているシェル環境と、普段使用しているターミナル環境とで Ruby バージョンが異なっていたことで pod コマンドでエラーが発生してました。

バージョンを一致させることで動作可能になりますが、今回は本題から外れるため、詳しい説明は省略いたします。

評価

★★★★★(5/5)

上記ブランチ作成のプロンプトと合わせることによって、Xcodeと統合されたCopilot上からブランチ作成〜ライブラリインストールまで実施可能であることがわかりました。

3. コードベースの検索機能

実際に弥生で開発している Misoca プロダクトにて以下のようなケースを用いて検証を行いました。

検証ケース

  • 文書に関する設定のモデル(DocumentSettings)を使用している箇所を検索
  • ステータス更新ロジックのバグ調査のため、呼び出し箇所に関する仕様把握

DocumentSettings モデルの使用箇所を検索

プロンプト

DocumentSettings モデルを使用しているすべてのファイルを一覧にしてください。

返答

評価

★☆☆☆☆(1/5)

一部では DocumentSettings というモデルクラスではなく DocumentSettingsViewController のクラスを検索している箇所もありました。

文言の一致で検索を行っているようですので、Xcode のIDE上でできる検索の結果と大きな差は見られませんでした。

Xcode の検索では該当のソースコードへタップで移動できるため、この用途においては、Copilot Agent の検索はあまり有用ではないと感じました。

ステータス更新の呼び出し箇所に関する仕様把握

プロンプト

ステータス更新のロジックにバグがあるかもしれないので、Documentのステータスを更新している箇所を一覧にしてください。Documentのsectionの変更がステータスの変更です。

要約した返答内容

回答結果が長かったため、評価ポイントが判断できるくらいのボリュームに要約させていただきます。

  1. Document の参照は多いが、主に 画面 および API クラス内にあり。
  2. Documentクラス にステータス関連のプロパティが多数存在。
  3. section プロパティがステータス変更に関与している可能性あり。
  4. APIクラス の change メソッドが更新処理を担当。
  5. バグがある場合は、APIレスポンスの処理方法に潜んでいる可能性がある。
評価

★★★★★(5/5)

仕様の把握から、バグの可能性がある箇所の特定まで、丁寧に説明してくれました。 このように、 DocumentSettings などの特定クラスを指定するのではなく、全体の仕様理解や関連箇所の調査を目的としたプロンプトを用いることでの Copilot Agent の活用は、非常に有効であると感じました。

参考:Askモードでの同一プロンプトへの返答

Documentのsectionが変更されることでステータスが更新される箇所を探すには、Xcodeで以下のようなコードパターンを検索する必要があります。 (以下略)

→ IDE検索の方法を教えてくれますが、ファイルを跨いだ推論・構造の把握まではできなかったです。ここが、冒頭にも書いたAgentの魅力ですね。

4. 新しいファイルの作成

デモアプリのプロジェクトに 設定画面 を追加してみました。

プロンプト

独自のコンポーネントを使った UIKit ベースのアプリです。
社内標準では以下のクラスを使用しています
- ViewController: 独自の共通ベースクラス
- TableView: 標準の UITableView を拡張したラッパークラス
各種カスタムセル(例: LabelTableCell、LabelDetailTableCell など)
以下の仕様で、新機能用の SettingsViewController を作成してください。
【要件】
ViewController を継承し、TableView を使って構成してください
viewDidLoad() でタイトルやレイアウトを設定し、複数セクション・セルを使って構成し、画面の構成要素を整理
コーディングスタイルは [参考のViewController]を参考にしてください

自動生成されたファイル

class SettingsViewController: ViewController {

    private let tableView = TableView()

    override func viewDidLoad() {
        super.viewDidLoad()

        title = "Settings"
        view.backgroundColor = .systemBackground

        tableView.delegate = self
        tableView.dataSource = self
        tableView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(tableView)

        NSLayoutConstraint.activate([
            tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            tableView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
            tableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
            tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
        ])
    }
}

評価

★☆☆☆☆(1/5)

独自コンポーネントの実装は考慮されておらず、そのまま採用できるのはクラス名程度にとどまっておりました。 また、ビルドが通らない状況であったことに加え、新規作成されたファイルがXcodeのプロジェクトファイルに自動で反映されていない点も確認されました。 これらの点から、Copilot Agent Mode における当該機能は、現時点では完成度が十分とは言い難く、弥生の開発環境において実用するのは難しいと判断いたしました。

5. コンテキストをまたぐ支援機能

プロンプト

Document モデルの content プロパティの型を String から Date に変更したいです。 プロパティ名を contentから createdDate に変更してください。 それにより影響を受ける他のファイルも合わせて修正してください。

修正されたファイルの差分 ( git diff コマンドの抜粋)

class Document {
-    var content: String
+    var createdDate: Date
}

class DocumentListViewController: ViewController {
-                        $0.labelDetail.text = document.content
+                        let dateFormatter = DateFormatter()
+                        dateFormatter.dateStyle = .medium
+                        dateFormatter.timeStyle = .none
+                        $0.labelDetail.text = dateFormatter.string(from: document.createdDate) // Format Date to String
}

評価

★★★☆☆(3/5)

一部ビルドエラーがあり修正が必要な箇所はありますが、型変換やプロパティ名の修正に伴う影響範囲の特定、およびそれに対する大まかな修正が行われており、手作業で行うよりも工数を削減できると感じました。

まとめ

GitHub Copilot Agent Mode は、エディタを離れることなく開発作業を支援してくれる新しいアシスタントとして非常に魅力的です。

特に「ファイルをまたぐコード修正」「クラスやプロパティに関するアプリ全体での使われ方の把握(仕様理解)」「ターミナル操作」といった日常の開発業務に対して、想定以上に高精度かつ実用的な支援を得ることができました。

一方で、「画面全体を作成する」などは弥生社内で使われているUI構成のスタンダードがあるため、それに沿った提案までは現時点ではできていない印象でした。

GitHub Copilot for Xcode最大の魅力はIDEに付随したAIがいることだと思っているので、開発者が詰まったときや困ったときの取っ掛かりとして、使用してみるのは非常に有用であると感じました。

弥生では一緒に働く仲間を募集しています。
herp.careers
弥生のエンジニアに関するnote記事もご覧ください。
note.yayoi-kk.co.jp