具有多个目标的iOS扩展

时间:2014-08-20 07:03:17

标签: ios duplicates ios8 target ios-app-extension

在iOS 8中,当我们创建一个新的扩展时,我们必须决定它所附加的目标。扩展名将具有与目标相同的捆绑包ID前缀。

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

8 个答案:

答案 0 :(得分:25)

要在很多目标中共享一个小部件,应该只添加widget.appex目标 Embedded Binaries配置选项卡中每个父目标的General

enter image description here

然后,您将自动Embed App Extensions Build Phases区域

enter image description here

答案 1 :(得分:9)

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

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

enter image description here

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

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

答案 2 :(得分:8)

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

似乎可以在首次构建时使用。

答案 3 :(得分: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是我的扩展

向目标添加您需要的那个并添加以确保此脚本在Build Phases中的扩展设置之前运行,然后您只需要执行两次构建操作(PS:第一次它将失败,将尝试改进)它将支持多个目标

答案 4 :(得分:3)

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

让我们说大约有10个“应用”目标,我无法想象每个主要目标都会添加Notification Content和Notification Service扩展(在这种情况下,我总共将维护30个目标-疯狂)。

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

答案 5 :(得分:1)

您需要为每个ID创建多个扩展,但您可以创建动态框架并将其与每个扩展名链接。然后,您不需要复制代码。

答案 6 :(得分:0)

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

如果要基于方案更改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方案更改应用程序初始屏幕:

Xcconfig: INITIAL_SCREEN = tabBarHome

列表: initialScreen : $(INITIAL_SCREEN)

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

答案 7 :(得分:0)

其他所有答案都有其必要之处。通过对此post进行一些重要的修改,我可以使事情变得正确。

您需要做三件事:

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

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

最终订单应如下所示:

enter image description here

更改appex的bundleId

plutil的总像是这样的:

-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是我拥有的目标名称。确保正确使用您的

最终结果是这样的:

enter image description here

别忘了为每个目标重复这些步骤。确保正确的最佳方法是将appex的bundleId设置为完全错误,然后在真实设备上测试所有目标。如果您在sim卡上对其进行测试,则将无法验证代码签名是否正确运行。

FWIW通常最好将所有shell都转储到一个目录中,然后从那里引用它们。但是为了简单起见,我不这样做。

还要确保您看到original gist更加聪明,如果您使用它来更改所有appexes。您只需传递appex的名称,然后它将找出其余的...