PAYATHON 2026

Flutter auto_updater 更新后 Finder/Dock 仍显示旧应用名怎么办?

数据小许

结论

定制构建 Sparkle 时,可以启用 SPARKLE_NORMALIZE_INSTALLED_APPLICATION_NAME。这是 Sparkle 自身的编译配置,无法通过修改 Flutter Runner 的构建参数、添加 Dart 配置或写入同名 Info.plist 键来改变已编译的 Sparkle framework 二进制的行为。

仅修改 bundle display name,无法保证 /Applications/旧名称.app 自动更名为 /Applications/新名称.app。需要分别检查应用元数据、磁盘上的 .app 文件名和 Dock 中保留的条目。

问题没有提供 auto_updater、Sparkle 的版本和依赖锁定信息。下面以 Sparkle 官方 2.x 分支的实现说明原理,实际接入时应核对项目所用版本。

为什么安装成功,名称却没有变化?

原问题中的更新已经能够下载和安装,故障集中在名称迁移。Sparkle 默认会沿用宿主应用的安装路径,因此更新可能安装成功,磁盘上的 .app 文件名却仍然是旧名称。

Sparkle 官方配置中的 SPARKLE_NORMALIZE_INSTALLED_APPLICATION_NAME 默认值为 0,用于控制是否在更新时规范化应用文件名。官方还提醒,如果用它进行应用改名,应在不再需要时关闭。参见 Sparkle 构建配置。

几个名称的用途如下:

配置或对象主要用途
CFBundleDisplayName应用的显示名称,可受本地化配置影响
CFBundleNamebundle 的名称元数据
SUBundleNameSparkle 使用的替代显示名,也参与其文件名规范化逻辑
OldName.app磁盘上的实际应用包文件名
Dock 条目需要确认它最终指向哪个应用副本

SUBundleName 在官方配置中是可选的替代显示名,仅设置它不能保证磁盘文件名改变。参见 Sparkle 自定义配置。

如何通过 auto_updater 启用这个 flag?

1. 先确认 Sparkle 的实际依赖方式

查看 macos/Podfile.lock 中解析到的 Sparkle 版本,再检查对应的 podspec 和依赖内容。

auto_updater_macos 的公开 podspec 声明了对 Sparkle 的依赖,其原生方法通道没有提供设置这个编译 flag 的接口。参见插件依赖声明和原生方法实现。

需要确认 Sparkle 是否在项目构建时从源码编译:

  • 如果引入的是预编译 framework,必须替换为启用了该配置的自定义构建。
  • 如果依赖确实从源码编译,可以在编译 Sparkle 的工程及相关安装组件时调整配置。

只给 Runner 或插件包装层添加宏不够,控制安装行为的 Sparkle 代码也必须使用该配置重新编译。

2. 构建并接入定制 Sparkle

对所用 Sparkle 版本的源码设置:

SPARKLE_NORMALIZE_INSTALLED_APPLICATION_NAME = 1

目前官方 2.x 分支对应的文件是 Configurations/ConfigCommon.xcconfig,与问题中写出的文件名不同。其他版本应以其源码为准。

随后构建完整的 Sparkle 分发产物,通过受版本控制的本地或私有 podspec 等方式接入,必要时维护 auto_updater_macos 的 fork。不要只临时修改 Pods 目录,重新安装依赖可能覆盖这些修改。

接入时还要避免同时嵌入两份 Sparkle,并按项目发布流程完成嵌套组件签名、应用签名及公证。

应用名称应该怎样配置?

假设新名称为 NewName,可以在 macos/Runner/Info.plist 的现有 <dict> 中配置:

<key>CFBundleName</key>
<string>NewName</string>
<key>CFBundleDisplayName</key>
<string>NewName</string>
<key>SUBundleName</key>
<string>NewName</string>

