🪶
Writing code with intelligence in Xcode
Xcode26.0からAI Agentが使える
皆さんこんにちわJboyです。
Appleが、Xcode26.0からCoding Agentなるものを搭載しました。これはどんなものかと言うと、Vscode/Cursor/Windsurfのエージェントモードと同じように、プロンプトを入力したり画像のuploadをすると、コードを自動生成してくれる機能です。
こちらが公式のリンク👇
使用方法
ChatGPT DesktopかClaude Desktopが必要です。有料ツールを使うのでアカウントも作成しておいてください。
iOSDCに行った日に、MacOS26までバージョン上げないと使用できないそうで、Mac miniのOSのバージョンを上げました。そしたら画面左に、AIのアイコンが表示されたので使えるようになりました!

AIツールはこちらを使用します
実際に動かして動画も撮ってみました。ChatDesktopを使用しております。
電卓とストップウォッチのソースコードを生成させてみました。使ってみた感じ失敗することもあったが、修正すると精度の高いものに仕上がりました!

ストップウォッチ
import SwiftUI
struct StopWatchView: View {
// MARK: - State
@State private var isRunning: Bool = false
@State private var baseElapsed: TimeInterval = 0 // 累積経過時間(停止中も保持)
@State private var startDate: Date? = nil // 計測再開時の開始時刻
@State private var tick: Date = Date() // 画面更新トリガー
// MARK: - Body
var body: some View {
VStack(spacing: 24) {
Spacer()
// Time Display
Text(formattedElapsed)
.font(.system(size: 64, weight: .semibold, design: .rounded))
.monospacedDigit()
.minimumScaleFactor(0.5)
.lineLimit(1)
.padding(.horizontal)
.accessibilityLabel("経過時間")
.accessibilityValue(formattedElapsed)
// Controls
HStack(spacing: 16) {
Button("スタート", action: start)
.buttonStyle(.borderedProminent)
.tint(.green)
.disabled(isRunning)
Button("ストップ", action: stop)
.buttonStyle(.borderedProminent)
.tint(.red)
.disabled(!isRunning)
Button("リセット", action: reset)
.buttonStyle(.bordered)
.tint(.gray)
.disabled(!canReset)
}
.padding(.horizontal)
Spacer()
}
.task(id: isRunning) {
guard isRunning else { return }
// スタート直後の即時更新
await MainActor.run { tick = Date() }
// 10ms 間隔で tick を更新(タスクがキャンセルされるまで)
while !Task.isCancelled {
try? await Task.sleep(nanoseconds: 10_000_000)
await MainActor.run { tick = Date() }
}
}
.navigationTitle("ストップウォッチ")
}
// MARK: - Derived
private var currentElapsed: TimeInterval {
if isRunning, let startDate {
return baseElapsed + tick.timeIntervalSince(startDate)
} else {
return baseElapsed
}
}
private var formattedElapsed: String { format(currentElapsed) }
private var canReset: Bool {
// 計測中は常にリセット可能、停止中は経過があるときのみ
isRunning || currentElapsed > 0.0
}
// MARK: - Actions
private func start() {
guard !isRunning else { return }
let now = Date()
startDate = now
tick = now
isRunning = true
}
private func stop() {
guard isRunning else { return }
let now = Date()
if let startDate {
baseElapsed += now.timeIntervalSince(startDate)
}
isRunning = false
self.startDate = nil
tick = now
}
private func reset() {
baseElapsed = 0
let now = Date()
if isRunning {
// 計測中にリセットした場合は 0 から継続
startDate = now
tick = now
} else {
startDate = nil
tick = now
}
}
// MARK: - Formatting
private func format(_ t: TimeInterval) -> String {
let totalCentiseconds = Int((t * 100).rounded())
let cs = totalCentiseconds % 100
let totalSeconds = totalCentiseconds / 100
let s = totalSeconds % 60
let totalMinutes = totalSeconds / 60
let m = totalMinutes % 60
let h = totalMinutes / 60
if h > 0 {
return String(format: "%d:%02d:%02d.%02d", h, m, s, cs)
} else {
return String(format: "%02d:%02d.%02d", m, s, cs)
}
}
}
#Preview {
StopWatchView()
}

