Adding an iOS App Clip target

This guide describes how to manually add another Flutter-rendering iOS App Clip target to your existing Flutter project or add-to-app project.

To see a working sample, see the App Clip sample on GitHub.

Step 1 - Open project

Open your iOS Xcode project, such as ios/Runner.xcworkspace for full-Flutter apps.

Step 2 - Add an App Clip target

2.1

Click on your project in the Project Navigator to show the project settings.

Press + at the bottom of the target list to add a new target.

<img src=/flutter_website/assets/images/docs/development/platform-integration/ios-app-clip/add-target.png class='' alt=''>

2.2

Select the App Clip type for your new target.

<img src=/flutter_website/assets/images/docs/development/platform-integration/ios-app-clip/add-app-clip.png class='' alt=''>

2.3

Enter your new target detail in the dialog.

Select Storyboard for Interface.

Select UIKit App Delegate for Life Cycle.

Select the same language as your original target for Language.

(In other words, to simplify the setup, don’t create a Swift App Clip target for an Objective-C main target, and vice versa.)

<img src=/flutter_website/assets/images/docs/development/platform-integration/ios-app-clip/app-clip-details.png class='' alt=''>

2.4

In the following dialog, activate the new scheme for the new target.

<img src=/flutter_website/assets/images/docs/development/platform-integration/ios-app-clip/activate-scheme.png class='' alt=''>

Step 3 - Remove unneeded files

3.1

In the Project Navigator, in the newly created App Clip group, delete everything except Info.plist and <app clip target>.entitlements.

<img src=/flutter_website/assets/images/docs/development/platform-integration/ios-app-clip/clean-files.png class='' alt=''>

Move files to trash.

3.2

If you don’t use the SceneDelegate.swift file, remove the reference to it in the Info.plist.

Open the Info.plist file in the App Clip group. Delete the entire dictionary entry for Application Scene Manifest.

<img src=/flutter_website/assets/images/docs/development/platform-integration/ios-app-clip/scene-manifest.png class='' alt=''>

Step 4 - Share build configurations

This step isn’t necessary for add-to-app projects since add-to-app projects have their custom build configurations and versions.

4.1

Back in the project settings, select the project entry now rather than any targets.

In the Info tab, under the Configurations expandable group, expand the Debug, Profile, and Release entries.

For each, select the same value from the drop-down menu for the App Clip target as the entry selected for the normal app target.

This gives your App Clip target access to Flutter’s required build settings.

<img src=/flutter_website/assets/images/docs/development/platform-integration/ios-app-clip/configuration.png class='' alt=''>

4.2

In the App Clip group’s Info.plist file, set:

  • Build version string (short) to $(FLUTTER_BUILD_NAME)
  • Bundle version to $(FLUTTER_BUILD_NUMBER)

Step 5 - Share code and assets

Option 1 - Share everything

Assuming the intent is to show the same Flutter UI in the standard app as in the App Clip, share the same code and assets.

For each of the following: Main.storyboard, Assets.xcassets, LaunchScreen.storyboard, GeneratedPluginRegistrant.m, and AppDelegate.swift (and Supporting Files/main.m if using Objective-C), select the file, then in the first tab of the inspector, also include the App Clip target in the Target Membership checkbox group.

<img src=/flutter_website/assets/images/docs/development/platform-integration/ios-app-clip/add-target-membership.png class='' alt=''>

Option 2 - Customize Flutter launch for App Clip

In this case, do not delete everything listed in Step 3. Instead, use the scaffolding and the iOS add-to-app APIs to perform a custom launch of Flutter. For example to show a custom Flutter route.

Step 6 - Add App Clip associated domains

This is a standard step for App Clip development. See the official Apple documentation.

6.1

Open the <app clip target>.entitlements file. Add an Associated Domains Array type. Add a row to the array with appclips:<your bundle id>.

<img src=/flutter_website/assets/images/docs/development/platform-integration/ios-app-clip/app-clip-entitlements.png class='' alt=''>

6.2

The same associated domains entitlement needs to be added to your main app, as well.

