# EvanBacon/expo-apple-targets: Native Apple Targets for Expo

> expo-apple-targets is an Expo Config Plugin for generating native Apple targets such as widgets and App Clips while keeping their source files outside the generated ios directory. It suits Expo SDK 53 or newer teams using Xcode 16 on macOS 15 or newer.

**EvanBacon/expo-apple-targets** — Config Plugin to setup Apple targets

- Repository: https://github.com/EvanBacon/expo-apple-targets
- Stars: 1,404 · Forks: 109
- Language: JavaScript
- License: not declared
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/evanbacon-expo-apple-targets

## Why expo-apple-targets exists

expo-apple-targets addresses a specific boundary in Expo development. Expo prebuild can generate native iOS project files, but native targets such as WidgetKit widgets, App Clips, and other Apple extensions may need source that is not represented by the usual React Native application structure. The plugin gives those targets a separate source location and links them into the generated Xcode project.

The intended users are Expo developers who need to add Apple-native code without abandoning the Expo prebuild workflow. The target files live under a root `targets` directory. Developers edit those files in Xcode through the virtual `expo:targets` folder, while the generated `ios` directory remains disposable. This is useful when a project needs native Swift, SwiftUI, WidgetKit, or extension-specific project configuration.

The plugin does not replace Xcode. Xcode remains the environment for building and developing the generated targets. The README specifically describes the plugin as a way to create native Apple targets and link them outside `/ios`, while preserving the option to regenerate the native project with Expo Continuous Native Generation.

## The generated target architecture

The central convention is the root `targets` directory. Each target is a subdirectory containing an `expo-target.config.js` or `expo-target.config.json` file. During prebuild, the plugin reads these configurations, generates the Xcode project, and links each target subdirectory into the project.

A target configuration can define a target type, product name, display name, colors, icon, frameworks, images, and serialized entitlements. The target name defaults to the directory name. Files inside the target directory become part of that target. The plugin can generate an `Info.plist` when one does not exist, although an existing root `Info.plist` remains unmanaged and can be modified.

Assets placed in a top-level `target/{name}/assets` directory are linked as target resources. This rule is documented as support for Safari Extensions. The plugin also supports generated asset catalogs for image sets and SF Symbol template SVGs through its configuration.

Entitlements have a separate data flow. When `entitlements` exists in `expo-target.config`, the plugin writes `ios/.targets/<productName>/generated.entitlements` and points `CODE_SIGN_ENTITLEMENTS` at that file. The generated file is overwritten by prebuild. To manage an entitlements file manually, remove the configuration object and place one top-level `*.entitlements` file in the source target directory. Only one such file is supported per target. If both approaches are present, the configuration-driven file wins and the handwritten file is ignored.

## expo-apple-targets requirements and maintenance

The README sets a high baseline for adoption. The plugin requires CocoaPods 1.16.2, Ruby 3.2.0, Xcode 16, macOS 15 Sequoia, and Expo SDK 53 or newer. These requirements make it a current native Apple development tool rather than a general-purpose Expo package that can run in any existing environment.

The repository is not archived, and the last push was on 2026-10-14. That indicates recent repository activity, although the repository has no GitHub releases. The repository package is marked private and uses `expo` `^55` in its root resolution, while the README directs users to work with Expo SDK 53 or newer. A project should therefore check its actual Expo dependency rather than infer compatibility from the package version.

The repository layout includes `packages/`, `apps/`, `scripts/`, `docs/`, and `skills/`, with a Bun workspace setup. The published plugin is named `@bacons/apple-targets`. The README does not state a release cadence, migration policy, or compatibility policy for every Apple target type. The lack of GitHub releases means users should not assume that tags or release notes provide an upgrade contract.

## Installing and generating a first widget

Start in an existing Expo project. The documented workflow uses the `create-target` CLI to generate the target and install the plugin. The README recommends a widget as the first target, and gives this command:

```bash
npx create-target widget
```

The command should generate the required widget files under the root `/targets` directory, install `@bacons/apple-targets`, and add the Expo Config Plugin to the project. The exact output is not further specified, so the expected confirmation is the generated target directory and the plugin configuration rather than a particular terminal message.

If the Apple Team ID is already known, add it under `ios.appleTeamId` in `app.json`. The README says it can also be set later in Xcode under Signing & Capabilities. Then regenerate the native project:

```bash
npx expo prebuild -p ios --clean
```

Open the generated project with Xcode:

```bash
xed ios
```

In Xcode, locate the target under `expo:targets/<target>`, select the target, and build it. Changes made through this virtual folder are saved outside the generated `ios` directory.

## Editing target configuration safely

The target configuration is the source of truth for settings that the plugin can generate. A minimal JavaScript configuration can define the target type and product name:

```js
/** @type {import('@bacons/apple-targets/app.plugin').Config} */
module.exports = {
  type: "widget",
  name: "my_widget",
  displayName: "My Widget",
};
```

This creates a target named `my_widget` with the display name `My Widget` and adds `SwiftUI` to the target's frameworks. The configuration can also set an icon, colors, images, and serialized entitlements. A target directory may contain native Swift source files and an unmanaged `Info.plist`, so developers can work in Xcode while keeping app-level configuration in the Expo project.

