Claude Code iOS Safety Guide
Run Claude Code autonomously for fast iOS development — without risking file destruction or project corruption. Step-by-step setup with every terminal command spelled out.
--dangerously-skip-permissions: This flag disables ALL built-in permission prompts, including settings.json allow/deny rules. When active, Claude can execute any command without asking. This guide provides external guardrails (git checkpoints, hooks, CLAUDE.md) that work regardless of this flag.
Prerequisites
You need Claude Code installed and working before starting this guide.
npm install -g @anthropic-ai/claude-code
claude --version
Safety Overview
The guide uses a layered defense approach. Steps are ranked by impact — do the top ones first and you're already 90% protected. Everything is persistent — you set it up once.
| Layer | What It Does | Protects Against |
|---|---|---|
| Step 1 — Git checkpoint | Auto-commit before every session | Everything (nuclear rollback) |
| Step 2 — Permission whitelist | Blocks rm, sudo, rmdir | File deletion, privilege escalation |
| Step 3 — CLAUDE.md | Forbids .pbxproj edits, sets rules | Project corruption, scope creep |
| Step 4 — XcodeBuildMCP | Structured build/test/run tools | Malformed xcodebuild commands |
| Step 5 — Slash commands | Quick access to safe workflows | Human error, repetitive typing |
| Step 6 — Simulator skill | Structured simulator interaction | Raw simctl mistakes |
| Step 7 — Daily workflow | Repeatable safe process | Forgetting to checkpoint |
deny rules in settings.json are not reliably enforced. Commands like find . -exec rm {} \; or bash -c "rm -rf ..." bypass Bash(rm:*) deny patterns. Additionally, --dangerously-skip-permissions bypasses the entire permission system. This is why the git checkpoint (Step 1) and hooks (Step 2b) are your real safety nets — not the permission config alone.
Choose Your Path
Two approaches to running Claude Code safely for iOS development. Pick one based on your needs.
Native Xcode Setup
Full Xcode and Simulator integration. Claude runs directly on your Mac with layered guardrails — git checkpoints, hooks, permissions, and CLAUDE.md rules.
Recommended · Steps 1–7Container Isolation
Claude runs inside a Docker container and cannot touch your host system. You build natively in Xcode. Trade-off: split workflow between two terminals.
Maximum Security · Skip to DevContainerGit Checkpoint Alias
This is your single most important safety layer. If Claude deletes files, corrupts your .pbxproj, or makes bad changes — one command rolls everything back. You're adding a shortcut command (called an "alias") to your shell configuration file.
1a — Open your shell config file
open ~/.zshrc
If you get an error saying the file doesn't exist, create it first:
touch ~/.zshrc
open ~/.zshrc
1b — Paste the alias at the bottom of the file
Scroll to the very bottom and paste this line:
# Safe Claude Code launcher — auto-commits before every session
alias clauded='git add -A && git commit -m "pre-claude checkpoint $(date +%H:%M)" --allow-empty && claude --dangerously-skip-permissions'
1c — Save and reload
Save the file (Cmd + S), close TextEdit, then reload:
source ~/.zshrc
1d — Verify it worked
which clauded
You should see output like clauded: aliased to git add -A && .... If you see "not found", repeat steps 1a–1c.
How to use it
From now on, start every Claude session with clauded instead of claude:
cd ~/path/to/your/ios-project
clauded
If anything goes wrong mid-session, undo everything Claude did:
# Undo everything
git reset --hard HEAD
# Or review changes first
git diff HEAD
Permission Whitelist
This explicitly allows only the commands Claude needs and blocks destructive ones like rm (delete).
2a — Create the settings directory
# Check if directory exists
ls ~/.claude/
# If "No such file or directory", create it:
mkdir -p ~/.claude
2b — Check for existing settings
cat ~/.claude/settings.json
If you see existing content, you'll merge carefully. If "No such file or directory", proceed to create it.
2c — Open the settings file
touch ~/.claude/settings.json
open ~/.claude/settings.json
2d — Paste the permission config
{
"permissions": {
"allow": [
// File tools
"Read(*)", "Write(*)", "Edit(*)",
// Shell basics
"Bash(ls:*)", "Bash(mkdir:*)", "Bash(mv:*)",
"Bash(cp:*)", "Bash(cat:*)", "Bash(head:*)",
"Bash(tail:*)", "Bash(grep:*)", "Bash(rg:*)",
"Bash(find:*)", "Bash(touch:*)", "Bash(echo:*)",
"Bash(which:*)", "Bash(open:*)",
"Bash(wc:*)", "Bash(sort:*)", "Bash(uniq:*)",
"Bash(diff:*)", "Bash(tr:*)", "Bash(cut:*)",
"Bash(tee:*)", "Bash(env:*)",
"Bash(basename:*)", "Bash(dirname:*)",
// Text processing
"Bash(sed:*)", "Bash(awk:*)",
// Git
"Bash(git:*)",
// Swift / Xcode toolchain
"Bash(swift:*)", "Bash(swiftc:*)",
"Bash(xcodebuild:*)", "Bash(xcrun:*)",
"Bash(xcode-select:*)", "Bash(plutil:*)",
"Bash(swiftlint:*)", "Bash(swiftformat:*)",
// Package managers
"Bash(pod:*)", "Bash(mint:*)",
"Bash(brew install:*)", "Bash(brew info:*)",
"Bash(npm:*)", "Bash(npx:*)",
// System
"Bash(chmod:*)", "Bash(make:*)",
"Bash(python3:*)", "Bash(curl:*)",
"Bash(pkill:*)"
],
"deny": [
"Bash(rm:*)",
"Bash(rmdir:*)",
"Bash(sudo:*)",
"Bash(bash -c:*)",
"Bash(sh -c:*)",
"Bash(chmod 777:*)",
"Edit(**/*.pbxproj)"
]
}
}
2e — Save and validate
Save the file (Cmd + S), close TextEdit, then validate the JSON:
python3 -c "import json; json.load(open('$HOME/.claude/settings.json')); print('Valid JSON')"
If you see "Valid JSON", you're good. If you see an error, open the file and check for missing commas or brackets.
rm, rmdir, sudo, bash -c, and .pbxproj edits are blocked. Dev tools (git, swift, xcodebuild, xcrun, swiftlint, swiftformat) are pre-approved. Note: brew is limited to brew install and brew info only — not brew uninstall or brew cleanup.
Safety Hooks
Hooks are shell commands that fire automatically before or after Claude uses a tool. Unlike deny rules, hooks execute deterministically and cannot be bypassed — even with --dangerously-skip-permissions. This is your most reliable enforcement layer.
PreToolUse hook intercepts the actual tool invocation and can inspect the full command. Deny rules only match the first token of a Bash command, so find . -exec rm {} \; bypasses Bash(rm:*). Hooks catch everything.
2b.1 — Add hooks to your settings
Open ~/.claude/settings.json and add a hooks key alongside the existing permissions key:
{
"permissions": { ... },
"hooks": {
"PreToolUse": [
{
// Block destructive commands (rm, rmdir, etc.)
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "echo \"$CLAUDE_TOOL_INPUT\" | grep -qiE '\\brm\\b|\\brmdir\\b|> /dev/null' && echo 'BLOCKED: destructive command detected' >&2 && exit 2 || exit 0"
}]
},
{
// Block .pbxproj modifications
"matcher": "Edit",
"hooks": [{
"type": "command",
"command": "echo \"$CLAUDE_TOOL_INPUT\" | grep -q '.pbxproj' && echo 'BLOCKED: .pbxproj edit' >&2 && exit 2 || exit 0"
}]
}
],
"PostToolUse": [
{
// Auto-format Swift files after edits
"matcher": "Write|Edit",
"hooks": [{
"type": "command",
"command": "echo \"$CLAUDE_TOOL_INPUT\" | grep -q '.swift' && which swiftformat > /dev/null && swiftformat \"$(echo $CLAUDE_TOOL_INPUT | grep -oE '/[^ ]*\\.swift')\" --quiet || exit 0"
}]
}
]
}
}
2b.2 — How hooks work
| Hook Event | When It Fires | Use For |
|---|---|---|
PreToolUse | Before a tool executes | Block destructive commands, prevent file edits |
PostToolUse | After a tool completes | Auto-format code, log changes, run linters |
Stop | When Claude finishes responding | Auto-commit changes, send notifications |
SessionStart | When a session begins | Validate environment, check prerequisites |
2b.3 — Exit codes
The hook's exit code controls behavior:
| Exit Code | Effect |
|---|---|
0 | Allow — tool proceeds normally |
2 | Block — tool execution is prevented, stderr shown to Claude |
| Other | Error — hook failure is reported but tool may still proceed |
matcher field matches tool names: "Bash", "Edit", "Write", "Read". Use | for multiple: "Write|Edit". The environment variable $CLAUDE_TOOL_INPUT contains the JSON input to the tool.
CLAUDE.md Guardrails
Claude reads a file called CLAUDE.md automatically at the start of every session and treats the contents as binding instructions. You'll create one in your iOS project.
3a — Navigate to your project
# Replace with your actual project path
cd ~/Developer/MyApp
3b — Create and open CLAUDE.md
touch CLAUDE.md
open CLAUDE.md
3c — Paste the guardrails
Paste this into the file and customize the bottom section:
# Project Rules for Claude Code
## Critical — Never Do These
- NEVER modify any .pbxproj file directly. Create new files
and tell me to add them in Xcode manually.
- NEVER modify .xcscheme or build configuration files.
- NEVER use rm, rm -rf, rmdir, or any file deletion command.
- NEVER create files outside the project directory.
- NEVER modify .gitignore without asking first.
- NEVER touch Podfile.lock, Package.resolved, or any lockfiles.
- NEVER modify Info.plist signing or provisioning settings.
- NEVER add third-party dependencies without asking first.
- NEVER run `pod install` or `swift package resolve` without asking.
- NEVER use `open -a` to launch applications.
## Required Practices
- Commit checkpoints after completing a feature or significant change.
- Place new Swift files in the correct group folder.
- Use SwiftUI for new views unless told otherwise.
- Prefer @Observable over ObservableObject for iOS 17+ targets.
- Use structured concurrency (async/await) over completion handlers.
- Use #Preview macro syntax for iOS 17+ projects.
- Add MARK: comments to organize code sections.
- Follow existing code patterns and naming conventions.
- Add Logger/os_log statements for complex async flows.
- Write XCTest unit tests for new business logic.
- When fixing a build error, fix only that error — do not refactor
surrounding code.
- Maximum file length: 300 lines. Suggest splitting if exceeded.
## Build Instructions
- Always use XcodeBuildMCP tools instead of raw xcodebuild.
- Build: simulator/build
- Test: simulator/test
- Run: simulator/build-and-run
## Project Architecture
- Architecture: [MVVM / MVC / TCA — replace with yours]
- Min target: iOS [17 / 18 — replace with yours]
- Packages: [SPM / CocoaPods — replace with yours]
- Main scheme: [YourAppScheme — replace with yours]
## Key Directories
- Sources/Features/ — feature modules
- Sources/Services/ — networking, persistence
- Tests/ — unit and UI tests
3d — Commit to your project
git add CLAUDE.md
git commit -m "add Claude Code project guardrails"
XcodeBuildMCP v2
XcodeBuildMCP (now maintained by Sentry) gives Claude 59 structured tools for building, testing, debugging, and interacting with your iOS app — instead of running raw shell commands. It supports Xcode projects, workspaces, and Swift packages.
4a — Check Node.js
node --version
If you see a version number (e.g. v20.11.0), skip to 4b. If "command not found":
# Install Homebrew first if you don't have it:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Then install Node:
brew install node
4b — Install XcodeBuildMCP
claude mcp add --transport stdio XcodeBuildMCP -- npx -y xcodebuildmcp@latest
4c — (Optional) Enable fast rebuilds
claude mcp add --transport stdio XcodeBuildMCP \
--env INCREMENTAL_BUILDS_ENABLED=true \
--env XCODEBUILDMCP_DYNAMIC_TOOLS=true \
-- npx -y xcodebuildmcp@latest
4d — Verify it works
# Start a Claude session in your project
cd ~/path/to/your/ios-project
claude
# Inside the session, type:
mcp__xcodebuildmcp__list_simulators
# You should see iPhone 15, iPhone 16, etc.
# Type /exit to leave
If you get an error, verify Xcode command line tools are installed:
xcode-select -p # Should print a path
xcode-select --install # Install if missing
4e — (Optional) Load only specific workflows
XcodeBuildMCP v2 has 59 tools. To save context window space, load only what you need:
claude mcp add --transport stdio XcodeBuildMCP \
--env XCODEBUILDMCP_ENABLED_WORKFLOWS=simulator,debugging \
-- npx -y xcodebuildmcp@latest
Available Tool Categories (v2)
| Category | Key Tools | What They Do |
|---|---|---|
| Simulator | simulator/build, simulator/build-and-run, simulator/test | Build, run, and test on iOS Simulator |
| Screenshots | simulator/screenshot | Capture simulator screen for visual verification |
| UI Automation | ui-automation/tap, ui-automation/swipe, ui-automation/type, ui-automation/long-press | Interact with running app UI elements |
| UI Inspection | describe_ui | Get full view hierarchy with frame coordinates |
| Debugging | debugging/attach, debugging/breakpoint | LLDB integration for runtime debugging |
| Device | Deploy/test tools | Physical device deployment over USB/Wi-Fi |
| SPM | swift_package_build, swift_package_test | Swift Package Manager operations |
| Project | clean, list_simulators, boot_simulator | Project management and simulator control |
xcodebuildmcp) remains the same. The GitHub repo moved to getsentry/XcodeBuildMCP. It integrates natively with Xcode 26.3's agentic coding features.
Skills & Commands
Shortcuts you can type inside a Claude Code session. Instead of a long instruction, type /build or /test.
.claude/commands/ approach still works (backward-compatible), but the recommended path is .claude/skills/ with user-invocable: true. Both approaches are shown below.
5a — Create the commands directory
cd ~/path/to/your/ios-project
mkdir -p .claude/commands
5b — Create the build command
cat > .claude/commands/build.md << 'EOF'
---
description: Build the iOS project on simulator
allowed-tools: mcp__xcodebuildmcp__*
---
Build the project using `mcp__xcodebuildmcp__build_sim_name_proj`.
Report any build errors clearly with file, line number, and suggested fix.
EOF
5c — Create the test command
cat > .claude/commands/test.md << 'EOF'
---
description: Run all unit tests
allowed-tools: mcp__xcodebuildmcp__*
---
Run all tests using `mcp__xcodebuildmcp__test_sim_name_proj`.
Summarize results: total, passed, failed.
For failures show test name, expected vs actual, and a fix.
EOF
5d — Create the checkpoint command
cat > .claude/commands/checkpoint.md << 'EOF'
---
description: Save current progress with a git commit
---
Run: git add -A && git commit -m "checkpoint: [brief description]"
Summarize what was committed.
EOF
5e — Verify and commit
ls -la .claude/commands/
# Should show: build.md test.md checkpoint.md
git add .claude/
git commit -m "add Claude Code slash commands"
iOS Simulator Skill
Adds deeper simulator interaction — tapping UI elements, accessibility audits, visual testing. Skip this if you only need Claude for code generation and builds.
# Create skills directory
mkdir -p ~/.claude/skills
# Clone the skill
git clone https://github.com/conorluddy/ios-simulator-skill.git \
~/.claude/skills/ios-simulator-skill
# Verify
ls ~/.claude/skills/ios-simulator-skill/SKILL.md
Restart Claude Code. The skill loads automatically. Test it by asking:
Run a health check on my iOS simulator setup
Daily Workflow
Here's how to use everything together, every day.
7a — Start on a dedicated branch
# 1. Navigate to your project
cd ~/path/to/your/ios-project
# 2. Create a feature branch (isolate Claude's work)
git checkout -b claude/profile-view
# 3. Start a safe Claude session
clauded
main.
7b — Give a well-scoped task
Add a ProfileView with name, email, and avatar using SwiftUI.
Follow the existing MVVM pattern in the project.
Create a ProfileViewModel with @Published properties.
Write unit tests for the ViewModel.
Shift+Tab twice to enter plan mode. Claude will explore your codebase and propose an implementation plan before writing any code. Approve or refine the plan first, then let it execute.
7c — Review Claude's changes
# See what files changed
git status
# See actual changes line by line
git diff HEAD
# If everything looks good — commit and merge
git add -A && git commit -m "feat: add ProfileView"
git checkout main && git merge claude/profile-view
# If something went wrong — undo EVERYTHING
git checkout main && git branch -D claude/profile-view
7d — iOS code review checklist
When reviewing Claude's Swift code, watch for these common issues:
- Force-unwrapped optionals (
!) — preferguard letorif let - Missing
@MainActoron ViewModels that update UI state - Incorrect access control —
publicon things that should beinternal - Overly complex SwiftUI view bodies — should be broken into subviews
- Unused
importstatements Claude added unnecessarily - Completion handlers where
async/awaitshould be used - Missing error handling in
do/catchblocks - New files that need to be added to the Xcode project manually
7e — Managing context in long sessions
Large iOS projects can fill Claude's context window quickly. Tips to manage this:
| Technique | How |
|---|---|
| Compact context | Type /compact to summarize and free up context mid-session |
| Scope to modules | Ask Claude to focus on one module/feature at a time, not the whole project |
| Multi-session workflow | Break large features into multiple sessions — each starts fresh with full context |
| Use subagents | Claude automatically spawns Explore subagents to search without filling your main context |
VSCode DevContainer
If you want full container-level isolation — where Claude literally cannot touch your host system — use this Docker-based approach with VS Code. This is the safest option but comes with a trade-off: you lose native Xcode/Simulator integration inside the container.
Anthropic's Official DevContainer
# Install Docker Desktop from docker.com
npm install -g @devcontainers/cli
# In VS Code: Cmd+Shift+X → "Dev Containers" → Install
cd ~/path/to/your/ios-project
mkdir -p .devcontainer
cat > .devcontainer/devcontainer.json << 'DEVEOF'
{
"name": "Claude Code iOS Sandbox",
"image": "mcr.microsoft.com/devcontainers/javascript-node:20",
"features": {
"ghcr.io/devcontainers/features/git:1": {}
},
"postCreateCommand": "npm install -g @anthropic-ai/claude-code",
"mounts": [
"source=${localEnv:HOME}/.claude,target=/home/node/.claude,type=bind",
"source=${localEnv:HOME}/.claude.json,target=/home/node/.claude.json,type=bind"
],
"forwardPorts": [],
"customizations": {
"vscode": { "extensions": ["anthropic.claude-code"] }
}
}
DEVEOF
# VS Code: Cmd+Shift+P → "Reopen in Container"
# Or via CLI:
devcontainer up --workspace-folder .
devcontainer exec --workspace-folder . claude --dangerously-skip-permissions
Trail of Bits DevContainer (Maximum Security)
Purpose-built for running Claude Code in bypass mode safely.
# Install the devc tool
npm install -g @devcontainers/cli
git clone https://github.com/trailofbits/claude-code-devcontainer ~/.claude-devcontainer
~/.claude-devcontainer/install.sh self-install
# Set up, start, and run
cd ~/path/to/your/ios-project
devc .
devc up
devc shell
claude --dangerously-skip-permissions
Quick Docker One-Liner
Minimal config. Filesystem sandboxed to mounted workspace.
docker run -it --rm \
-v $(pwd):/workspace \
-v ~/.claude:/root/.claude \
-v ~/.claude.json:/root/.claude.json \
node:20 \
bash -c "npm i -g @anthropic-ai/claude-code && cd /workspace && claude --dangerously-skip-permissions"
--network none: Claude Code requires network access to the Anthropic API. The container already provides filesystem isolation.
DevContainer Comparison
| Approach | Setup Time | Security | Xcode Integration |
|---|---|---|---|
| Native + guardrails (Steps 1–7) | 45 min | Good | Full |
| Option A — VS Code DevContainer | 15 min | Strong | Split workflow |
| Option B — Trail of Bits | 20 min | Maximum | Split workflow |
| Option C — Docker one-liner | 2 min | Strong | Split workflow |
Config Builders
Generate customized configuration files for your iOS project.
CLAUDE.md Builder
Fill in your project details and get a ready-to-use CLAUDE.md file.
Quick Reference
| Command | What It Does |
|---|---|
clauded | Start a safe Claude session (auto-checkpoints first) |
git reset --hard HEAD | Undo everything Claude did since checkpoint |
git diff HEAD | Review what Claude changed |
git checkout -b claude/feature | Create isolated branch for Claude's work |
/build | Build the project (inside session) |
/test | Run all tests (inside session) |
/checkpoint | Save progress as git commit (inside session) |
/compact | Summarize context to free up space (inside session) |
Shift+Tab (x2) | Enter plan mode for complex tasks (inside session) |
/exit | Leave a Claude Code session |
source ~/.zshrc | Reload shell config after editing |
open ~/.claude/settings.json | Edit permissions and hooks |
open CLAUDE.md | Edit project guardrails |
Troubleshooting
Your shell didn't load the alias. Run:
source ~/.zshrc
# Still not found? Check the alias is in the file:
grep "clauded" ~/.zshrc
# If nothing prints, the alias wasn't saved. Repeat Step 1.
Open the file and check for common mistakes:
open ~/.claude/settings.json
Common issues: missing comma after a line, extra comma after the last item in a list, mismatched brackets or quotes.
Make sure you installed it with claude mcp add (Step 4b), not just npm. Close your Claude session and open a new one — the MCP server loads on session start.
Some commands may not match the whitelist patterns exactly. Add them:
open ~/.claude/settings.json
# Add the command to the "allow" array, e.g.:
# "Bash(swiftlint:*)"
# Save and restart Claude Code.
Rare but possible. This is exactly why Step 1 exists:
git reset --hard HEAD
Then add a stronger instruction to your CLAUDE.md. You can also add a deny rule: "Edit(**/*.pbxproj)"
Make sure the mount paths are correct. The ~/.claude directory and ~/.claude.json file must exist on your host before mounting:
ls ~/.claude.json # Should exist
ls ~/.claude/ # Should exist
# If missing, log in to Claude first outside the container:
claude
Check that your hooks are in the correct location and have the right structure:
# Validate your settings.json
python3 -c "import json; json.load(open('$HOME/.claude/settings.json')); print('Valid JSON')"
# Make sure hooks key is at the top level, alongside permissions
# NOT nested inside permissions
Also verify the hook command works standalone: copy the "command" value and run it directly in terminal with a test input.
Large iOS projects fill context quickly. Solutions:
# Inside a Claude session:
/compact # Summarize and free context
# Or start a fresh session for the next subtask
# Claude reads CLAUDE.md at session start, so context resets cleanly
Tip: scope tasks to one module at a time. "Add login to the Auth module" is better than "refactor the whole app."
Xcode 26.3 Agentic Coding
Xcode 26.3 (February 2026) introduced agentic coding — a major leap beyond autocomplete and chat. Agents can explore your project, build, run tests, fix errors, and capture Xcode Previews for visual verification.
What's new in Xcode 26.3
| Feature | Details |
|---|---|
| Claude Agent SDK | Native integration — the same engine powering Claude Code, now built into Xcode |
| OpenAI Codex | Also supported alongside Claude |
| Agent Activity pane | Transparent log of every file accessed, command run, and decision made |
| MCP support | Xcode exposes capabilities via MCP — XcodeBuildMCP auto-detects scheme and simulator |
| One-click setup | Xcode Settings → Intelligence → add your API key and select a model |