@bacons/apple-targets

中文文档
5.0.0发布于 2026/07/18MITTypeScriptCommonJS近 30 天下载 322.4万

以 Expo 预建创建生成苹果目标

中文使用文档
文档目录27 节
  1. 如何使用
  2. 如何运作
  3. 应享权利
  4. 发展
  5. AI 代理技能
  6. 目标配置
  7. 颜色
  8. SF 符号
  9. 可可罂粟
  10. _shared
  11. exportJs
  12. 实例
  13. widget
  14. action
  15. spotlight
  16. 支持类型
  17. 尚未支持
  18. 代码签名
  19. 建筑部件
  20. 目标之间的数据共享
  21. 配置应用组
  22. 设置共享数据
  23. 获取共享数据
  24. 更多数据更新
  25. Xcode 解析
  26. 控制部件
  27. 缩写

苹果目标

一个 Expo 配置插件, 生成本地的 Apple 目标, 如 Widget 或 App Clips, 并将其链接到外部 /ios 目录。 您可以打开 Xcode 并开发虚拟内的目标 expo:targets 文件夹和更改将被保存在 ios 目录。 这种模式使得建筑不属于“土著反应”范围的东西能够同时获得所有的好处 连续的土著一代。 。 。 .

targets

如何使用

这个插件至少需要CocoaPods 1.16.2 (ruby 3.2.0),Xcode 16 (macOS 15 Sequoia),和 Expo SDK + 53.

  1. 运行 npx create-target 以生成苹果目标.
  2. 选择要生成的目标,我建议从 widget (例如:. npx create-target widget) (中文(简体) ). 这将生成根中所需的部件文件 /targets 目录, 安装 @bacons/apple-targets,并在您的工程中添加 Expo 配置插件.
  3. 如果你已经知道你的苹果团队身份, 那就把它放在下面 ios.appleTeamId 属性 app.json。 您可以在签名和功能标签下的 Xcode 中找到它, 如有必要, 稍后设置它 .
  4. 运行 npx expo prebuild -p ios --clean 生成 Xcode 项目.
  5. 您现在可以在 Xcode 中打开您的工程(xed ios并开发其中的部件 expo:targets/<target> 文件夹.
  6. 准备建造时,在Xcode中选择目标并建造.

如何运作

词根 /targets 文件夹是魔法,每个子目录应该有一个 expo-target.config.js 定义目标设置的文件。 当你跑的时候 npx expo prebuild --clean,插件将生成 Xcode 项目,并将目标文件与项目链接. 插件也会生成一个 Info.plist 如果一个不存在的话.

配置插件将链接子文件夹(如:. targets/widget)到Xcode,里面的所有文件都将是目标的一部分. 这意味着你可以开发目标 在内部 expo:targets 文件夹和更改将被保存在生成的外 ios 目录.

词根 Info.plist 每个目标目录中的文件没有管理,可以自由修改.

顶层的任何文件 target/{name}/assets 目录将作为目标的资源链接。 加入这一规则是为了支持Safari扩展.

应享权利

如果 expo-target.config 文件定义 entitlements: {} 对象, a generated.entitlements 文件写入预建输出于 ios/.targets/<productName>/generated.entitlements (不输入目标源文件夹),和 CODE_SIGN_ENTITLEMENTS 它指着它。 这个 .targets 目录使得其中的文件明显是衍生出来的,不应由手来编辑. 源目标目录没有被触及,因此可以保持git中衍生出文物的清洁. 更新 expo-target.config 要更改应享待遇——从不直接编辑生成的文件,它会被覆盖在每一个文件上 npx expo prebuild。要代替手工管理应享权利文件,请删除 entitlements 对象从 expo-target.config 添加一个 *.entitlements 转到目标源文件夹.

如果 entitlements 对象在配置中没有定义,您可以手动添加单个顶级 *.entitlements 文件到目标源目录——重运行 npx expo prebuild 将该文件链接到目标。 只有一级 *.entitlements 每个目标都支持文件。 如果两者都由配置驱动 entitlements 块和一个手写 *.entitlements 文件已存在,配置胜出后手写文件被忽略(你会看到日志行并可以删除它).

有些目标有特殊的权利行为:

  • 缩写( A)clip) 自动设置所需的 com.apple.developer.parent-application-identifiers 改为 $(AppIdentifierPrefix)${config.ios.bundleIdentifier}
  • 能够使用 App Groups 的目标将自动映射 ios.entitlements['com.apple.security.application-groups'] 从 app.json 如果它的定义。 可以通过指定一个 entitlements['com.apple.security.application-groups'] 数组 expo-target.config.js 文档.

