Skip to content

Code Push for iOS Hybrid Apps

This guide explains how to use Shorebird in an iOS hybrid app scenario (that is, your app embeds Flutter UI in non-Flutter UI).


This guide assumes you have already have an iOS app and a Flutter module. Our iOS app will be named IosCodePushDemo and our Flutter module will be named flutter_module.

This guide also assumes that you have created a Shorebird account. If you have not created a Shorebird account, please see our code push guide for instructions.

The reference code for this guide is available at

Add Shorebird to your Flutter module

First, run shorebird init in your Flutter module:

Terminal window
shorebird init

Create a Shorebird release

First, we need to package our Flutter module for release. This will produce an .xcframework that we can embed in our iOS app and provide Shorebird with the information it needs to apply patches.

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

Terminal window
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

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.

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.

The steps to do this are the same as the option B in the official instructions, so in the event of a conflict between the docs here and the official docs, defer to the official docs.

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

Embed App.xcframework and Flutter.xcframework in your app

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

Xcode embed frameworks

Verify that your app runs

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

Xcode edit schemes Xcode release scheme

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

We won’t cover this step 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:

Terminal window
shorebird patch ios-framework --release-version 1.2.3+4

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 we 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 a logs from Shorebird informing you that the app is checking for new patches and saying that the patch was installed:

Sending patch check request: PatchCheckRequest { app_id: "db32f785-284a-429b-9348-d3ead3485438", channel: "stable", release_version: "1.2.3+4", patch_number: None, platform: "ios", arch: "aarch64" }
/// other logs
Patch 1 successfully installed.

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.