为不同的构建方案使用不同的GoogleService-Info.plist文件

195

我正在使用一个针对生产环境的构建方案和一个针对暂存环境的构建方案(带有2个不同的包标识符)并且我正在尝试为每个构建方案使用单独的GoogleService-Info.plist。

是否有办法在初始化GCM(和Google登录)时手动选择要使用的plist文件?或者是否可以避免使用plist并手动进行设置?

谢谢!


3
你使用了两个不同的目标?那么在不同的“复制捆绑资源”(在构建阶段中)中有相同名称的不同文件。 - Alexander Zimin
7
那篇文章对我很有帮助。链接是:https://medium.com/rocket-fuel/using-multiple-firebase-environments-in-ios-12b204cfa6c0。 - Eugeny
对我来说完美无缺。https://dev59.com/fFoU5IYBdhLWcg3wTVkd#58709334 - Mashood Murtaza
2
Firebase文档:https://firebase.google.com/docs/projects/multiprojects - Lal Krishna
25个回答

234

详细信息

测试过的版本:

  • Xcode 9.2
  • Xcode 10.2 (10E125)
  • Xcode 11.0 (11A420a)

解决方案

  1. 在项目中创建一个包含所有Google.plist文件(不同名称)的文件夹

enter image description here

  1. 添加运行脚本

enter image description here

不要忘记更改{{PATH_TO_GOOGLE_PLISTS}}的值。 代码
PATH_TO_GOOGLE_PLISTS="${PROJECT_DIR}/SM2/Application/Firebase"

case "${CONFIGURATION}" in

   "Debug_Staging" | "AdHoc_Staging" )
        cp -r "$PATH_TO_GOOGLE_PLISTS/GoogleService-Info-dev.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" ;;

   "Debug_Production" | "AdHoc_Production" | "Distribution" | "Test_Production" )
        cp -r "$PATH_TO_GOOGLE_PLISTS/GoogleService-Info-prod.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" ;;

    *)
        ;;
esac

构建方案名称

enter image description here


3
这是正确的答案。显然,Firebase Analytics要求在您的应用程序的根目录中有plist文件,即使您调用configure(options:)也是如此。 https://github.com/firebase/quickstart-ios/issues/5 - Rob Bajorek
2
这是一个非常好的解决方案,应该被接受为答案。 - Luke Brandon Farrell
2
@smileBot,你可以跳过“-r”,更多信息请参考:Linux/Unix中的cp命令 - Vasily Bodnarchuk
2
case "${CONFIGURATION}" in "Debug" ) cp -r "$PATH_TO_GOOGLE_PLISTS/GoogleService-Staging-Info.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" ;; "Release" ) cp -r "$PATH_TO_GOOGLE_PLISTS/GoogleService-Info.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" ;; *) cp -r "$PATH_TO_GOOGLE_PLISTS/GoogleService-Staging-Info.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" ;; esac我已经这样做了,但它总是在两种模式(Debug和Release)中都使用GoogleService-Staging-Info.plist。这里有什么问题吗? - Bhavin_m
1
我没有找到“Application”文件夹,而我的方案名称是标准的,所以我不得不调整脚本使其工作:我将Firebase文件夹放在YourAppName中,并使用“Debug”和“Release”作为方案名称:“PATH_TO_GOOGLE_PLISTS = $ {PROJECT_DIR} / YourAppName / Firebase”根据"${CONFIGURATION}"的值,执行以下操作: “Debug” cp -r "$PATH_TO_GOOGLE_PLISTS/GoogleService-Info-dev.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist" ;; “Release” cp -r "… … - Raphael Pinel
显示剩余17条评论

94

@inidona的答案对我有用。在将其转换为Swift后,它可以正常工作。

适用于Swift 2.3:

let filePath = NSBundle.mainBundle().pathForResource("GoogleService-Info", ofType: "plist")
let options = FIROptions(contentsOfFile: filePath)
FIRApp.configureWithOptions(options)

对于Swift 3.0:

let filePath = Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist")!
let options = FIROptions(contentsOfFile: filePath)
FIRApp.configure(with: options)

对于Swift 4.0:

let filePath = Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist")!
let options = FirebaseOptions(contentsOfFile: filePath)
FirebaseApp.configure(options: options!)