发展

外出的任何变化 expo:targets xcode 中的目录将被下个覆盖 npx expo prebuild --clean。检查是否在 Info.plist 或 expo-target.config.js 文档. 如果你修改 expo-target.config.js 或你的根头 app.json您需要重新运行 npx expo prebuild --clean 以同步更改.

您可以使用自定义的预建模板 --template ./node_modules/@bacons/apple-targets/prebuild-blank.tgz 创建一个没有React Industrial的建筑,这可以使开发速度快一些,因为编译时间较少. 这是先进的发展技术 不生产 并不打算用于第三方配置插件.

AI 代理技能

这回转船是 代理技能 用于教学 AI 编码代理( Claude Code, Cursor, Codex等) 如何用此插件构建、配置和调试每个支持的 Apple 扩展名和目标类型 。 它包括一个每个目标参考文件与苹果docs,运行时间行为,生产准备的斯威夫特,平台支持,以及Gotchas.

安装到您的代理中 npx skills数字 :

npx skills add EvanBacon/expo-apple-targets

CLI自动检测您安装的代理,并将技能复制到他们的技能目录(例如. .claude/skills/),当您要求代理添加 Apple 扩展时,它会自动加载。 见 医生/技能 细节、更新和删除.

目标配置

目标配置可以是 expo-target.config.js,或 expo-target.config.json 文档.

此文件可以有以下属性 :

/** @type {import('@bacons/apple-targets/app.plugin').Config} */
module.exports = {
  type: "widget",

  // Name of the target/product. Defaults to the directory name.
  name: "my_widget",

  // Optional override for CFBundleDisplayName. Defaults to `name` if not provided.
  displayName: "My Widget",

  // Generates colorset files for the target.
  colors: {
    // or "$accent": "red",
    $accent: { color: "red", darkColor: "blue" },
  },
  icon: "../assets/icon.png",
  // Can also be a URL
  frameworks: [
    // Frameworks with or without the `.frameworks` extension, these will be added to the target.
    "SwiftUI",
  ],
  entitlements: {
    // Serialized entitlements. Useful for configuring with environment variables.
  },
  // Generates xcassets for the target. Supports images (imageset) and SF Symbol template SVGs (symbolset).
  images: {
    thing: "../assets/thing.png",
    // SF Symbol SVGs are automatically detected and generated as symbolsets.
    mySymbol: "./my-symbol.svg",
  },

  // The iOS version fot the target. Defaults to 18.0
  deploymentTarget: "15.1",

  // Optional bundle identifier for the target. Will default to a sanitized version of the root project bundle id + target name.
  // If the specified bundle identifier is prefixed with a dot (.), the bundle identifier will be appended to the main app's bundle identifier.
  bundleIdentifier: ".mywidget",

  // Should the release build export the JS bundle and embed. Intended for App Clips and Share Extensions where you may want to use React Native.
  exportJs: false,
};

您也可以返回一个接受 ExpoConfig 的函数,并返回一个同步权限和其他值的目标函数:

/** @type {import('@bacons/apple-targets/app.plugin').ConfigFunction} */
module.exports = (config) => ({
  type: "widget",
  colors: {
    $accent: "steelblue",
  },
  entitlements: {
    // Use the same app groups:
    "com.apple.security.application-groups":
      config.ios.entitlements["com.apple.security.application-groups"],
    // Or generate an app group:
    "com.apple.security.application-groups": [
      `group.${config.ios.bundleIdentifier}.widget`,
    ],
  },
});

在目标配置中不支持无害环境管理和类型脚本。 坚持 require 用于在目标之间分享变量.

颜色

