In a bare React Native iOS project, what does pod install do, and why must you open the .xcworkspace afterwards?
answer
- a Ruby gem reading ios/Podfile
- use_react_native! pulls the React pods
- Podfile.lock pins resolved versions
- workspace joins app project and Pods project
- rerun after any native dependency change
basics
~20 spod install reads ios/Podfile, resolves React Native's pods and every native library's pods, writes Podfile.lock and generates a Pods project plus an .xcworkspace that joins it to the app project, so Xcode must build the workspace.
solid answer
~40 sCocoaPods is the iOS dependency manager a bare React Native app uses by default. `pod install` evaluates `ios/Podfile`, whose React Native helpers (`prepare_react_native_project!`, `use_native_modules!`, `use_react_native!`, `react_native_post_install`) add the React Native core pods, the pods of every autolinked library and the generated `ReactCodegen` pod. It records the exact resolved versions in `Podfile.lock`, builds a separate `Pods` Xcode project, and writes an `.xcworkspace` that contains both your app project and `Pods`. Only the workspace lets Xcode see the libraries, so opening the `.xcodeproj` alone fails with missing React headers. You rerun it whenever a native dependency is added, removed or upgraded, not when JavaScript changes.
code
bash · 5 linesnpm install
cd ios
bundle install
bundle exec pod install
open *.xcworkspacego deeper
Recall that pod install reads the Podfile, writes Podfile.lock and the Pods project, and that you open the .xcworkspace from then on.
Explain what the React Native Podfile helpers add: core pods, autolinked library pods, the Codegen pod and the Xcode minimum check, and why npm install must come first.
Show you keep native builds reproducible: committed Podfile.lock, Gemfile-pinned CocoaPods via bundle exec, and a clear rule for when CI must rerun pod install.
Weigh staying on CocoaPods against piloting the experimental SwiftPM path, given library support and the team's release risk.
## What CocoaPods is **CocoaPods** is a dependency manager for iOS projects, distributed as a Ruby gem. A project declares its dependencies in a `Podfile`; each dependency is a **pod**, described by a `.podspec`. Running `pod install` in the `ios/` folder of a bare React Native app resolves those pods, downloads or links their sources, and wires them into Xcode. In React Native 0.87, CocoaPods is still the **default and supported** iOS integration. The release adds an experimental Swift Package Manager path (`npx react-native spm`), but the release post says CocoaPods remains the default and the SwiftPM path is not for production yet. ## What the React Native Podfile adds A React Native Podfile is ordinary Ruby that calls helper functions shipped inside `node_modules/react-native/scripts/`: - `platform :ios, min_ios_version_supported` sets the deployment target to React Native's minimum, **iOS 15.1** in 0.87. - `prepare_react_native_project!` prepares the folder, including creating `.xcode.env` and `.xcode.env.local` if they are missing. - `use_native_modules!` asks the autolinking step which installed npm packages contain native iOS code, and adds their pods. - `use_react_native!` adds React Native's own pods, checks that Xcode is at least the supported minimum (**16.1** in 0.87), and sets up **Codegen**, whose output becomes a generated `ReactCodegen` pod. - `react_native_post_install` runs after resolution to patch build settings on the generated projects. Because these helpers live in `node_modules`, the JavaScript dependencies must be installed with npm, Yarn or pnpm **before** `pod install` can work. ## What pod install produces 1. **`Podfile.lock`** records the exact version of every resolved pod. Commit it, so every machine and CI job resolves the same graph. 2. **`ios/Pods/`** holds the pod sources and a generated `Pods.xcodeproj`. The React Native template's `.gitignore` excludes it, because it is regenerated. 3. **`<App>.xcworkspace`** is a workspace that references both `<App>.xcodeproj` and `Pods.xcodeproj`. ## Why the workspace, not the project An Xcode **project** describes one set of targets. An Xcode **workspace** groups several projects so that one can build and link against another. Your app target depends on libraries that exist only in the `Pods` project, so opening `<App>.xcodeproj` on its own gives Xcode no way to find React Native's headers or frameworks, and the build fails. The React Native docs make the same point: once CocoaPods is in use, open the `.xcworkspace`. | You open | What Xcode sees | Result | |---|---|---| | `<App>.xcworkspace` | app project plus `Pods` project | builds | | `<App>.xcodeproj` | app project only | missing React headers and libraries | ## When to rerun it Run `pod install` again when the **native** dependency graph changes: - after adding, removing or upgrading an npm package that contains iOS native code; - after upgrading React Native itself; - after pulling a change that edited `Podfile` or `Podfile.lock`; - after a clean checkout, because `Pods/` is not committed. Editing TypeScript or JavaScript never requires it: that code is bundled by Metro and reaches the app without touching the native build. ## Bundler and the pinned CocoaPods version The React Native template ships a `Gemfile` that pins the CocoaPods gem (in 0.87: `~> 1.13`, excluding `1.15.0` and `1.15.1`). The docs therefore recommend `bundle install` followed by `bundle exec pod install`, which runs the CocoaPods version the project was tested with instead of whatever is installed globally. When two developers get different `Podfile.lock` diffs from the same Podfile, a different CocoaPods version is a common reason, and Bundler removes it. ## Codegen during pod install `use_react_native!` also prepares **Codegen**, React Native's generator that turns the typed JavaScript specs of native modules and components into native glue code. Its output is written under `ios/build/generated/ios/` and added to the workspace as the `ReactCodegen` pod. Two practical consequences follow: - Codegen runs Node, so `pod install` fails early if Node cannot be found or is outside React Native's engine range. - After adding a library that ships a native spec, the generated code only appears once `pod install` has run again, which is one more reason to rerun it whenever the native dependency graph changes. ## Swift Package Manager, briefly React Native 0.87 adds an **experimental** alternative: `npx react-native spm` injects Swift package references into the existing `.xcodeproj` instead of producing a CocoaPods workspace, and needs only Xcode, not Ruby or Bundler. The release post is explicit that CocoaPods stays the default and supported path, that community libraries must ship a `Package.swift` to take part, and that the SwiftPM path should not be used in production yet. For an interview in 2026, the right answer is still CocoaPods, with SwiftPM named as the direction of travel.
- In a bare React Native app, should Podfile.lock and ios/Pods be committed?Commit `Podfile.lock`: it pins the resolved pod versions so every machine and CI run gets the same native graph. Do not commit `ios/Pods/`; the React Native template ignores it because `pod install` regenerates it from the Podfile and the lock file.
- Why do the React Native docs run bundle exec pod install instead of plain pod install?The template's `Gemfile` pins the CocoaPods gem version. `bundle exec` runs that pinned version rather than a globally installed one, so lock-file changes and resolution behaviour are the same for everyone on the team.
- Is CocoaPods still required for iOS in React Native 0.87?It is the default and supported path. 0.87 adds an experimental Swift Package Manager integration, set up with `npx react-native spm`, which uses the prebuilt XCFrameworks; the release post says not to use it in production yet.
saying these in an interview costs you the question
- pod install also installs the app's npm packages
- Opening the .xcodeproj works the same as opening the workspace
- pod install only needs to run once, when the project is created
- Podfile.lock is generated noise and belongs in .gitignore
- You must rerun pod install after editing a TypeScript screen