这是示例。实际项目还需要检查构建变量和 InfoPlist.strings 是否覆盖这些值。如果新发布产物也需要命名为 NewName.app,还要检查 Runner 的 PRODUCT_NAME。修改前应确认 CFBundleExecutable 与实际可执行文件保持一致。

Sparkle 当前的规范化函数优先读取宿主应用的 SUBundleName,缺失时读取 CFBundleName,再拼接应用包扩展名。该函数没有使用 CFBundleDisplayName 作为回退,因此修改 display name 后,仍需检查规范化文件名的配置。参见 SUNormalization.m。

为保留同一应用的升级身份,改名时通常应保持 CFBundleIdentifier 不变。

已经发布的旧版本怎样迁移?

执行更新的是用户当前安装版本所携带的更新器。只在下载的新包里加入定制 Sparkle,不能让旧更新器提前获得新的安装行为。

当前规范化实现读取的是正在被更新的宿主应用元数据。如果旧应用里仍是旧名称,即使它已启用规范化,也不能仅凭新包中的名称推断本次安装会改名。依据见 Sparkle 安装路径选择逻辑。

根据这些实现,可以设计分阶段迁移:

  1. 发布过渡版本,写入新的名称元数据,并携带启用了规范化的 Sparkle。用户从旧版升级到过渡版时,磁盘文件名可能仍保留旧名称。
  2. 过渡版本运行后,由它携带的更新器执行下一次更新,才具备按新名称规范化安装路径的条件。
  3. 名称迁移完成后,再评估关闭规范化配置,避免后续更新持续重置用户自行修改的文件名。

这一迁移方案根据源码推导,仍需要用实际版本验证。还必须考虑用户跳过过渡版本的情况:如果旧版能够直接升级到最新版本,就不能假定所有用户都已完成迁移。

当前安装逻辑发现规范化目标路径已经存在时,会回退到原宿主路径。因此,如果目录中已有另一份 NewName.app,不能期待该 flag 自动处理冲突。

不定制 Sparkle,有哪些替代方案?

可以根据改名要求选择:

  • 只需要调整显示名称:更新应用元数据并验证界面显示,接受磁盘上的旧 .app 文件名继续存在。
  • 必须统一磁盘文件名:让用户退出应用,并确认更新安装进程结束后,在 Finder 中手动重命名应用包;随后从新路径启动并重新固定 Dock 条目。
  • 需要受控地迁移大量安装:设计单独的安装或迁移流程,处理目录权限、目标重名、旧副本清理和失败恢复。不要在 Sparkle 正在替换应用时并行移动应用包。

手动改名适合少量安装或作为兜底。自动迁移需要额外的原生安装逻辑,不能靠一个 Dart 配置完成。

Finder 正常后,Dock 仍显示旧名称怎么办?

先确认更新后的实际文件。下面的命令只读取信息;请把路径替换为当前真正安装的应用路径:

APP='/Applications/OldName.app'

/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' "$APP/Contents/Info.plist"
/usr/libexec/PlistBuddy -c 'Print :CFBundleName' "$APP/Contents/Info.plist"
/usr/libexec/PlistBuddy -c 'Print :CFBundleDisplayName' "$APP/Contents/Info.plist"
/usr/libexec/PlistBuddy -c 'Print :SUBundleName' "$APP/Contents/Info.plist"

某个可选键不存在时,命令会报找不到该条目,这本身不代表应用损坏。还应检查 Contents/Resources 下的本地化名称配置。

确认应用包路径、元数据和运行版本都正确后,退出应用,移除 Dock 中保留的旧条目,再从目标路径启动并重新固定。若系统中存在多个副本,可通过 Dock 的“在 Finder 中显示”核对实际指向。

如果磁盘上仍然是旧路径,刷新 Dock 无法替代文件名迁移;如果元数据仍为旧值,应先修正构建产物。

发布前至少验证旧版直接升级、经过过渡版升级、目标名称已存在,以及用户自行改过文件名这几种情况,并确认升级后仍能继续检查和安装下一次更新。

备注:内容仅供参考。