目标之间有某些共同的价值观。 我们使用预先界定的公约来描绘这些目标之间的这些价值.

名称构建设置目的
$accentASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME设置全局口音颜色, 在元件中, 用于编辑元件时按钮的颜色 .
$widgetBackgroundASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME设置部件的背景颜色.

SF 符号

SVG 文件传递给 images 自动检查以确定是否为 SF 符号模板。当检测到时, SVG 将生成为 .symbolset 内 Assets.xcassets 而不是常规 .imageset。这对本地文件路径和URL都有效.

/** @type {import('@bacons/apple-targets/app.plugin').Config} */
module.exports = {
  type: "widget",
  images: {
    // SF Symbol template SVGs are auto-detected:
    expo: "./expo.sfsymbol.svg",
    // URLs work too:
    remote: "https://example.com/my-symbol.svg",
    // Regular images still work as expected:
    photo: "./photo.png",
  },
};

在斯威夫特语中,使用符号名作为自定义符号图像:

Image("expo")

您可以轻松使用 :

bun create symbol /path/to/icon.svg

可可罂粟

正在添加文件 pods.rb 在 repo 根中,您可以修改项目的目标设置.

红宝石模块对地产进行全球访问评估 podfile_properties。 。 。 .

例如,以下内容对于在 App Clip 目标中实现 React Native 有用 :

require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")

exclude = []
use_expo_modules!(exclude: exclude)

if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1'
  config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"];
else
  config_command = [
    'node',
    '--no-warnings',
    '--eval',
    'require(require.resolve(\'expo-modules-autolinking\', { paths: [require.resolve(\'expo/package.json\')] }))(process.argv.slice(1))',
    'react-native-config',
    '--json',
    '--platform',
    'ios'
  ]
end

config = use_native_modules!(config_command)

use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']
use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS']

use_react_native!(
  :path => config[:reactNativePath],
  :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes',
  # An absolute path to your application root.
  :app_path => "#{Pod::Config.instance.installation_root}/..",
  :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false',
)

此块在 Podfile 结尾处执行 :

target "target_dir_name" do
   target_file
end

目标名称必须与目标目录名称相匹配.

_shared

一些文件需要与您的目标和主要目标同时链接. 要支持, 您可以添加顶级 _shared 目录。 此目录中的任何文件都将同时链接到主要目标和子目标. 每次添加,重命名,或删除此目录中的文件,都需要重新运行预建.

还可以添加一个 _shared 根内部的目录 targets/_shared 目录。 这将将文件链接到您工程中的所有目标 .

exportJs

这个 exportJs 当目标使用 React Native(App Clip,Share扩展名)时,应当使用选项。 它通过连接 主要目标的 Bundle React Native code and images 建相到目标。 这将确保生产得以建立(Release)将JS主条目文件与Metro捆绑,并嵌入捆绑/资产供下线使用.

要检测正在构建的目标, 您可以使用 expo-application。 。 。 .

实例

widget

我写了一篇关于这个的博客, 学习更多: Expo x 苹果部件。 。 。 .

/** @type {import('@bacons/apple-targets/app.plugin').Config} */
module.exports = {
  type: "widget",
  icon: "../../icons/widget.png",
  colors: {
    // This color is referenced in the Info.plist
    $widgetBackground: "#DB739C",

    $accent: "#F09458",

    // Optional: Add colors that can be used in SwiftUI.
    gradient1: {
      light: "#E4975D",
      dark: "#3E72A0",
    },
  },
  // Optional: Add images that can be used in SwiftUI.
  // SF Symbol SVGs are automatically detected and generated as symbolsets.
  images: {
    valleys: "../../valleys.png",
    myIcon: "./my-icon.sfsymbol.svg",
  },
  // Optional: Add entitlements to the target, this one can be used to share data between the widget and the app.
  entitlements: {
    "com.apple.security.application-groups": ["group.bacon.data"],
  },
};

action

IMG_C2C825ACC8C7-1

这些出现在股票单上。 图标应当透明,因为系统将遮盖图标.