2
通过上述代码,您是否在不同位置拥有两个名为GoogleService-Info.plist的不同文件,或者可能是两个具有不同名称的文件。请提供更多关于实际文件名称和位置的信息。 - Varun Gupta
1
我有两个文件,分别命名为GoogleService-Info-dev.plist和GoogleService-Info-live.plist。这段代码可以让你告诉你的应用程序要使用哪个信息文件,而不是默认的GoogleService-Info-dev.plist。使用条件语句或目标标志在你的文件之间进行切换。 - Essam Elmasry
是的,在AppDelegate中。 - Essam Elmasry
2
提供给我的是“找不到配置文件:'GoogleService-Info.plist'” - orium
7
最近的文件中提到:“警告:这种方法可能会在某些情况下影响分析数据的收集。” https://firebase.google.com/docs/projects/multiprojects - Shingo Fukuyama
显示剩余2条评论

49

如果GoogleService-Info.plist名称不同,将会影响您的分析结果。 Firebase会提醒您。因此,这些运行时解决方案均无法提供最佳的分析结果。

有两个解决方案不会影响分析:

  1. 使用每个方案的不同目标并将每个GoogleService-Info.plist 的版本与其自己的目标相关联。在Xcode右侧的文件检查器中查看目标成员资格。更多信息请参见此问题

  2. 使用构建阶段脚本复制正确的版本GoogleService-Info.plist到构建目录中。我针对暂存和生产使用不同的束 ID。这使我能够并行安装应用程序的两个版本。下面的脚本还意味着我可以使用束 ID 命名不同的GoogleService-Info.plist文件。例如:

  • GoogleService-Info-com.example.app.plist
  • GoogleService-Info-com.example.app.staging.plist

构建阶段脚本

PATH_TO_CONFIG=$SRCROOT/Config/GoogleService-Info-$PRODUCT_BUNDLE_IDENTIFIER.plist
FILENAME_IN_BUNDLE=GoogleService-Info.plist
BUILD_APP_DIR=${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app
echo cp $PATH_TO_CONFIG "$BUILD_APP_DIR/$FILENAME_IN_BUNDLE"
cp $PATH_TO_CONFIG "$BUILD_APP_DIR/$FILENAME_IN_BUNDLE"

注意:您需要更改PATH_TO_CONFIG以适应您的设置。

Build Phase Script


2
目前这是最好的解决方案。因为Firebase Crashlytics只能使用GoogleService-Info.plist文件通过'upload-symbols'脚本上传dSYM文件 - 这个解决方案完美地实现了这一点! - Alex
1
我确认这个解决方案也适用于通过Swift Package Manager安装的Firebase Crashlytics。 - Meng-Yuan Huang
@onato 我尝试了类似的方法,但在添加 Crashlytics 后它不起作用。我得到了以下错误。错误(Xcode):无法从构建环境中获取 Google Services 文件中的 GOOGLE_APP_ID - Parth Bhanderi

37

请查看此文章:https://medium.com/@brunolemos/how-to-setup-a-different-firebase-project-for-debug-and-release-environments-157b40512164

在 Xcode 中,创建两个目录:DebugRelease。将每个GoogleService-Info.plist文件放入其中。

AppDelegate.m 文件中的 didFinishLaunchingWithOptions 方法内添加以下代码:

Objective-C

  NSString *filePath;
#ifdef DEBUG
  NSLog(@"[FIREBASE] Development mode.");
  filePath = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist" inDirectory:@"Debug"];
#else
  NSLog(@"[FIREBASE] Production mode.");
  filePath = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist" inDirectory:@"Release"];
#endif

  FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:filePath];
  [FIRApp configureWithOptions:options];

Swift 4

var filePath:String!
#if DEBUG
    print("[FIREBASE] Development mode.")
    filePath = Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist", inDirectory: "Debug")
#else
    print("[FIREBASE] Production mode.")
    filePath = Bundle.main.path(forResource: "GoogleService-Info", ofType: "plist", inDirectory: "Release")
#endif

let options = FirebaseOptions.init(contentsOfFile: filePath)!
FirebaseApp.configure(options: options)

DebugRelease文件夹同时拖到Build Phases > Copy Bundle Resources中:

Build Phases > Copy Bundle Resources

就这样 :)


3
这对我有用。你需要将这些文件夹添加为引用,否则它会崩溃,确实如此。谢谢! - Eironeia
1
这个解决方案仍然无法解决“Analytics”框架的问题,你无法确定正在加载哪个“.plist”文件。 - Lifely
@Bruno Lemos,我可以同时在一个Xcode项目中使用两个Firebase项目吗?不是作为“Debug”和“Release”?因为当我尝试这样做时,我总是遇到“已配置崩溃”的问题。我已经按照Firebase官方文档的最新说明进行了操作。谢谢。 - Tulon

