【问题标题】:iOS extensions with multiple targets具有多个目标的 iOS 扩展
【发布时间】:2014-10-13 10:50:51
【问题描述】:

在 iOS 8 中,当我们创建应用扩展时,我们必须决定它附加到哪个目标。扩展将具有与目标相同的包 ID 前缀。

  1. 以后有什么办法可以改变目标吗?
  2. 如果我的项目包含 2 个(或更多)目标(例如,一个用于调试/模拟器,一个用于生产/设备),使用扩展的最佳方式是什么?我是否需要创建另一个扩展并复制代码(为两个目标保留相同的代码非常麻烦)?

【问题讨论】:

  • 您找到问题的答案了吗?
  • 还没有。我必须暂时复制第二个目标的代码。
  • 嗯,现在好像没有办法在多个扩展上使用相同的代码
  • 只需在“嵌入应用程序扩展”中添加您的扩展程序的 .appex 并将其添加到目标依赖项中。
  • 看到这个relevant question

标签: ios plist code-signing ios-app-extension bundle-identifier


【解决方案1】:

要与其他目标共享一个小部件,您只需添加 widget.appex 目标 为General 配置选项卡中的每个父目标Embedded Binaries

然后你会在Build Phases自动得到Embed App Extensions区域

【讨论】:

  • 它显示错误:“嵌入式二进制文件的包标识符没有以父应用程序包标识符为前缀”,因为我的两个目标都有不同的包 ID,所以为了广告扩展,我需要提供扩展的包-id 以目标的 bundle-id 开头(或前缀)。所以不能为多个目标添加相同的通知服务扩展。有什么解决办法吗?
  • @Ankush 在具有不同标识符的目标之间使用一个扩展是不可能的。在开发者门户中,您需要为具有不同标识符的应用程序进行物理上不同的扩展。
  • @malex 如何在开发者门户网站上做你能解释一下
  • 在 Xcode11 中,Embed Binaries 现在重命名为 Frameworks, Libraries, and Embedded Content
【解决方案2】:

这是我的设置:我有 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"

似乎在第一次构建时工作。

【讨论】:

  • 这是一个很好的解决方案,还修复了Neo Chen 报告的“初始构建失败”问题。
  • 如果扩展 plist 的路径与扩展的 bundle ID 不匹配,您可能需要编辑脚本。
  • 此解决方案完美运行,只需一个通知服务扩展即可使用不同的目标。该脚本需要添加到特定应用程序目标中的预操作中。确保在 pre-actions 选项卡中的“provide build settings for:”下拉菜单中选择您的目标,否则将不会执行。
  • 我有多个目标,拥有不同的帐户和开发团队。该扩展需要代码签名。我如何实现代码签名与我的父目标相同。我已经用 xcconfig 文件试过了。但我无法弄清楚如何为我的扩展正确设置 DEVELOPMENT_TEAM。有什么想法吗?
  • 可以在本地运行。但是,当我尝试将每个目标的存档版本上传到 App Store 时,我得到an error which is similar to this one 用于两个目标之一。有人知道如何解决这个问题吗? @Erumaru 这与您遇到的问题相同吗?你找到解决方法了吗?
【解决方案3】:

您似乎应该能够使用自己的 Info.plist 复制 Extension 目标,但不能复制其他任何内容。

但是,当您创建扩展时,Xcode 会将“嵌入应用扩展”添加到应用目标的构建阶段,如下所示,并且还没有 UI 可以执行此操作。

不过,您可以为第二个目标创建扩展名,然后删除除 .plist 之外的所有文件,并修复需要修复的内容。这是一步一步的:

  • 为“目标 1”创建“扩展 1”
  • 为“目标 2”创建“扩展 2”
  • 删除为“扩展 2”创建的所有文件,但其 Info.plist 除外
  • 使“扩展 2”目标的“构建阶段”与“扩展 1”的构建阶段相同。通常是将必要的 .m 文件添加到“编译源”阶段,并将资源添加到“复制捆绑资源”阶段