/** @type {import('@bacons/apple-targets/app.plugin').Config} */
module.exports = {
  type: "action",
  displayName: "Inspect Element",
  icon: "./assets/icon.png",
  colors: {
    TouchBarBezel: "#DB739C",
  },
};

将 JavaScript 文件添加到 assets/index.js数字 :

class Action {
  /**
   * `extensionName: "com.bacon.2095.axun"`
   * @param {*} arguments: {completionFunction: () => unknown; extensionName: string; }
   */
  run({ extensionName, completionFunction }) {
    // Here, you can run code that modifies the document and/or prepares
    // things to pass to your action's native code.

    // We will not modify anything, but will pass the body's background
    // style to the native code.
    completionFunction({
      /* */
    });
  }

  finalize() {
    // Runs after the native action code has completed.
  }
}

window.ExtensionPreprocessingJS = new Action();

保证 NSExtensionJavaScriptPreprocessingFile: "index" 在Info.plist中.

spotlight

用您的应用内容来传播 Spotlight 搜索结果 .

/** @type {import('@bacons/apple-targets/app.plugin').Config} */
module.exports = {
  type: "spotlight",
};

支持类型

理想的情况是,这将会在一个完全合格的Xcode项目上自动生成,但目前这是一个人工过程. 目前所支持的类型是基于对iOS App Store中最常用目标的静态分析. 我还没有测试过所有这些 而且它们可能不起作用.

类型说明
动作共享动作
应用程序应用程序意向扩展
部件部件/ 活活动
监视监视 App( 附带 iOS App)
监视器观察相貌复杂
剪贴板缩写
萨法里Safari 扩展
份额共享扩展
通知内容通知内容扩展
通知服务通知服务扩展
意图Siri 意向扩展
意向 - uiSiri 意向 UI 扩展
聚光灯焦点索引扩展
bg- 下载背景下载扩展
快看通肠快速外观缩略图扩展
位置推动地点 :
全权证书提供者证书提供者扩展
账户-自动账户认证扩展
设备活动监测器设备活动监视器扩展
键盘自定义键盘扩展
事项物质设备设置扩展
网络包通包式隧道网络扩展
网络应用代理App代理网络扩展
网络代理DNS 代理网络扩展
网络过滤数据过滤数据网络扩展
内容屏蔽器Safari 内容屏蔽扩展
文件提供者文件提供者扩展名
广播加载量广播上传扩展
呼叫目录调用目录扩展
信件过滤器信件过滤扩展
文件提供者- ui文件提供者 UI 扩展名
广播设置广播设置 UI 扩展
classkit 语句ClassKit 上下文提供者扩展
意外通讯不需要的通讯报告
相片编辑相片编辑扩展
快速视图审查快速浏览预览扩展
聚光灯CoreSpotlight 代表扩展
虚拟会议虚拟会议提供者
屏蔽动作盾牌行动扩展
图标配置盾牌配置扩展
打印服务打印服务扩展
智能卡智能卡/ 持久托肯
认证服务认证服务扩展
钱包钱包供应(非UI)
钱包 - ui钱包提供 UI

尚未支持

以下扩展类型在Xcode中存在,但尚未被支持. 欢迎捐款! 见 docs/xcode-target-discovery.md 关于如何发现这些细节.

扩展点标识符Xcode 模板名称
com.apple.tv-top-shelfTV 高架扩展(tvOS)
com.apple.FinderSync查找器同步扩展( macOS)
com.apple.email.extension邮件扩展( MacOS)

运行 bun scripts/scan-xcode-targets.ts --diff 从本地 Xcode 安装中重生成此列表.

代码签名

密码是完全由 EAS 构建。此插件将添加目标签名工作所需的权利。 我只用我的柱子谷部件测试过这个端到端.

如果你愿意,你也可以手动签下所有子目标,我会为你点起蜡烛.

我还没有得到 App Clip 代号的完全自动化。 公关欢迎.

建筑部件

我写了一篇博客文章 Expo x 苹果部件。 。 。 .

如果你遇到建造部件的问题, 这可能是因为 React Industrial 被运出时没有压缩, 这使得建筑的复杂性要高得多。 这往往导致与斯威夫特编译器和斯威夫特UI预览出现问题.

