Ios dev
Custom skills and prompts for Claude Code based on my personal workflows.
npx -y skills add Sean-Michael/skills --skill ios-devAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use for any mobile app task: building, modifying, testing, or deploying with Expo + React Native. Triggers: mobile app, iOS app, React Native, Expo, simulator, TestFlight, App Store, expo run, app.json. Core rule: never hand off without completing the validation loop. The human's only job is UI/UX feel.
SKILL.md
6.8 KB, as published. Nobody here has run it
iOS Development Skill (Expo + React Native)
Stack
- React Native via Expo SDK (latest stable), TypeScript strict
- Build:
npx expo run:ios(local builds, no EAS) - Navigation: Expo Router (preferred)
- Backend: Supabase (always — see supabase/postgres skills for schema/RLS/migrations)
- State: Zustand + React Query
- Styling: NativeWind or StyleSheet — pick one per project, stay consistent
Project Setup
npx create-expo-app@latest <app-name> --template blank-typescript
npx expo install expo-router react-native-safe-area-context react-native-screens
Set "strict": true in tsconfig. Configure app.json bundleIdentifier, version: "1.0.0", ios.buildNumber: "1". Install all required libraries before writing features.
Version discipline — Apple rejects without this: version is semver, bump manually per App Store release. ios.buildNumber increments per TestFlight upload. Expo never auto-bumps either.
Supabase Wiring (RN-specific only)
The supabase skill handles everything else. These three things differ from web:
1. Install
npx expo install @supabase/supabase-js @react-native-async-storage/async-storage \
expo-secure-store expo-auth-session expo-web-browser
2. Client config — SecureStore adapter + detectSessionInUrl: false (breaks auth without it):
import { createClient } from '@supabase/supabase-js'
import * as SecureStore from 'expo-secure-store'
export const supabase = createClient(
process.env.EXPO_PUBLIC_SUPABASE_URL!, // EXPO_PUBLIC_ prefix required
process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY!,
{
auth: {
storage: { getItem: SecureStore.getItemAsync, setItem: SecureStore.setItemAsync, removeItem: SecureStore.deleteItemAsync },
autoRefreshToken: true, persistSession: true,
detectSessionInUrl: false,
},
}
)
3. OAuth — web redirect flow doesn't work in RN:
import * as AuthSession from 'expo-auth-session'
import * as WebBrowser from 'expo-web-browser'
WebBrowser.maybeCompleteAuthSession()
const redirectUri = AuthSession.makeRedirectUri({ scheme: 'your-scheme' })
const { data } = await supabase.auth.signInWithOAuth({
provider: 'google',
options: { redirectTo: redirectUri, skipBrowserRedirect: true },
})
if (data.url) await WebBrowser.openAuthSessionAsync(data.url, redirectUri)
Register scheme in app.json. Register redirect URI in Supabase dashboard → Auth → URL Configuration.
Storage uploads — direct URI doesn't work, read via expo-file-system as base64 first.
Targeting a Simulator by Name
npx expo run:ios --simulator "iPhone 17 Pro" # simulator — use --simulator, NOT --device
# --device targets physical devices and requires code signing
Boot + open:
xcrun simctl boot "iPhone 17 Pro" 2>/dev/null || true && open -a Simulator
Simulator Interaction
xcrun simctl io booted tap does NOT exist. Do not attempt it.
Options for driving UI without touch injection:
1. Debugger-driven navigation (preferred for validation)
Wire up a globalThis.__navigateTo helper in __DEV__ so the Metro debugger can drive navigation and read/write Zustand state without UI interaction:
// App.tsx or root layout — DEV only
import { createNavigationContainerRef } from '@react-navigation/native';
export const navigationRef = createNavigationContainerRef<any>();
if (__DEV__) {
(globalThis as any).__navigateTo = (name: string) => {
if (navigationRef.isReady()) navigationRef.navigate(name as never);
};
}
Connect from Node.js:
const WebSocket = require('ws');
// get device id: GET http://localhost:8081/json
const ws = new WebSocket('ws://localhost:8081/inspector/debug?device=<id>&page=1');
ws.on('open', () => {
ws.send(JSON.stringify({
id: 1, method: 'Runtime.evaluate',
params: { expression: '__navigateTo("ProfileScreen")' }
}));
});
Use Runtime.evaluate to call __navigateTo, read/write Zustand stores, assert UI state.
2. xcrun simctl io booted screenshot <path> — always works, any process:
xcrun simctl io booted screenshot /tmp/sim.png
3. Physical mouse clicks on Simulator — only when Simulator is frontmost app.
4. xcrun simctl io booted sendPasteboard + keyboard shortcut — for text input.
Validation Loop
Run after every feature. Do not declare done until all pass.
npx tsc --noEmit # must be clean
npx expo-doctor # must be clean
xcrun simctl boot "iPhone 17 Pro" 2>/dev/null || true && open -a Simulator
npx expo run:ios --simulator "iPhone 17 Pro"
For each screen: navigate (via debugger or manual click) → screenshot → verify renders → exercise interactive elements → screenshot → verify result. Check empty/loading/error states.
xcrun simctl io booted screenshot /tmp/sim.png
Repeat on iPhone SE:
xcrun simctl boot "iPhone SE (3rd generation)" 2>/dev/null || true
npx expo run:ios --simulator "iPhone SE (3rd generation)"
Sign-off checklist:
-
tsc --noEmitclean -
expo-doctorclean - Launches without crash on iPhone 17 Pro and iPhone SE sims
- Every screen renders correctly (screenshot verified)
- Every interactive element works (tap + screenshot verified, or debugger-driven)
- No Metro console errors
- Auth flow works: sign in, protected route gating, sign out
- Data screens load real Supabase data
Footguns
- Native rebuild: adding a native-code library requires re-running
expo run:ios, not just restarting Metro. Same applies to changes tobabel.config.js— module IDs change at the native boundary. Rule: anything that affects the Metro module graph at the native boundary needs a full rebuild. --simulatorvs--device: use--simulator "iPhone 17 Pro"to target a sim by name.--devicetargets physical devices and will trigger code signing prompts if one is paired.- Safe area: root needs
<SafeAreaProvider>, screens need<SafeAreaView>— missing = content under notch - Keyboard: text input screens need
<KeyboardAvoidingView behavior="padding"> - Expo Go: some libraries don't work in Expo Go; always use
expo run:iosdev builds - babel-preset-expo: use
babel-preset-expo, not@react-native/babel-preset— the latter causes crashes
References
references/eas-testflight.md— TestFlight/App Store submission when readyreferences/common-libraries.md— curated Expo-compatible library listreferences/plugin-architecture.md— recommended structure for extensible apps