title: macOS 插件实现与打包 date: 2026-08-27 tags:
- AiPPT
- macOS
- PowerPoint
- PPAM
- Swift
- PKG status: active
macOS 插件实现与打包
快速打包
WebView前置条件
sign_and_notarize.command只消费已经同步到macOS仓库的离线页面,不会构建前端或解压offline.zip。WebView有改动时,先执行WebView实现与打包 > 快速打包,再清空并解压到:
App/macos-app/src/offline/交互打包:
./AiPPT-pkg/AiPPTAaddin/Scripts/sign_and_notarize.command脚本询问版本、渠道、架构、签名、公证Profile和Git发布参数。
正式非交互发布:
./AiPPT-pkg/AiPPTAaddin/Scripts/sign_and_notarize.command \
--version 2.0.3 \
--source PLUGIN \
--archs "arm64 x86_64" \
--notary-profile aippt-profile \
--yes正式流程编译Universal Binary,签名App和PKG,提交Apple公证并Staple,然后提交版本、创建annotated Tag并原子推送分支和Tag。
只生成本地测试包:
./AiPPT-pkg/AiPPTAaddin/Scripts/sign_and_notarize.command \
--version 2.0.3 \
--source PLUGIN \
--archs "arm64 x86_64" \
--skip-notarization \
--no-git-release \
--yes默认产物:
AiPPT-pkg/AiPPTAaddin/AiPPT 插件-<version>.pkg正式发布要求Developer ID Application、Developer ID Installer和notarytool Keychain Profile可用。
实现概览
macOS端由四个产物组成:
| 产物 | 作用 |
|---|---|
AiPPT.ppam | PowerPoint启动加载项,提供Ribbon和VBA入口 |
AiPPTAddin.bundle | Swift dylib,处理窗口、网络和PPT操作 |
AiPPT.app | 独立桌面程序,提供工作台和PPAM修复安装 |
AiPPT 插件-<version>.pkg | 安装以上文件的分发包 |
运行链路:
PowerPoint加载AiPPT.ppam
→ VBA调用Swift dylib导出函数
→ rootRouter分发Native方法
→ Swift操作PowerPoint或打开NSPanel
→ WKWebView加载共享前端页面仓库:
aippt-macos-addin1. AiPPT.ppam
源文件:
AiPPTAddin/AiPPT.pptm分发文件:
AiPPT.ppam安装位置:
~/Library/Group Containers/UBF8T346G9.Office/
User Content.localized/Startup.localized/PowerPoint/AiPPT.ppamPowerPoint启动时自动加载该文件。PPAM内的VBA负责:
- Ribbon回调;
- 调用Swift dylib;
- 传递UTF-16字符串和JSON参数;
- 接收Native返回值。
VBA依赖固定的dylib路径:
/Library/Application Support/Microsoft/
AiPPTAddin.bundle/Contents/MacOS/AiPPTAddin修改导出函数名、参数格式或安装路径时,VBA和Swift必须一起改。
2. AiPPTAddin.bundle
工程:
AiPPTAddin/AiPPTAddin.xcodeproj主二进制是Swift dylib,放在:
AiPPTAddin.bundle/Contents/MacOS/AiPPTAddinVBA入口
AiPPTAddin/AiPPTBridge/Globals.swift主要导出:
| 导出函数 | 用途 |
|---|---|
initRibbonConfig | 初始化Ribbon配置、离线资源和Client |
executeSwiftMessage | VBA到Swift的统一消息入口 |
getImageSize | 图片尺寸 |
getImageFromApp | 下载或读取图标 |
freeUTF16Pointer | 释放返回字符串 |
getContextMenuVisible | 右键菜单可见性 |
业务路由:
executeSwiftMessage(method, params)
→ rootRouter
→ Native方法
→ JSON结果
→ UTF-16返回VBANative实现
AiPPTAddin/Native/包含:
- Application、Slides和Shapes;
- Browser和窗口;
- Libraries;
- UniformFont/UniformColor;
- ShapeConnector和DesignTools;
- 导出PDF、视频和长图;
- PPT拼图;
- 文件和系统设置。
窗口
AppDelegate.createWindow
→ NSPanel
→ NSHostingView
→ WebBrowserView
→ WKWebViewNSPanel按业务ID复用。它是浮动窗口,不是PowerPoint原生停靠TaskPane。
3. AiPPT.app
工程:
App/macos-app/src/AiPPT.xcworkspace安装名称:
/Applications/AiPPT插件.app职责:
- 独立AiPPT工作台;
- 素材库;
- AppleScript驱动PowerPoint;
- 检查并修复PPAM安装;
- 本地文件和设置;
- StoreKit内购代码骨架。
PPAM修复安装:
OfficePowerPointStartupFolderAccess
→ NSOpenPanel获取Startup目录授权
→ 保存security-scoped bookmark
→ 复制App资源中的AiPPT.ppamStoreKit有商品查询和购买队列代码,但收据校验和权益发放没有完成。状态按“有代码,未完成交易闭环”处理。
4. 代码保护状态
macOS端没有使用.NET Reactor、UPX或其他专用混淆器。
| 产物 | 当前保护 |
|---|---|
AiPPTAddin.bundle | Swift原生编译、Release -O和whole-module optimization |
AiPPT.app | Swift原生编译、Release -O和dead code stripping |
| Web离线资源 | Webpack压缩和变量名缩短 |
| PKG | Developer ID签名、Apple公证和Staple |
这些能力不等于代码混淆。当前Add-in工程仍配置:
COPY_PHASE_STRIP = NO
DEAD_CODE_STRIPPING = NO
GCC_SYMBOLS_PRIVATE_EXTERN = NO因此Release dylib可能保留较多符号。增加符号裁剪时必须保留:
@_silgen_name导出函数
VBA/Swift Bridge入口
Objective-C运行时方法
WKWebView消息处理器
AppleScript调用入口错误裁剪会导致PPAM加载成功但VBA无法调用Swift实现。
5. 开发依赖
| 依赖 | 用途 |
|---|---|
| Xcode | 编译dylib和App |
| PowerPoint macOS | 宿主验证 |
| CocoaPods | App工程依赖安装 |
| Swift Package Manager | Swift运行依赖 |
| Packages/packagesbuild | 生成PKG |
| Developer ID Application | 签bundle、dylib和App |
| Developer ID Installer | 签PKG |
| notarytool | Apple公证 |
| WKWebView离线资源 | 插件页面 |
主要SPM依赖:
SwifterSwift
SwiftyJSON
ZIPFoundation
SDWebImageSwiftUI
Async
swift-collectionsCocoaPods中的SwiftLint和SwiftFormat属于开发工具。
6. 构建和组装
WebView离线资源
输入目录:
App/macos-app/src/offline/完整链路:
aippt-frontend-addin的dist/offline.zip
→ 人工清空并解压到App/macos-app/src/offline
→ Xcode rsync到AiPPT.app/Contents/Resources/offline
→ sign_and_notarize.command复制到AiPPTAddin.bundle/Contents/Resources/offline
→ Packages Payload同时包含AiPPT.app和AiPPTAddin.bundle
→ PKG安装安装后有两条加载路径:
| 使用方 | 离线页面位置 | 加载方式 |
|---|---|---|
| PowerPoint插件 | PowerPoint沙盒内Application Support/AiPPT/offline/ | Swift把offline/<page>.html解析到home目录,WKWebView调用loadFileURL |
AiPPT插件.app | App自身Contents/Resources/offline/ | Bundle.main加载offline/desktop.html |
postinstall把bundle资源中的offline/同步到:
~/Library/Containers/com.microsoft.Powerpoint/Data/
Library/Application Support/AiPPT/offline/运行时还会检查服务端offline包版本;有更新时下载offline.zip并解压到home目录。
人工边界只有第一步:前端offline.zip同步到App/macos-app/src/offline/。后续Xcode、Payload组装、PKG安装和WKWebView加载均自动完成。
当前缺失处理存在风险:
App/macos-app/src/offline/不存在时,打包脚本跳过复制,不会失败;postinstall找不到offline目录时只记录警告,不会让安装失败;- 正式发布前必须检查bundle和App中都存在
offline/desktop.html及业务HTML。
编译Swift dylib
xcodebuild AiPPTAddin
configuration=Release
platform=macOS
archs=arm64 x86_64编译AiPPT.app
pod install --deployment
xcodebuild AiPPT.app
MARKETING_VERSION=<version>
archs=arm64 x86_64组装Payload
Payload/
├── AiPPTAddin.bundle/
│ ├── Contents/MacOS/AiPPTAddin
│ ├── Contents/Info.plist
│ └── Contents/Resources/
│ ├── offline/
│ ├── AiPPT.ppam
│ └── 模板PPTX
└── AiPPT.app/
└── Contents/Resources/AiPPT.ppam打包资源:
AiPPT.ppam
Standard.pptx
SingleStandardIconBlack.pptx
AiPPTPluginOverview.pptx
offline/
Resources/7. 签名
顺序:
签AiPPTAddin主二进制
→ 签AiPPTAddin.bundle
→ 签AiPPT.app
→ codesign --verify
→ packagesbuild
→ productsign签PKG
→ pkgutil --check-signature证书:
| 证书 | 对象 |
|---|---|
| Developer ID Application | dylib、bundle、App |
| Developer ID Installer | PKG |
AiPPT.app签名使用AiPPT.entitlements。
8. 公证
xcrun notarytool submit <pkg> --keychain-profile <profile> --wait
→ xcrun stapler staple <pkg>
→ xcrun stapler validate <pkg>
→ spctl -a -t install -vv <pkg>通过标准:
notarytool status = Accepted
stapler validate成功
spctl source = Notarized Developer ID9. PKG结构
Packages工程:
AiPPT-pkg/AiPPTAaddin/AiPPTAddin.pkgproj初始安装位置:
/Library/Application Support/AiPPTPKG包含:
AiPPTAddin.bundle
AiPPT.app
preinstall
postinstall10. preInstall和postinstall
preInstall
删除旧Payload中的bundle和App,处理文件flags、权限和隔离属性。
postinstall
顺序:
- 识别当前控制台用户;
- 关闭PowerPoint;
- 复制PPAM到PowerPoint Startup目录;
- 复制模板和离线资源到PowerPoint容器;
- 原子替换
AiPPTAddin.bundle; - 安装
AiPPT插件.app; - 写入版本和渠道设置;
- 启动PowerPoint和App;
- 上报
install_success。
日志:
/tmp/aippt-postinstall.log关键安装位置:
| 文件 | 位置 |
|---|---|
| PPAM | PowerPoint Startup目录 |
| dylib bundle | /Library/Application Support/Microsoft/AiPPTAddin.bundle |
| 桌面App | /Applications/AiPPT插件.app |
| 离线资源 | PowerPoint沙盒容器内的AiPPT目录 |
11. 发布产物
AiPPT 插件-<version>.pkg发布前检查:
- App和dylib都是
arm64 x86_64; codesign --verify --deep --strict通过;- PKG使用Developer ID Installer签名;
- Apple公证Accepted;
- Staple有效;
AiPPT.app/Contents/Resources/offline/desktop.html存在;AiPPTAddin.bundle/Contents/Resources/offline/包含业务HTML;- 安装后PowerPoint沙盒的
Application Support/AiPPT/offline/已同步; - 安装后PowerPoint能自动加载PPAM;
- VBA能找到固定路径下的dylib;
- NSPanel能打开在线和离线页面;
- App能修复PPAM。
12. 已知约束
- VBA和Swift的ABI契约只体现在导出函数名、UTF-16和JSON格式中;
- dylib安装路径被VBA引用,不能单边修改;
- PPAM受PowerPoint宏安全策略影响;
codesign --deep应谨慎使用,嵌套代码最好逐层签名;- notarytool依赖本机Keychain profile;
- Packages工程格式可能受Packages版本影响;
- App Store内购未形成交易闭环。