一些工作轮廓:

  • 清除 SwiftUI 预览缓存 : xcrun simctl --set previews delete all
  • 不重复原生的预建 : npx expo prebuild --template ./node_modules/@bacons/apple-targets/prebuild-blank.tgz --clean
  • 如果部件在构建应用程序时没有在主屏幕上显示,则使用iOS 18. 您可以按下应用程序图标并选择部件显示选项, 将应用程序图标转换为部件 .

目标之间的数据共享

要在应用程序与目标之间共享值,您必须使用 App Groups 和 NSUserDefaults 。 我增加了一个本地模块 来让反应原生API变得比较容易.

配置应用组

从定义 App 组开始, 一个好的默认值是 group.<bundle identifier>。 App Groups 可以跨应用程序使用,所以如果计划多个扩展,您可能想要更通用或更通用的东西.

首先,定义您在您的 App Group 中的主要权益 app.json数字 :

{
  "expo": {
    "ios": {
      "entitlements": {
        "com.apple.security.application-groups": ["group.bacon.data"]
      }
    },
    "plugins": ["@bacons/apple-targets"]
  }
}

第二,定义目标中相同的App组 expo-target.config.js数字 :

/** @type {import('@bacons/apple-targets/app.plugin').ConfigFunction} */
module.exports = (config) => ({
  type: "widget",
  entitlements: {
    // Use the same app groups:
    "com.apple.security.application-groups":
      config.ios.entitlements["com.apple.security.application-groups"],
  },
});

现在你可以预建 生成权利。 您可能需要创建 EAS 构建或打开 Xcode 来同步应享待遇 .

设置共享数据

