在 iOS 8 中,当我们创建应用程序扩展时,我们必须决定它附加到哪个目标。扩展将具有与目标相同的捆绑 ID 前缀。
- 之后有什么办法可以改变目标吗?
- 如果我的项目包含 2 个(或更多)目标(例如,一个用于调试/模拟器,一个用于生产/设备),使用扩展的最佳方式是什么?我是否需要创建另一个扩展并复制代码(为两个目标保留相同的代码非常麻烦)?
在 iOS 8 中,当我们创建应用程序扩展时,我们必须决定它附加到哪个目标。扩展将具有与目标相同的捆绑 ID 前缀。
这是我的设置:我有 3 个目标(生产、暂存、本地)和一个我不想重复 3 次的扩展目标。
只是为了澄清Neo Chen的答案,编辑每个父目标的方案:
Build > Pre-actions > New Run Script Action > 从(父方案)提供构建设置。
为每个扩展粘贴这个:
#!/bin/bash
buildID=${PRODUCT_BUNDLE_IDENTIFIER}
extId="notification-service"
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $buildID.$extId" "${SRCROOT}/${extId}/Info.plist"
似乎在第一次构建时工作。
似乎您应该能够仅使用其自己的 Info.plist 复制 Extension 目标,但不能复制其他任何内容。
但是,当您创建扩展时,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 是我的扩展
添加到您需要的目标并添加确保此脚本在构建阶段中的扩展设置之前运行,然后您只需要执行两次构建操作(PS:第一次它会失败,将尝试改进)它会支持多目标
在我的项目中,我需要构建一些不同版本的应用程序(细节不同,例如每个应用程序都带有不同的徽标)。
假设有大约 10 个“应用程序”目标,我无法想象为每个主要目标添加通知内容和通知服务扩展(在这种情况下,我将总共维护 30 个目标 - 疯狂)。
我在“嵌入应用程序扩展”阶段之后运行一个脚本(https://gist.github.com/damian-rzeszot/0b23ad87e5ab5d52aa15c095cbf43c59),它覆盖应用程序扩展列表和权利、应用程序版本中的捆绑ID,更改配置文件并重新签署捆绑。
Xcconfigs 是基于 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 方案更改我的应用程序初始屏幕:
配置:
INITIAL_SCREEN = tabBarHome
列表:
initialScreen : $(INITIAL_SCREEN)
SWIFT代码:
var initialScreen = object(forInfoDictionaryKey: "initialScreen") as? String
2个解决方案:
XCConfig
. 请参阅此处提到的答案:https ://stackoverflow.com/a/63583849/5175709和此处。也搜索XCConfig
这个 SO question。你可能会发现一些评论。老实说,我认为XCConfig
解决方案要优雅得多。你只是交换东西,而不是试图以非常特定的顺序覆盖所有东西......
每个其他答案都有一部分是必要的。通过对这篇文章的一些重要修改,我能够把事情做好。
你需要做三件事:
注意:您无法在 appex 嵌入之前对其进行签名。因此,“重新签署 appex”步骤需要在“嵌入应用程序扩展”步骤之后进行。同样,如果 bundleId 没有以父应用程序的 bundleId 为前缀,则无法嵌入 appex。
最终订单应如下所示:
plutil
的 sytanx 是这样的:
-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 设置为完全错误的值,然后在真实设备上测试所有目标。如果您在 sim 上对其进行测试,那么您将无法验证代码签名是否正常工作。
FWIW 通常最好将所有 shell 转储到一个目录中,然后从那里引用它们。但是为了这篇文章的简单起见,我没有这样做。
还要确保你看到原来的要点,如果你用它来改变你所有的 appexes,它会更聪明。您只需要传递应用程序的名称,然后它就会找出其余的...
您需要为每个 ID 创建多个扩展,但您可以创建动态框架并将其与每个扩展链接。然后你就不需要复制你的代码了。