Copy the <app clip target>.entitlements file from your App Clip group to your main app group and rename it to the same name as your main target such as Runner.entitlements.

Open the file and delete the Parent Application Identifiers entry for the main app’s entitlement file (leave that entry for the App Clip’s entitlement file).

<img src=/flutter_website/assets/images/docs/development/platform-integration/ios-app-clip/main-app-entitlements.png class='' alt=''>

6.3

Back in the project settings, select the main app’s target, open the Build Settings tab. Set the Code Signing Entitlements setting to the relative path of the second entitlements file created for the main app.

<img src=/flutter_website/assets/images/docs/development/platform-integration/ios-app-clip/main-app-entitlements-setting.png class='' alt=''>

Step 7 - Integrate Flutter

These steps are not necessary for add-to-app.

7.1

In your App Clip’s target’s project settings, open the Build Settings tab.

For setting Framework Search Paths, add 2 entries:

  • $(inherited)
  • $(PROJECT_DIR)/Flutter

In other words, the same as the main app target’s build settings.

<img src=/flutter_website/assets/images/docs/development/platform-integration/ios-app-clip/app-clip-framework-search.png class='' alt=''>

7.2

For the Swift target, set the Objective-C Bridging Header build setting to Runner/Runner-Bridging-Header.h

In other words, the same as the main app target’s build settings.

<img src=/flutter_website/assets/images/docs/development/platform-integration/ios-app-clip/bridge-header.png class='' alt=''>

7.3

Now open the Build Phases tab. Press the + sign and select New Run Script Phase.

<img src=/flutter_website/assets/images/docs/development/platform-integration/ios-app-clip/new-build-phase.png class='' alt=''>

Drag that new phase to below the Dependencies phase.

Expand the new phase and add this line to the script content:

/bin/sh "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build

In other words, the same as the main app target’s build phases.

<img src=/flutter_website/assets/images/docs/development/platform-integration/ios-app-clip/xcode-backend-build.png class='' alt=''>

This ensures that your Flutter Dart code is compiled when running the App Clip target.

7.4

Press the + sign and select New Run Script Phase again. Leave it as the last phase.

This time, add:

/bin/sh "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed_and_thin

In other words, the same as the main app target’s build phases.

<img src=/flutter_website/assets/images/docs/development/platform-integration/ios-app-clip/xcode-backend-embed.png class='' alt=''>

This ensures that your Flutter app and engine are embedded into the App Clip bundle.

Step 8 - Integrate plugins

8.1

Open the Podfile for your Flutter project or add-to-app host project.

For full-Flutter apps, replace the following section:

target 'Runner' do
  use_frameworks!
  use_modular_headers!

  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end

with:

use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))

target 'Runner'
target '<name of your App Clip target>'

At the top of the file, also uncomment platform :ios, '11.0' and set the version to the lowest of the two target’s iOS Deployment Target.

For add-to-app, add to:

target 'MyApp' do
  install_all_flutter_pods(flutter_application_path)
end

with:

target 'MyApp' do
  install_all_flutter_pods(flutter_application_path)
end

target '<name of your App Clip target>'
  install_all_flutter_pods(flutter_application_path)
end

8.2

From the command line, enter your Flutter project directory and then install the pod:

cd ios
pod install

Run

You can now run your App Clip target from Xcode by selecting your App Clip target from the scheme drop-down, selecting an iOS 14 device and pressing run.

<img src=/flutter_website/assets/images/docs/development/platform-integration/ios-app-clip/run-select.png class='' alt=''>

To test launching an App Clip from the beginning, also consult Apple’s doc on Testing Your App Clip’s Launch Experience.

Debugging, hot reload

Unfortunately flutter attach cannot auto-discover the Flutter session in an App Clip due to networking permission restrictions.

In order to debug your App Clip and use functionalities like hot reload, you must look for the Observatory URI from the console output in Xcode after running. [PENDING: Is this still true?]

<img src=/flutter_website/assets/images/docs/development/platform-integration/ios-app-clip/observatory-uri.png class='' alt=''>

You must then copy paste it back into the flutter attach command to connect.

For example:

flutter attach --debug-uri <copied URI>