为了定义共享数据,我们将使用本地模块(ExtensionStorage与 NSUserDefaults。 。 。 .

在你的博览会应用软件中,你可以设定一个值:

import { ExtensionStorage } from "@bacons/apple-targets";

// Create a storage object with the App Group.
const storage = new ExtensionStorage(
  // Your app group identifier. Should match the values in the app.json and expo-target.config.json.
  "group.bacon.data"
);

// Then you can set data:
storage.set("myKey", "myValue");

// Finally, you can reload the widget:
ExtensionStorage.reloadWidget();

ExtensionStorage 拥有下列API:

  • set(key: string, value: string | number | Record<string, string | number> | Array<Record<string, string | number>> | undefined): void - 在给定密钥的共享存储中设置一个值。 设置 undefined 将删除密钥.
  • ExtensionStorage.reloadWidget(name?: string): void - 重新装入部件的静态方法。 在幕后,这个电话 WidgetCenter.shared.reloadAllTimelines()。如果给定名称,它将使用 WidgetCenter.shared.reloadTimelines(ofKind: timeline)。 。 。 .
  • ExtensionStorage.reloadControls(name?: string): void - 重新装入控制器的静态方法。 在幕后,这个电话 ControlCenter.shared.reloadAllControls(): void。如果给定名称,它将使用 ControlCenter.shared.reloadControls(ofKind?: string): void。 。 。 .
  • remove(key: string): void - 从共享存储中移除密钥的方法.
  • get(key: string): string | null - 从共享存储中获取值的静态方法.

获取共享数据

假设使用 Swift 代码完成, 您可以使用 NSUserDefaults 直接说 以下是您如何在部件中访问数据的例子 :

let defaults = UserDefaults(suiteName:
  // Use the App Group from earlier.
  "group.bacon.data"
)
// Access the value you set:
let index = defaults?.string(forKey: "myKey")

更多数据更新

对于更先进的用途,我建议使用下列资源:

Xcode 解析

此插件使用我的专有 Xcode 解析库, @bacons/xcode大多是打入的,非常未经测试, 并且可能充满了bugs-尽管如此,它仍然是10x更好的选择.

控制部件

Simulator Screenshot - iPhone 16 - 2025-01-26 at 15 57 44

控制部件 是显示在控制中心、Siri建议、锁屏和快捷键上的部件类型.

一般情况下,您会想要将控制部件添加到一个 widget 目标,但是它们可以 添加到任何目标.

你可以加入多种意图,它们应该出现在 [target]/_shared/*.swift 文件夹,这样可以将它们添加到主要目标以及部件目标中,这就要求它们正确工作.

以下是为我的应用程序发射通用链接的控制部件的例子.

// targets/widget/_shared/intents.swift

import AppIntents
import SwiftUI
import WidgetKit

// TODO: These must be added to the WidgetBundle manually. They need to be linked outside of the _shared folder.
// @main
// struct exportWidgets: WidgetBundle {
//     var body: some Widget {
//         widgetControl0()
//         widgetControl1()
//     }
// }

@available(iOS 18.0, *)
struct widgetControl0: ControlWidget {
    // Unique ID for the control.
    static let kind: String = "com.bacon.clipdemo.0"
    var body: some ControlWidgetConfiguration {
      StaticControlConfiguration(kind: Self.kind) {
        ControlWidgetButton(action: OpenAppIntent0()) {
          // You can also use a custom image but it must be an SF Symbol.
          Label("App Settings", systemImage: "star")
        }
      }
      // This is the configuration for the widget.
      .displayName("Launch Settings")
      .description("A control that launches the app settings.")
    }
}

// This must be in both targets when `openAppWhenRun = true`. We can do that by adding it to the _shared folder.
// https://developer.apple.com/forums/thread/763851
@available(iOS 18.0, *)
struct OpenAppIntent0: ControlConfigurationIntent {
    static let title: LocalizedStringResource = "Launch Settings"
    static let description = IntentDescription(stringLiteral: "A control that launches the app settings.")
    static let isDiscoverable = true
    static let openAppWhenRun: Bool = true

    @MainActor
    func perform() async throws -> some IntentResult & OpensIntent {
        // Here's the URL we want to launch. It can be any URL but it should be a universal link for your app.
        return .result(opensIntent: OpenURLIntent(URL(string: "https://pillarvalley.expo.app/settings")!))
    }
}

你应该把意图 复制到主 WidgetBundle 结出.

从您的 App 重新装入控件

您的应用程序状态的变化可能影响控制显示 。 您可以使用

ExtensionStorage.reloadControls();

自定义图像可以使用,但必须是SF符号. 你可以用 bun create symbol /path/to/icon.svg 或 SF 符号应用程序。将 SVG 添加到目标 images 配置后将自动检测和生成 .symbolset (见 SF 符号) (中文(简体) ).

您可以用控制部件做很多事情,比如启动自定义的UI而不是打开应用程序. 此插件应允许大多数这些东西工作.

缩写

IMG_6BC9D9534F1D-1

App Clips 利用了 Expo Router 的真正力量, 使您能够链接一个网站和本地应用程序, 以便立即在 iOS 上打开本地应用程序 。 不过他们很难工作.

这是我经验楼的一些笔记 https://pillorvalley.expo.app 页面存档备份,存于互联网档案馆 (打开iOS测试).

先构建应用,再构建网站. 如果情况不对,您随时可以更新网站。 其中包括AASA和元数据.

你也许需要 此 RN 补丁 否则在试飞时会坠毁 或者,你可以在 App Store Connect 中加入 App Clip 体验,并按预期发射.

在运行预建后,打开Xcode中的项目并导航到每个目标的签名分页,这将确保第一个版本的代码签名绝对正确. 我们需要调整EAS Build,以确保它也能做到这一点.

确保您的 App Clip 不存在 expo-updates 已安装, 否则它将无法在 AppDelegate 中安装一些关于 React 缺失的加密错误 .

确保所有建筑编号都一样 CURRENT_PROJECT_VERSION 财务报告和审定财务报表 CFBundleVersion (Info.plist) 否则应用程序将无法构建.

确保您添加 public/.well-known/apple-app-site-association 文件到您的网站并部署到网络上 (eas deploy --prod) (中文(简体) ). 这里 实例。 。 。 .

值会是 <Apple Team ID>.<App Clip Bundle ID>数字 :

{
  "appclips": {
    "apps": ["QQ57RJ5UTD.com.evanbacon.pillarvalley.clip"]
  }
}

将网站 URL 添加到您的 App Clip 权限( 不是主权限) 。 举个例子 https://pillarvalley.expo.app数字 :

<key>com.apple.developer.associated-domains</key>
<array>
  <string>appclips:pillarvalley.expo.app</string>
</array>

如果没有做到这一点,那么您的 App Clip 将只能从默认 App Store URL 启动 : https://appclip.apple.com/id?p=com.evanbacon.pillarvalley.clip (其中您的 App Clip 捆绑ID 将成为 URL 中的 ID).

您应该用一个 app/+native-intent.ts 文件 :

export function redirectSystemPath({ path }: { path: string }): string {
  try {
    // Handle App Clip default page redirection.
    // If path matches https://appclip.apple.com/id?p=com.evanbacon.pillarvalley.clip (with any query parameters), then redirect to `/` path.
    const url = new URL(path);
    if (url.hostname === "appclip.apple.com") {
      // Redirect to the root path and make the original URL available as a query parameter (optional).
      return "/?ref=" + encodeURIComponent(path);
    }
    return path;
  } catch {
    return path;
  }
}

你应该用 expo-linking 获取与 App Clip 相关的 URL , 因为 上游 React 本地链接 有一些处理 App Clip 的问题 .

当您发布 App Clip 时, 二进制需要约 5 分钟才能在 App Store 出现(在它获得批准后), 但 App Clip 需要 25 分钟才能在您的网站上出现 .

您还需要在您的网站上添加一些元标记 。 这些需要快速运行 所以我建议把它们放进你的 app/+html.tsx 文件 :

<meta
  name="apple-itunes-app"
  content={
    "app-id=1336398804, app-clip-bundle-id=com.evanbacon.pillarvalley.clip, app-clip-display=card"
  }
/>

您还应添加 og:image 使用属性 expo-router/head。 。 。 . 学多点。这似乎是一条通往 png 图像的绝对路径,即 1200×630 b. 涉及各层面的以此为基础) (中文(简体) ).

<Head>
  {/* Required for app clips: */}
  {/* https://developer.apple.com/documentation/appclip/supporting-invocations-from-your-website-and-the-messages-app */}
  <meta property="og:image" content="https://pillarvalley.expo.app/og.png" />
