在iOS 8中,当我们创建一个应用程序扩展时,我们必须决定它要附加到哪个目标。扩展将与目标具有相同的Bundle ID前缀。
- 是否有办法之后更改目标?
- 如果我的项目包含2个(或更多)目标(例如一个用于debug/simulator,一个用于production/device),那么最好的处理扩展的方式是什么?我需要创建另一个扩展并复制代码(非常麻烦,需要保持两个目标的代码完全一样)吗?
在iOS 8中,当我们创建一个应用程序扩展时,我们必须决定它要附加到哪个目标。扩展将与目标具有相同的Bundle ID前缀。
#!/bin/bash
buildID=${PRODUCT_BUNDLE_IDENTIFIER}
extId="notification-service"
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $buildID.$extId" "${SRCROOT}/${extId}/Info.plist"
第一次构建似乎可以正常工作。
Set: Entry, ":CFBundleIdentifier", Does Not Exist
。有什么想法吗? - heyfrank看起来您只需要使用其自己的Info.plist复制扩展目标,但不需要其他任何内容。
然而,当您创建一个扩展时,Xcode会将“嵌入应用程序扩展”添加到应用程序目标的构建阶段中,如下所示,尚无UI可处理此操作。
尽管如此,您仍然可以为第二个目标创建扩展,然后删除除.plist文件之外的所有文件,并修复需要修复的内容。以下是逐步过程:
我创建了一个运行脚本来支持这个需求。
#!/bin/sh
buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$INFOPLIST_FILE")
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "${SRCROOT}/ImagePush/Info.plist"
buildVersion=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "$INFOPLIST_FILE")
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $buildVersion" "${SRCROOT}/ImagePush/Info.plist"
buildID=${PRODUCT_BUNDLE_IDENTIFIER}
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $buildID.ImagePush" "${SRCROOT}/ImagePush/Info.plist"
ImagePush是我的扩展程序
将其添加到目标中,确保此脚本在Build Phases中的扩展设置之前运行,然后只需执行两次构建操作(PS:第一次会失败,我会尝试改进),它将支持多个目标。
在我的项目中,我需要构建一些不同版本的应用程序(细节上有所不同,例如每个应用程序都带有不同的标志)。
假设有大约10个“应用”目标,我无法想象每个主要目标都添加Notification Content和Notification Service扩展(在这种情况下,我将总共维护30个目标 - 疯狂)。
我运行一个脚本(https://gist.github.com/damian-rzeszot/0b23ad87e5ab5d52aa15c095cbf43c59),在“嵌入应用程序扩展”阶段之后, 覆盖应用程序扩展plist和证书、应用程序版本,更改配置文件并重新签署bundle。
TO-BE-REPLACED
。 - Damian RzeszotXcconfigs可以通过修改Xcode方案来修改plist条目和代码变量,是一种很好的方式。
如果您想根据方案更改给定扩展程序的Bundle identifier
:
为您的扩展创建一个Xcconfig文件。我的iMessageExtension-Debug.xcconfig
有以下条目:PRODUCT_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER).iMessageExtension
从Xcode文件检查器中点击项目>在细节面板中点击项目>信息选项卡>添加配置
我创建了一个调试配置。
在新创建的配置中,进入您的扩展程序>选择配置文件
创建一个新方案:在该新方案的运行选项卡中,您可以选择您新创建的配置。同样的过程也适用于发布环境。
我们创建的Xcconfig选项可以直接在iMessageExtension > Info.plist中使用:
Bundle identifier : $(PRODUCT_BUNDLE_IDENTIFIER)
如果您需要基于xcconfig变量在代码中引用变量:
例如,我想根据选择的Xcode方案更改我的应用程序初始屏幕:
Xcconfig:
INITIAL_SCREEN = tabBarHome
Plist:
initialScreen : $(INITIAL_SCREEN)
Swift代码:
var initialScreen = object(forInfoDictionaryKey: "initialScreen") as? String
2个解决方案:
XCConfig
。请参见此处提到的答案: https://dev59.com/1l8e5IYBdhLWcg3wwMlW#63583849和这里。还可以在此SO问题中搜索XCConfig
。您可能会发现一些评论。说实话,我认为XCConfig
解决方案要优雅得多。您只需交换东西即可,而不是尝试以非常特定的顺序覆盖所有内容...
其他答案都有必要的一部分。通过对此帖子进行一些重要修改,我能够做到正确无误。
你需要做三件事:
注意:在嵌入应用程序扩展名之前,您无法对其进行签名。因此,“重新签名appex”的步骤需要在“嵌入应用程序扩展名”步骤之后发生。同样,如果bundleId未以父应用程序的bundleId作为前缀,则无法嵌入应用程序扩展名。
最终顺序应如下图所示:
plutil
的语法如下:
-replace keypath -type value
那就这样做:
plutil -replace \
CFBundleIdentifier -string \
$PRODUCT_BUNDLE_IDENTIFIER.contentExt \
"$BUILT_PRODUCTS_DIR/contentExt.appex/Info.plist"
如果您想了解更多关于plutil
的信息(请参见这里和这里)。PlistBuddy
有点过时了。
注意:ContentExtension是我的目标名称。请确保您正确使用您的名称。
/usr/bin/codesign \
--force \
--sign $EXPANDED_CODE_SIGN_IDENTITY \
--entitlements $CONFIGURATION_TEMP_DIR/ContentExtension.build/ContentExtension.appex.xcent \
--timestamp=none \
"$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/$BUNDLE_PLUGINS_FOLDER_PATH/ContentExtension.appex"
注意: 我这里的目标名是ContentExtension,请确保你正确使用自己的目标名。
最终结果如下:
不要忘记为每个目标重复这些步骤。确保你做对了的最好方法是将appex的bundleId设置为完全错误的内容,然后在真实设备上测试所有目标。如果你在模拟器上测试,就无法验证代码签名是否正常工作。
顺便说一句,通常最好将所有shell都放在一个目录中,然后从那里引用它们。但为了简化本文,我没有这样做。
还要确保你看到了原始Gist。如果你使用它来更改所有的appexes,它会更加智能。你只需要传递appex的名称,它就会自动解决其余的问题...