How to change $(PRODUCT_BUNDLE_IDENTIFIER) in Xcode?

FlutterFirebase picture FlutterFirebase · Feb 3, 2019 · Viewed 12.7k times · Source

I am build different flavor of Flutter app with different Firebase environment (development and production). I need set different bundle ID for development and production in Xcode for iOS apps.

I am use schemes to configure the different flavor (in Build Settings I add environment value for every configuration).

But I have big issue with change $(PRODUCT_BUNDLE_IDENTIFIER). I need add suffix .development to normal app id for development app id.

I have try follow this method(use User Defined Settings) and change info.plist to get variable from User Defined Settings but it not work.

Error is:

The operation couldn’t be completed. Application “$(EXAMPLE_BUNDLE_ID)" is unknown to FrontBoard.

So it seem when pass in User Defined Setting it is not interpolate correct.

I have also try mix method of add default PRODUCT_BUNDLE_IDENTIFIER and User Defined Settings. For example: com.example.app$(EXAMPLE_BUNDLE_ID) where EXAMPLE_BUNDLE_ID = .development

I also try reference User Defined Setting $(EXAMPLE_BUNDLE_ID) by direct add it to Bundle Identifier in Target General tab under ‘Identity’. But this then change to : -- EXAMPLE_BUNDLE_ID-

I have also try in info.plist use $(PRODUCT_BUNDLE_IDENTIFIER)$(EXAMPLE_BUNDLE_ID) for Bundle Identifier value. But this give similar error:

The operation couldn’t be completed. Application “com.example.app$(EXAMPLE_BUNDLE_ID)" is unknown to FrontBoard.

Again this look like interpolation issue.

Anyone know solution? I have look but cannot find answer.

This easy for android because just use applicationIdSuffix ".development” in productFlavors. But I cannot find way like this for Xcode.

Answer

shadowsheep picture shadowsheep · Feb 3, 2019

Do you need to have different package name (Android) and bundle id (iOS) because you need to use Firebase Auth plugin?

In this case for iOS project you shold consider using PlistBuddy and you could set it adding a Run Script in your XCode build phases like that

if [ "${CONFIGURATION}" = "Debug" ]; then
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.example.developmento.appName" "$PROJECT_DIR/Runner/Info.plist"
echo "Changed bundle id for developement $PROJECT_DIR/Runner/Info.plist"
else
echo "Nothing to do"
fi

enter image description here

Anyway if you don't use Firebase Auth, you can have the same bundle id in different firebase projects.

If you need then to differenziate firebase projects file between staging and production, you could have a look here:

How to choose between development and production firebase project based on build flavours?

UPDATE

So following OP chat, knowing that he's following this tutorial to setup flutter flavors I've tryed myself to see where we were stuck.

Starting point is the following:

  • Two Firebase project
  • Use of Firebase Auth module (so the need to change the bundle id between projects)
  • And of course two different GoogleService-Info.plist

I start with Xcode bundle id and GoogleService-Info.plist set to production (just an option)

enter image description here

Then I've save both GoogleServices-Info-staging.plist and GoogleServices-Info-production.plist save in my ios/Runner folder

enter image description here

Then I setup this build script before the script for Compile Sources

# Type a script or drag a script file from your workspace to insert its path.
if [ "${CONFIGURATION}" == "Debug" ] || [ "${CONFIGURATION}" == "Debug-Runner-staging" ]; then 

echo "Setting up staging firebase environment"
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.example.staging.flutterAppAuthFlavours" "${PROJECT_DIR}/Runner/Info.plist"
cp -r "${PROJECT_DIR}/Runner/GoogleService-Info-staging.plist" "${PROJECT_DIR}/Runner/GoogleService-Info.plist" 
echo "$(date) staging flavour - Configuration: ${CONFIGURATION}" > "${PROJECT_DIR}/environment.txt"

elif [ "${CONFIGURATION}" == "Debug-Runner-production" ]; then 

echo "Setting up production firebase environment"
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.example.flutterAppAuthFlavours" "${PROJECT_DIR}/Runner/Info.plist" 
cp -r "${PROJECT_DIR}/Runner/GoogleService-Info-production.plist" "${PROJECT_DIR}/Runner/GoogleService-Info.plist"
echo "$(date) production flavour - Configuration:  ${CONFIGURATION}" > "${PROJECT_DIR}/environment.txt"

fi

And I called it Setup Firebase Environment (you can call it what you want)

enter image description here

This script store also some logs (with timestamp) in a file called environment.txt inside ios folder in order to easy check what xcode build has done

enter image description here

And now about Schemes and Build Configurations:

I've done two Build Configuration that are the exact copy of my Debug Build Configuration and I called them

enter image description here

  • Debug-Runner-staging
  • Debug-Runner-production

The rule of thumb is to name the build configurations as 'Debug-<your flavor>' and you need to have a scheme for every flavors you have, so I have these:

  • Runner-staging whose Run calls Debug-Runner-staging build configuration
  • Runner-production whose Run calls Debug-Runner-production build configuration

enter image description here

enter image description here

So now if I call flutter run --flavor Debug-staging I have a build that runs on my staging firebase project.

and if I call flutter run --flavor Debug-production I have a build that runs on my production firebase project.

enter image description here

enter image description here

UPDATE 2

Just for completness you could change bundle id also here:

enter image description here

Anyway it seems that there's a strange behavior that once you build a flavour a second time flutter command build correctly the flavor but run the previos build flavor.

As building with XCode and switching with schemes all works as expected (even the run of the right application) I guess that this could be a flutter command issue. So I suggest you trying file an issue here linking also this SO question/answer.

UPDATE 3

After a bit of intel I've found that flutter tools set the applicaiton launching environment before building the project. So when we change CFBundleIdentifier inside Info.plist the first time, the second time we launch flutter run it takes the previous modified value and try launching this bundle id while during build we are changing it because we are building a different variant.

A possible solution could be to launch a script that change the CFBundleIdentifier inside Info.plist before calling fluetter run.

For example starting with a Info.plist with a production bundle id of com.example.flutterAppAuthFlavours we could do something like that

enter image description here

enter image description here

Here I’ve used sed command just to think different, but you could call always our belowed PlistBuddy to make the change before calling flutter run.