Changes made directly outside the `expo:targets` folder in Xcode can be overwritten by `npx expo prebuild --clean`. Changes to `expo-target.config.js` or `app.json` also require another clean prebuild. This makes the plugin predictable, but it also means Xcode is not the right place to store durable target settings. A developer who needs a persistent native project setting must find a corresponding configuration option or accept that the setting may be lost during regeneration.

## Entitlements, App Clips, and failure modes

Entitlements are a common source of mistakes with this plugin because there are two competing workflows. Configuration-driven entitlements are safest when the values should be reproducible. The generated file belongs under `ios/.targets/<productName>/generated.entitlements`, not in the source target directory, and direct edits to it are overwritten on every `npx expo prebuild`.

For App Clips, the plugin automatically sets `com.apple.developer.parent-application-identifiers` to `$(AppIdentifierPrefix)${config.ios.bundleIdentifier}`. Targets that support App Groups can mirror the `ios.entitlements['com.apple.security.application-groups']` array from `app.json`. A target-level `entitlements['com.apple.security.application-groups']` array can override that mirrored value.

The plugin is a poor fit when native files must be edited directly in the generated Xcode project, because clean prebuild can overwrite changes there. It is also a poor fit for environments that do not meet the documented macOS, Xcode, CocoaPods, Ruby, and Expo SDK requirements. The README does not claim that every target type is interchangeable, so teams should verify support for the specific Apple target they intend to add rather than assuming that “Apple targets” means every native extension.

## How expo-apple-targets differs from manual Xcode targets

The practical alternative is to create and maintain an Xcode target manually. That approach gives the developer direct control over target membership, build phases, signing settings, and entitlements. It can be appropriate for an existing iOS codebase, especially one that does not use Expo prebuild or already has a carefully managed Xcode project.

expo-apple-targets uses a different approach. It treats each target as a source directory outside `ios`, reads a declarative configuration file, and asks prebuild to link that directory into a newly generated Xcode project. The result is compatible with Expo's regeneration model, but it also makes configuration files and clean prebuilds part of the maintenance contract.

Manual Xcode targets do not require the plugin's virtual folder or configuration-driven generation, but they do not receive the plugin's described behavior of regenerating and linking targets during Expo prebuild. Neither approach removes the need to understand Apple's signing, entitlements, target membership, and build settings. The choice is between direct Xcode ownership and configuration-driven regeneration, not between native and non-native development.

## Upgrades, licensing, and adoption boundary

The README does not document a license for this repository or package, so an organization cannot confirm usage rights from the repository files. License status should be checked in the repository's license metadata before copying code into an application or distributing a modified package. This is a repository-level compliance question, not a claim about the plugin's functionality.

Upgrade cost depends on whether the project has moved configuration into `targets` and depends on generated Xcode files. Changes to target configuration and `app.json` require `npx expo prebuild -p ios --clean`. Developers also need to check that CocoaPods, Ruby, Xcode, macOS, and Expo SDK still match the documented requirements. Since there are no GitHub releases, there is no documented release-note migration path to consult.

The main adoption boundary is clear. Teams that need a native Apple target inside an Expo prebuild workflow have a concrete reason to use this plugin. Teams that need persistent manual changes in Xcode, operate on older toolchains, or require documented release and license assurances should resolve those gaps first.

## Conclusion

Adopt expo-apple-targets when an Expo SDK 53 or newer project needs a native Apple target whose source must survive prebuild and remain outside ios. Do not adopt it for unsupported Apple platforms, older toolchains, or workflows that require manual Xcode project changes to persist. Verify the CocoaPods, Ruby, Xcode, macOS, and Expo SDK requirements first, then inspect the generated target and entitlements in Xcode.

## FAQ

### What does EvanBacon/expo-apple-targets do?

It is an Expo Config Plugin that generates native Apple targets such as Widgets and App Clips and links them outside the generated `ios` directory. Target sources remain under the root `targets` directory.

### How do I install expo-apple-targets and create a widget?

The README documents running `npx create-target widget` inside an Expo project. That command generates the target files, installs `@bacons/apple-targets`, and adds the Config Plugin, followed by `npx expo prebuild -p ios --clean`.

### Where should I edit a target generated by expo-apple-targets?

Edit target source through the `expo:targets/<target>` folder in Xcode, with the actual source stored outside `ios`. Changes to `expo-target.config.js` or `app.json` require another clean prebuild.

### Does expo-apple-targets require a specific Apple development environment?

Yes. The README requires CocoaPods 1.16.2, Ruby 3.2.0, Xcode 16, macOS 15 Sequoia, and Expo SDK 53 or newer.

### Can I manage entitlements manually with expo-apple-targets?

You can. Remove the `entitlements` object from `expo-target.config` and add one top-level `*.entitlements` file to the target source directory. The README states that a configuration-driven entitlements block takes precedence if both approaches are present.

## Sources

- [EvanBacon/expo-apple-targets on GitHub](https://github.com/EvanBacon/expo-apple-targets)
- [Issues](https://github.com/EvanBacon/expo-apple-targets/issues)
- [README](https://github.com/EvanBacon/expo-apple-targets/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/evanbacon-expo-apple-targets
