---
title: "Code Push for iOS add-to-app"
description: "Use Code Push in an add-to-app scenario with an iOS app"
---

This guide explains how to use Shorebird in an iOS add-to-app scenario (that is, your app embeds Flutter UI in non-Flutter UI). If you are new to this pattern, see the [Add Flutter to an existing app](/flutter-concepts/add-to-app/) Flutter Concepts article first.

Note

If your app is a pure Flutter app, follow the [standard Code Push guide](/code-push/initialize) instead.

## Prerequisites

This guide assumes you already have an iOS app and a Flutter module. The example iOS app will be named `IosAddToAppDemo` and the example Flutter module will be named `my_flutter_module`.

This guide also assumes that you have created a Shorebird account. If you have not created a Shorebird account, please see the [Code Push guide](/code-push/initialize) for instructions.

The reference code for this guide is available at <https://github.com/shorebirdtech/samples/tree/main/add_to_app>.

Note

Shorebird only supports the iOS Framework embedding method of adding Flutter to a native iOS app (see “Link and Embed frameworks in Xcode” in Flutter’s [legacy integration docs](https://docs.flutter.dev/add-to-app/ios/project-setup-legacy#method-b)). It doesn’t support the Swift Package Manager integration that Flutter has recommended since Flutter 3.44. There is an open issue to support embedding via CocoaPods ([link](https://github.com/shorebirdtech/shorebird/issues/1198)), but given that [Flutter plans to deprecate CocoaPods support](https://github.com/flutter/flutter/issues/168015), it is unlikely that this will be implemented.

## Add Shorebird to your Flutter module

First, run `shorebird init` in your Flutter module:

```
shorebird init
```

## Create a Shorebird release

First, package the Flutter module for release. This will produce an `.xcframework` that can be embedded in your iOS app and provides Shorebird with the information it needs to apply patches.

To create a release, run the following in the root directory of your Flutter module:

```
shorebird release ios-framework --release-version 1.2.3+4
```

The `release-version` parameter needs to match the version of the iOS app that uses this module (i.e., `version+build` from the Xcode settings, or `CFBundleShortVersionString+CFBundleVersion` from your app’s Info.plist).

![Xcode build version](/_astro/hybrid_ios_build_version.CLljq76e_thYgj.webp)

*The version number for this app would be 1.2.3+4*

The `ShorebirdFlutter.xcframework` and `App.xcframework` artifacts produced by the `shorebird release` command will be placed in the `release` directory in the root of your Flutter module.

Note

Because Shorebird only works with release builds, this will only produce a release version of your archive. This is similar to running `flutter build ios-framework --no-debug --no-profile`.

## Embed the Flutter module in your iOS app

While there are multiple ways to embed a Flutter module in an iOS app, Shorebird requires that your Flutter module be embedded in your iOS app as an .xcframework.

Note

The steps to do this are the same as Flutter’s [Link and Embed frameworks in Xcode](https://docs.flutter.dev/add-to-app/ios/project-setup-legacy#method-b) instructions, so in the event of a conflict between the docs here and the official docs, defer to the official docs.

Note

`ShorebirdFlutter.xcframework` is nearly identical to `Flutter.xcframework` from Google, but as part of compliance with Apple’s signing requirements, the framework has been renamed when applying Shorebird’s (legally Code Town, Inc’s) digital signature.

### Add the path to your .xcframeworks to Framework Search Paths

In Xcode:

1. Navigate to your app target’s “Build Settings” tab.
2. Find the “Framework Search Paths” setting (the Filter field on the top right of the build settings tab is helpful for this).
3. Add an entry to the “Framework Search Paths” list. This entry should be the relative path to the directory containing `ShorebirdFlutter.xcframework` and `App.xcframework` artifacts. By default, these artifacts are placed in the `release` directory in the root of your Flutter module, but you should feel free to move them elsewhere if you prefer.

![Xcode framework search paths](/_astro/hybrid_ios_framework_search_paths.D8dALScx_2vl85C.webp)

### Embed App.xcframework and ShorebirdFlutter.xcframework in your app

In the “General” tab of your app target, add `App.xcframework` and `ShorebirdFlutter.xcframework` to the “Frameworks, Libraries, and Embedded Content” section. Make sure to select “Embed & Sign” for both frameworks.

![Xcode embed frameworks](/_astro/hybrid_ios_embed_frameworks.Ck1H8aSI_Z7DL6W.webp)

Note

You may also see that `ShorebirdFlutter.xcframework` is signed by Code Town Inc. That’s the Shorebird team 🙂

## Verify that your app runs

In Xcode, update the current scheme’s build configuration to “Release”

![Xcode edit schemes](/_astro/hybrid_ios_edit_schemes.CnGS09JC_ZwcyOE.webp) ![Xcode release scheme](/_astro/hybrid_ios_release_scheme.DBc7Z2UN_1ncL2V.webp)

Now run your app on a device (*not* a simulator). Your app should run as normal, and you should see debug logs from Shorebird.

## Submit your app to the App Store

This step isn’t covered in detail here, but this is where you would submit your app to the App Store. For Code Push to work, it is important that you submit *with the same `xcframework` generated by the release command above*.

You can skip this step if you just want to see Code Push working in an app.

## Verify that Shorebird is working with a patch

Make a user-visible change to the code in your Flutter module. Then run:

```
shorebird patch ios-framework --release-version 1.2.3+4 --track=stable
```

`release-version` should be the version of the iOS app you released with the output of the `release` command. As before, this should match your app’s version and build numbers in Xcode. The command above will patch the release created earlier in this guide.

You can now test the patch in your app by running the app from Xcode. After the app launches, you should see logs from Shorebird informing you that the app is checking for new patches and saying that the patch was downloaded:

```
[shorebird] Sending patch check request: app_id: db32f785-284a-429b-9348-d3ead3485438, channel: stable, release_version: 1.2.3+4, platform: ios, arch: aarch64, current_patch_number: None

/// other logs

[shorebird] Patch 1 successfully downloaded. It will be launched when the app next restarts.
```

For the app to reflect the changes, you will need to close and reopen the app. Do this by stopping the app in Xcode and then running it again.

## Release and patch the module from CI

In CI, the module and the host app are usually built by different jobs. Keep the module’s `release` directory from the job that released it, and build the host app you ship from that directory:

1. The release job runs `shorebird release ios-framework --release-version 1.2.3+4` in the module on a macOS runner, and keeps the `release` directory it writes, for example as a build artifact.
2. The host app’s store build embeds `App.xcframework` and `ShorebirdFlutter.xcframework` from that `release` directory. Don’t rebuild the module for the store build: patches install only on the frameworks that `shorebird release ios-framework` built.
3. A patch job runs `shorebird patch ios-framework --release-version 1.2.3+4 --track=stable` in the module on a macOS runner. It doesn’t need the `release` directory; Shorebird downloads what it needs from the release.

Use the same Xcode version for the release and its patches, and pin the runner image rather than using a “latest” label; see [Build patches with the same toolchain as the release](/code-push/ci/generic/#build-patches-with-the-same-toolchain-as-the-release).

The job needs `SHOREBIRD_TOKEN`; see the [CI guides](/code-push/ci/generic/). With GitHub Actions, the Shorebird actions take `platform: ios-framework`. The release version goes in `args` for a release and in `release-version` for a patch:

```
- name: 🚀 Release the module
  uses: shorebirdtech/shorebird-release@v1
  with:
    flutter-version: 3.47.9
    platform: ios-framework
    working-directory: my_flutter_module
    args: --release-version 1.2.3+4

- name: 📦 Keep the module's release directory
  uses: actions/upload-artifact@v7
  with:
    name: my_flutter_module-release
    path: my_flutter_module/release
```

```
- name: 🚀 Patch the module
  uses: shorebirdtech/shorebird-patch@v1
  with:
    platform: ios-framework
    release-version: 1.2.3+4
    track: stable
    working-directory: my_flutter_module
```

See [GitHub Integration](/code-push/ci/github/#pin-the-runner-and-xcode-for-ios) for the runner and Xcode setup.

## Test patches before publishing them

To try a patch in a host app you build yourself before your users get it, see [Test an add-to-app patch before publishing it](/code-push/guides/add-to-app/testing-patches).

To give QA a build of your host app that gets patches before your users do, see [Give QA an add-to-app build, then promote it](/code-push/guides/add-to-app/qa-builds).

## Multiple Dart entrypoints

If your host app starts the module through more than one Dart entrypoint or engine, see [Multiple Dart entrypoints and engines](/code-push/guides/add-to-app/multiple-entrypoints).