電卓
import SwiftUI
struct ContentView: View {
// MARK: - Calculator State
@State private var display: String = "0"
@State private var accumulator: Double? = nil
@State private var pendingOperation: Operation? = nil
@State private var currentInput: String = ""
@State private var isError: Bool = false
enum Operation {
case add, subtract, multiply, divide
}
// MARK: - Derived Strings
private var expression: String {
if isError { return "" }
if let acc = accumulator, let op = pendingOperation {
let left = format(acc)
let sym = symbol(for: op)
if currentInput.isEmpty {
return "\(left) \(sym)"
} else {
return "\(left) \(sym) \(currentInput)"
}
} else {
return currentInput.isEmpty ? "" : currentInput
}
}
private func symbol(for op: Operation) -> String {
switch op {
case .add: return "+"
case .subtract: return "−"
case .multiply: return "×"
case .divide: return "÷"
}
}
// MARK: - UI
var body: some View {
VStack(spacing: 16) {
Spacer(minLength: 0)
// Display
HStack {
Spacer()
VStack(alignment: .trailing, spacing: 4) {
Text(expression)
.font(.system(size: 20, weight: .regular, design: .rounded))
.foregroundStyle(.secondary)
.lineLimit(1)
.minimumScaleFactor(0.5)
.opacity(expression.isEmpty ? 0 : 1)
Text(display)
.font(.system(size: 64, weight: .light, design: .rounded))
.lineLimit(1)
.minimumScaleFactor(0.3)
.monospacedDigit()
}
.padding(.horizontal)
}
// Buttons Grid
Grid(horizontalSpacing: 12, verticalSpacing: 12) {
GridRow {
calcButton(title: "AC", bg: .gray.opacity(0.25), fg: .primary) { allClear() }
.gridCellColumns(3)
opButton("÷", op: .divide)
}
GridRow {
digitButton("7")
digitButton("8")
digitButton("9")
opButton("×", op: .multiply)
}
GridRow {
digitButton("4")
digitButton("5")
digitButton("6")
opButton("−", op: .subtract)
}
GridRow {
digitButton("1")
digitButton("2")
digitButton("3")
opButton("+", op: .add)
}
GridRow {
digitButton("0")
.gridCellColumns(2)
decimalButton()
equalsButton()
}
}
.padding(.horizontal)
.padding(.bottom)
}
.padding(.top)
}
// MARK: - Button Builders
private func digitButton(_ title: String) -> some View {
calcButton(title: title, bg: .gray.opacity(0.15)) {
appendDigit(title)
}
}
private func decimalButton() -> some View {
calcButton(title: ".", bg: .gray.opacity(0.15)) {
appendDecimal()
}
}
private func opButton(_ symbol: String, op: Operation) -> some View {
calcButton(title: symbol, bg: .orange, fg: .white) {
selectOperation(op)
}
}
private func equalsButton() -> some View {
calcButton(title: "=", bg: .orange, fg: .white) {
equals()
}
}
private func calcButton(title: String, bg: Color, fg: Color = .primary, action: @escaping () -> Void) -> some View {
Button(action: action) {
Text(title)
.font(.system(size: 28, weight: .semibold, design: .rounded))
.frame(maxWidth: .infinity, minHeight: 64)
.contentShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
}
.buttonStyle(.plain)
.foregroundStyle(fg)
.background(bg, in: RoundedRectangle(cornerRadius: 16, style: .continuous))
}
// MARK: - Logic
private func appendDigit(_ d: String) {
if isError { allClear() }
// Limit length to avoid overflow
guard currentInput.count < 12 else { return }
if currentInput == "0" {
// Replace leading zero unless adding decimal later
currentInput = d
} else {
currentInput.append(d)
}
display = currentInput
}
private func appendDecimal() {
if isError { allClear() }
if currentInput.isEmpty { currentInput = "0" }
guard !currentInput.contains(".") else { return }
currentInput.append(".")
display = currentInput
}
private func selectOperation(_ op: Operation) {
if isError { allClear() }
if let value = Double(currentInput) {
if let acc = accumulator, let pending = pendingOperation {
// Compute chained operation first
if let result = compute(lhs: acc, op: pending, rhs: value) {
accumulator = result
display = format(result)
} else {
showError()
return
}
} else {
accumulator = value
}
currentInput = ""
}
// If no current input but we have an accumulator, just change the pending op
pendingOperation = op
}
private func equals() {
if isError { allClear(); return }
guard let acc = accumulator, let pending = pendingOperation, let value = Double(currentInput) else { return }
if let result = compute(lhs: acc, op: pending, rhs: value) {
display = format(result)
accumulator = result
pendingOperation = nil
currentInput = ""
} else {
showError()
}
}
private func allClear() {
accumulator = nil
pendingOperation = nil
currentInput = ""
isError = false
display = "0"
}
private func compute(lhs: Double, op: Operation, rhs: Double) -> Double? {
switch op {
case .add: return lhs + rhs
case .subtract: return lhs - rhs
case .multiply: return lhs * rhs
case .divide:
if rhs == 0 { return nil }
return lhs / rhs
}
}
private func showError() {
display = "Error"
isError = true
accumulator = nil
pendingOperation = nil
currentInput = ""
}
private func format(_ x: Double) -> String {
if x.isNaN || x.isInfinite { return "Error" }
let roundedToInt = x.rounded()
if abs(roundedToInt - x) < 1e-10 {
return String(Int(roundedToInt))
}
var s = String(format: "%.8f", x)
// Trim trailing zeros
while s.contains(".") && s.last == "0" { s.removeLast() }
if s.last == "." { s.removeLast() }
return s
}
}
#Preview {
ContentView()
}
最後に
使ってみた感想ですが、GitHub Copilot For Xcodeと比較すると劣ってはいないように思えました。違いがあるとしたら、質問機能のaskのpull downがないことでしょうか。プロンプトをコンテキストを読み込んで質問をすれば回答はしてくれるので、ここは慣れかなと思います。
ファイル名を指定して質問を投げるとこんな感じで回答が返ってきます。これがあれば多分Cursorはいらなくなるだろうと思いました。できれば、同じIDEで操作は完結したいので💦
ご興味ある方は試してみてください!

Discussion