28
我认为你可以使用这种方式动态配置你的GoogleService-Info.plist,并为不同的包标识符使用不同的名称。
你好 Andreas
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist"];
FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:filePath];
[FIRApp configureWithOptions:options];

6
根据上述代码,你是否在不同的位置拥有两个名为GoogleService-Info.plist的文件,或者可能有两个具有不同名称的文件。请提供更多关于这些实际文件名及其位置的信息。 - Varun Gupta
4
在调用 configureWithOptions 函数时,我的日志中出现了这个错误:无法找到配置文件:'GoogleService-Info.plist' - Babken Vardanyan

26

这个回答受到了@abbood的启发,但更具体地说明了如何操作。

对于每个目标,例如dev、stg、prod:

  • 将相应的GoogleService-Info.plist下载到以目标命名的单独文件夹中
  • 在Xcode中,右键单击您的应用程序文件夹,选择Add files to "your app" enter image description here
  • 选择包含目标的GoogleService-Info.plist的文件夹,确保选中Copy items if neededCreate groups,只勾选列表中对应的目标,然后按Add enter image description here

就这样。现在您应该有类似于这样的结构

enter image description here

当您构建目标时,将使用正确的GoogleService-Info.plist


1
无需自定义脚本。完美。 - sidon
1
这应该是被接受的答案。用这种方式做和维护起来都容易得多。 - gokeji
3
实际上,这些是针对不同的构建目标。问题是如何处理模式而不是目标。但这似乎是最理想的方法。 - Ismail Iqbal
2
我已经尝试了几天,这个方法完美地解决了问题。只需确保删除任何其他对GoogleServices-Info.plist的引用,否则您可能会遇到以下错误:error: Multiple commands produce GoogleServices-Info.plist... - Dennis Barzanoff
完美运行 :D - dangg
显示剩余2条评论

22

我注意到谷歌希望代码中的文件名为GoogleServiceInfo.plist:

 * The method |configureWithError:| will read from the file GoogleServices-Info.plist bundled with
 * your app target for the keys to configure each individual API. To generate your
 * GoogleServices-Info.plist, please go to https://developers.google.com/mobile/add
 *
 * @see GGLContext (Analytics)
 * @see GGLContext (SignIn)
 */
@interface GGLContext : NSObject

关键短语是这个

从与您的应用程序目标捆绑的GoogleServices-Info.plist文件中读取

所以我只是将相同的文件复制到不同的目录中,并将其绑定到不同的目标:

enter image description here


谢谢,非常简单,对我来说运行得非常好,Xcode 10.1,FirebaseCore(5.3.1) - infinity_coding7
如何避免“多个命令产生”错误,该错误是由于应用程序中存在多个Plist或其他文件而导致的。 - Wahab Khan Jadon
1
非常好用!如此简单。真不敢相信“编写一个在构建时更改文件名的脚本”答案的投票比这个高。 - joshuakcockrell

21

虽然已经晚了,但我认为我必须发布这个答案来帮助新开发者。我发现了一篇非常好的文章,解决了我的问题,我保证它也能帮助到你:

请查看这篇文章,同样可以解决你的问题。

步骤1:
将与Firebase开发环境相对应的GoogleService-Info.plist复制到Dev目录中。类似地,将与Firebase生产环境相对应的GoogleService-Info.plist复制到Prod目录中。确保取消选中“如果需要,复制项目”以及“添加到目标”的所有目标

(Step 1 image link (I do not able to add image because of less reputations))

步骤2:
在Xcode项目导航器中,选择应用程序目标。切换到顶部的Build Phases选项卡,然后添加一个新的运行脚本阶段。将该阶段命名为“设置Firebase环境GoogleService-Info.plist”或类似名称,并将其放置在“复制Bundle资源”步骤之前。

步骤3:
实现一个shell脚本,根据构建配置将适当的GoogleService-Info.plist复制到应用程序包中。将以下shell脚本复制并粘贴到刚创建的运行脚本阶段中:

# Name of the resource we're selectively copying
GOOGLESERVICE_INFO_PLIST=GoogleService-Info.plist