【讨论】:

  • “嵌入应用程序扩展”构建阶段只是具有自定义名称的标准复制文件阶段。您可以轻松地自己添加它并将 extension.appex 目标放入插件中。如果您愿意,您也可以更改构建阶段的名称。
  • 扩展 2 将使用哪些证书和应用组 ID?
  • 成功了,谢谢。但是如果我们有 10+ 个目标就不好了,所以我们必须为每个目标创建 10+ 个小部件:(
【解决方案4】:

我已经创建了一个运行脚本来支持这个要求

#!/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:第一次会失败,会尝试改进)它将支持多个目标

【讨论】:

  • 这是一个很好的解决方案。你有没有找到一种方法来设法通过第一个构建错误?
  • 您是如何对扩展进行代码签名的?我有多个目标并使用相同的扩展名。如果在“常规”中手动设置扩展包 ID 和代码设计,它会很好地工作。您是否有任何运行脚本可以在更改扩展包标识符后进行代码签名?
【解决方案5】:

在我的项目中,我需要构建一些不同版本的应用程序(细节不同,例如每个应用程序都有不同的徽标)。

假设有大约 10 个“应用”目标,我无法想象为每个主要目标添加通知内容和通知服务扩展(在这种情况下,我总共会维护 30 个目标 - 疯狂)。

我运行一个脚本 (https://gist.github.com/damian-rzeszot/0b23ad87e5ab5d52aa15c095cbf43c59)“嵌入应用扩展”阶段之后, 覆盖应用扩展 plist 和权利中的包 ID、应用版本、更改配置文件并重新签署包。

【讨论】:

  • 错误:嵌入式二进制文件的包标识符没有以父应用的包标识符为前缀。 “嵌入式二进制包标识符:com.foo.NotificationContent 父应用程序包标识符:com.foo.staging”。在构建阶段遇到此错误,您知道如何解决吗?
  • @kl.woon 更新脚本 - 有TO-BE-REPLACED
  • 是的,我确实用目标的捆绑 ID 替换了该字符串。我的猜测是我的所有目标都有不同的包 ID,即:com.foo.dev、com.foo.staging 和 com.foo,但扩展名默认为 com.foo。我将尝试改用 .xcconfig 。无论如何,感谢您的脚本,从中学到了很多东西。
  • 能否详细描述一下步骤。
  • @iKushal 这个解决方案背后的主要思想很简单:在将 appex 捆绑包复制到主应用捆绑包之后,我们更改了 appex 的捆绑包并辞职
【解决方案6】:

Xcconfigs 是根据 Xcode 方案更改修改 plist 条目和代码变量的好方法。

如果您想更改Bundle identifier,例如基于方案的给定扩展:

  1. 为您的扩展创建一个 Xcconfig 文件。我的iMessageExtension-Debug.xcconfig 有这个条目: PRODUCT_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER).iMessageExtension

  2. 从 Xcode 文件检查器中单击您的项目 > 在详细信息窗格中单击您的项目 > 信息选项卡 > 添加配置

  3. 我创建了一个调试配置。

  4. 在你新创建的配置钻到你的扩展 > 选择配置文件

  5. 创建一个新方案:在该新方案的运行选项卡中,您可以选择新创建的配置。可以为额外发布执行相同的过程。

  6. 我们创建的 Xcconfig 选项可以直接在 iMessageExtension > Info.plist 中使用: Bundle identifier : $(PRODUCT_BUNDLE_IDENTIFIER)

如果需要在代码中基于xcconfig变量引用变量:

例如,我想根据所选的 Xcode 方案更改我的应用程序初始屏幕:

Xc 配置: INITIAL_SCREEN = tabBarHome

列表: initialScreen : $(INITIAL_SCREEN)

Swift 代码: var initialScreen = object(forInfoDictionaryKey: "initialScreen") as? String

【讨论】:

    【解决方案7】:

    编辑

    2 个解决方案:

    • 使用XCConfig。请参阅此处提到的答案:https://stackoverflow.com/a/63583849/5175709here。还要在这个 SO 问题上搜索 XCConfig。您可能会发现一些 cmets。
    • 通过脚本改变一切。我的答案是这样做。

    说实话,我认为XCConfig 解决方案要优雅得多。您只需将内容交换进出,而不是尝试以非常特定的顺序覆盖所有内容...


    每个其他答案都有一部分是必要的。对这个 post 进行一些重要修改后,我能够把事情做好。

    你需要做三件事:

    • 更改 appex 的 bundleId(应用扩展)
    • 更改捆绑包后重新签署 appex。虽然应用可以在模拟器上正确运行,但如果没有这一步,它将无法在真实设备上运行。
    • 设置适当的配置文件。 (排除此答案,因为我还不知道该怎么做)

    注意:在 appex 嵌入之前,您不能对其进行签名。因此,“重新签署 appex”步骤需要在“嵌入应用程序扩展”步骤之后进行。同样,如果 bundleId 没有以父应用的 bundleId 为前缀,则无法嵌入 appex。

    最终的顺序应该是这样的:

    改变appex的bundleId

    plutil的sytanx是这样的:

    -replace keypath -type value 
    

    那就这样吧:

    plutil -replace \
    CFBundleIdentifier -string \
    $PRODUCT_BUNDLE_IDENTIFIER.contentExt \
    "$BUILT_PRODUCTS_DIR/contentExt.appex/Info.plist"
    

    如果您想了解更多关于plutil 的信息(请参阅herehere 了解更多信息)。 PlistBuddy 有点老了。

    注意: ContentExtension 是我的目标名称。确保你正确使用你的

    重新签署appex

    /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 转储到一个目录中,然后从那里引用它们。但是为了这篇文章的简单起见,我没有这样做。

    还要确保你看到original gist,如果你用它来改变你所有的appexes,它会更聪明。您只需要传递应用程序的名称,然后它就会找出其余的...

    【讨论】:

      【解决方案8】:

      您需要为每个 ID 创建多个扩展,但您可以创建动态框架并将其与每个扩展链接。那么你就不需要复制你的代码了。

      【讨论】:

      • 你有关于如何做到这一点的任何例子/链接吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多