</Head>

你也需要一个 1800x1200 用于App Store Connect图像预览的图像,因此在大约同时制作这两个图像.

从试飞发射App Clips 测试深度连接. 似乎没有任何合理的方法 测试从你的网站上启动开发。 我通过在我的应用程序的"设置 > 开发者"屏幕中设置本地体验,然后安装应用程序,打开网站,删除应用程序,然后在没有应用程序的情况下安装App Clip,来完成这项工作. 你大部分时间都得和上帝一起去.

您可以使用 CLI 工具生成代码 在此下载。 。 。 .

版本与发布记录

@bacons/apple-targets 共发布 50 个版本,首次发布于 2023/12/04;过去 12 个月发布了 11 个版本。

版本发布日期距今
5.0.0latest2026/07/183 个月前
4.0.72026/05/145 个月前
4.0.62026/03/018 个月前
4.0.52026/02/288 个月前
4.0.42026/02/278 个月前
4.0.32026/02/158 个月前

关于 @bacons/apple-targets 的常见问题

@bacons/apple-targets 是什么?

以 Expo 预建创建生成苹果目标

如何安装 @bacons/apple-targets?

在项目目录运行 npm install @bacons/apple-targets,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:expo(>=52)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@bacons/apple-targets 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@bacons/apple-targets 支持 ESM 还是 CommonJS?

以 CommonJS 形式发布,可以直接 require();在 ES Module 项目中也可以用 import 引入(Node.js 会把 module.exports 作为默认导出)。

@bacons/apple-targets 可以商用吗?

@bacons/apple-targets 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@bacons/apple-targets 的下载量有多少?

根据 npm 官方下载统计,@bacons/apple-targets 在 2026/10/08 – 2026/11/06 这 30 天内被下载 3,224,156 次,日均约 107,472 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@bacons/apple-targets 还在维护吗?

最新版本 5.0.0 发布于 2026/07/18(3 个月前);至今共发布 50 个版本,其中过去 12 个月发布了 11 个;首次发布于 2023/12/04。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。