# Get references to dev and prod versions of the GoogleService-Info.plist
# NOTE: These should only live on the file system and should NOT be part of the target (since we'll be adding them to the target manually)
GOOGLESERVICE_INFO_DEV=${PROJECT_DIR}/${TARGET_NAME}/Firebase/Dev/${GOOGLESERVICE_INFO_PLIST}
GOOGLESERVICE_INFO_PROD=${PROJECT_DIR}/${TARGET_NAME}/Firebase/Prod/${GOOGLESERVICE_INFO_PLIST}

# Make sure the dev version of GoogleService-Info.plist exists
echo "Looking for ${GOOGLESERVICE_INFO_PLIST} in ${GOOGLESERVICE_INFO_DEV}"
if [ ! -f $GOOGLESERVICE_INFO_DEV ]
then
    echo "No Development GoogleService-Info.plist found. Please ensure it's in the proper directory."
    exit 1
fi

# Make sure the prod version of GoogleService-Info.plist exists
echo "Looking for ${GOOGLESERVICE_INFO_PLIST} in ${GOOGLESERVICE_INFO_PROD}"
if [ ! -f $GOOGLESERVICE_INFO_PROD ]
then
    echo "No Production GoogleService-Info.plist found. Please ensure it's in the proper directory."
    exit 1
fi

# Get a reference to the destination location for the GoogleService-Info.plist
PLIST_DESTINATION=${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app
echo "Will copy ${GOOGLESERVICE_INFO_PLIST} to final destination: ${PLIST_DESTINATION}"

# Copy over the prod GoogleService-Info.plist for Release builds
if [ "${CONFIGURATION}" == "Release" ]
then
    echo "Using ${GOOGLESERVICE_INFO_PROD}"
    cp "${GOOGLESERVICE_INFO_PROD}" "${PLIST_DESTINATION}"
else
    echo "Using ${GOOGLESERVICE_INFO_DEV}"
    cp "${GOOGLESERVICE_INFO_DEV}" "${PLIST_DESTINATION}"
fi

14

我发现在只有单个目标的情况下,唯一可行的方法是在构建过程中复制与构建配置对应的plist;但是这里的答案在如何做方面存在细节上的差异,而且没有一个方便我使用的。我的答案基于@KnightFighter的回答和Medium上的这篇文章


首先添加所有不同的plist到工程中,并使用不同的名称(它们不能作为资源添加到目标中):

enter image description here

接下来创建用户定义的构建设置,在其中可以为每个构建配置分配特定的plist:

enter image description here

最后添加“运行脚本”阶段,其中包含以下代码:

GOOGLE_SERVICE_INFO_PLIST_SOURCE=${PROJECT_DIR}/${TARGET_NAME}/${GOOGLE_SERVICE_INFO_PLIST_FILENAME}

if [ ! -f $GOOGLE_SERVICE_INFO_PLIST_SOURCE ]
then
    echo "${GOOGLE_SERVICE_INFO_PLIST_SOURCE} not found."
    exit 1
fi

GOOGLE_SERVICE_INFO_PLIST_DESTINATION="${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"

cp "${GOOGLE_SERVICE_INFO_PLIST_SOURCE}" "${GOOGLE_SERVICE_INFO_PLIST_DESTINATION}"

我认为这种方法有一些优点:

  • 无需拥有文件夹层次结构来存储 plist 文件;
  • 使用单个 plist 文件即可为多个配置使用,无需复制文件;
  • 如果需要添加配置或重新分配 plist,更改构建设置中的文件名比编辑脚本更容易;尤其是对于非程序员(即构建管理器)。

我使用多个方案和配置(只有一个目标),所以这个解决方案非常适合我。非常感谢您提供如此详细的解释。 - nahung89

7

假设我们有两个配置集,developproduction。你需要完成以下两件事:

  1. 将两个plist文件重命名以符合给定的配置:
  • GoogleService-Info-develop.plist
  • GoogleService-Info-production.plist
  1. 添加一个运行脚本,复制所选配置的正确的plist文件:
FIREBASE_PLIST_PATH="${PROJECT_DIR}/App/Resources/Plists/GoogleService-Info-${CONFIGURATION}.plist"
echo "Firebase plist path: ${FIREBASE_PLIST_PATH}"
cp -r ${FIREBASE_PLIST_PATH} "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"

FirebaseCrashlytics脚本之前放置运行脚本。

您可以像单一方案一样初始化Firebase:FirebaseApp.configure()


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接