Skip to content

electron mas 开发-打包-签名-公证-测试(testflight)文档

Mac 电脑苹果芯片(m2)环境下配置

一、electron builder 配置

1. 编写配置文件 electron-builder.config.js

package.json

json
{
  "scripts": {
    "build": "electron-builder -c electron-builder.config.js"
  }
}

.env

环境变量

VUE_APP_API_VERSION=1.16

# 自定义协议
VUE_APP_CUSTOM_FILE_PROTOCAL_WIN32=app
VUE_APP_CUSTOM_FILE_PROTOCAL_MAC=yourapp_app
# 加载本地图片资源
VUE_APP_LOCAL_RESOURCE=yourapp-local-resource

VUE_APP_MAC_TARGET=mas

VUE_APP_WIN32_ARCH=x64
js
const isMasDev = process.env.VUE_APP_MAC_TARGET === "mas-dev";
const isWin32 = process.platform === "win32";
const isDorwin = process.platform === "darwin";

module.exports = {
  cache: false,
  builderOptions: {
    afterSign:
      isDorwin && ["mas"].includes(process.env.VUE_APP_MAC_TARGET)
        ? path.join(__dirname, "build/notarize.js")
        : undefined,
    appId: isDorwin ? "你的苹果开发者id" : "你的Win开发者id",
    icon: isDorwin
      ? path.join(__dirname, "icons/mac/icon.icns")
      : path.join(__dirname, "./icons/win/icon.ico"),
    productName: "你的app名称",
    // eslint-disable-next-line no-template-curly-in-string
    artifactName: "你的app名称-${os}-${arch}-${version}.${ext}",
    copyright: "Copyright © 2023 your_company All rights reserved.",
    directories: { output: "dist_electron" },
    win: {
      icon: path.join(__dirname, "./icons/win/icon.ico"),
      target: [
        {
          target: "nsis",
          arch: [process.env.VUE_APP_WIN32_ARCH === "ia32" ? "ia32" : "x64"],
        },
      ],
    },
    nsis: {
      oneClick: false, // 设置为 false 以提供安装类型选择界面,允许用户选择是否创建桌面图标,允许用户选择安装路径
      perMachine: true, // 设置为 true 将使安装程序默认为所有用户安装应用,这需要管理员权限
      allowToChangeInstallationDirectory: true, // 如果设置为 true,安装程序将允许用户更改安装目录
      allowElevation: true, // 一般情况下,此字段不会被直接使用,权限提升主要依赖于 perMachine 的设定。当perMachine为true,安装程序会请求管理员权限
      deleteAppDataOnUninstall: true, // 如果设置为 true,卸载程序将删除AppData中的所有程序数据
      createDesktopShortcut: true, // 如果设置为 true,安装程序将在桌面上创建程序快捷方式
      createStartMenuShortcut: true, // 如果设置为 true,安装程序将在开始菜单中创建程序快捷方式
      shortcutName: "你的app名称", // 设置在桌面和开始菜单中创建的快捷方式的名称
      // license: path.join(__dirname, 'build/license_zh.rtf')
    },
    publish: [
      {
        provider: "generic",
        channel: "alpha",
        url: "",
      },
    ],
    asar: true,
    electronLanguages: isWin32
      ? ["en", "zh-TW", "zh-CN", "en-US", "en-GB"]
      : ["en", "zh_TW", "zh_CN", "en_US", "en_GB"],
    masDev: {
      type: "development",
      icon: "icons/mac/icon.icns",
      identity: process.env.IdentityNameMacDeveloper,
      hardenedRuntime: false,
      entitlements: "./entitlements.mas.plist",
      entitlementsInherit: "./entitlements.mas.inherit.plist",
      entitlementsLoginHelper: "./entitlements.mas.loginhelper.plist",
      provisioningProfile:
        "./provisionprofile/yourappMacDevelopment.provisionprofile",
    },
    mas: {
      type: "distribution",
      icon: "icons/mac/icon.icns",
      hardenedRuntime: false,
      identity: process.env.IdentityName3rdPartyMacDeveloperApplication,
      entitlements: "./entitlements.mas.plist",
      entitlementsInherit: "./entitlements.mas.inherit.plist",
      entitlementsLoginHelper: "./entitlements.mas.loginhelper.plist",
      provisioningProfile:
        "./provisionprofile/yourappMac_App_Store.provisionprofile",
    },
    mac: isWin32
      ? {}
      : {
          type: isMasDev ? "development" : "distribution",
          target: [
            { target: process.env.VUE_APP_MAC_TARGET, arch: "universal" },
          ],
          identity: process.env.IdentityNameDeveloperIDApplication,
          bundleVersion: process.env.VUE_APP_MAC_VERSION || undefined,
          icon: "icons/mac/icon.icns",
          mergeASARs: false,
          singleArchFiles: "*",
          x64ArchFiles: "*",
          category: "public.app-category.productivity",
          extendInfo: {
            ElectronTeamID: "你的TeamID",
            LSHasLocalizedDisplayName: true,
            ITSAppUsesNonExemptEncryption: "NO",
            NSApplicationDelegate: "YES",
            NSBluetoothAlwaysUsageDescription: null,
            NSBluetoothPeripheralUsageDescription: null,
            NSCameraUsageDescription: null,
            NSMicrophoneUsageDescription: null,
            CFBundleLocalizations: ["en", "zh_TW", "zh_CN"],
            CFBundleURLTypes: [
              {
                CFBundleTypeRole: "Editor",
                CFBundleURLName: "appURLHandler",
                CFBundleURLSchemes: [
                  process.env.VUE_APP_CUSTOM_FILE_PROTOCAL_MAC,
                ],
              },
              {
                CFBundleTypeRole: "Editor",
                CFBundleURLName: "localResourceHandler",
                CFBundleURLSchemes: [process.env.VUE_APP_LOCAL_RESOURCE],
              },
              {
                CFBundleTypeRole: "Editor",
                CFBundleURLName: "openURLHandler",
                CFBundleURLSchemes: ["hfc"],
              },
            ],
            "com.apple.security.app-sandbox": true,
            "com.apple.security.application-groups": ["你的TeamID.你的appid"],
            "com.apple.application-identifier": "你的TeamID.你的appid",
            "com.apple.security.cs.allow-jit": true,
            "com.apple.security.cs.allow-dyld-environment-variables": true,
            "com.apple.security.cs.disable-library-validation": true,
            "com.apple.security.network.client": true,
            "com.apple.security.network.server": true,
            "com.apple.security.files.user-selected.read-write": true,
            "com.apple.security.files.user-selected.read-only": true,
            "com.apple.security.files.bookmarks.document-scope": true,
            "com.apple.security.files.bookmarks.app-scope": true,
            // eslint-disable-next-line max-len
            // https://developer.apple.com/library/archive/documentation
            // /Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html
            // #//apple_ref/doc/uid/TP40011195-CH4-SW6
            // Downloads Folder
            "com.apple.security.files.downloads.read-write": true,
            // Music Folder
            "com.apple.security.assets.music.read-only": true,
            "com.apple.security.assets.music.read-write": true,
            // Movies Folder
            "com.apple.security.assets.movies.read-only": true,
            "com.apple.security.assets.movies.read-write": true,
            // Pictures Folder
            "com.apple.security.assets.pictures.read-only": true,
            "com.apple.security.assets.pictures.read-write": true,
            // Allow USB Access
            "com.apple.security.device.usb": true,
            NSLocalNetworkUsageDescription:
              "此应用需要访问局域网来上传或者下载文件。",
            NSDesktopFolderUsageDescription:
              "此应用需要访问您的桌面文件夹,以便您可以浏览或上传或下载文件。",
            NSDocumentsFolderUsageDescription:
              "此应用需要访问您的文档文件夹,以便您可以浏览或上传或下载文件。",
            NSDownloadsFolderUsageDescription:
              "此应用需要访问您的下载文件夹,以便您可以浏览或上传或下载文件。",
            NSMusicFolderUsageDescription:
              "此应用需要访问您的音乐文件夹,以便您可以浏览或上传或下载文件。",
            NSPicturesFolderUsageDescription:
              "此应用需要访问您的图片文件夹,以便您可以浏览或上传或下载文件。。",
            NSMoviesFolderUsageDescription:
              "此应用需要访问您的电影文件夹,以便您可以浏览或上传或下载文件。",
          },
        },
    asarUnpack: ["**/sqlite3/**/*"],
    files:
      process.platform === "win32"
        ? [
            "**/*",
            "!**/node_modules/*/{CHANGELOG.md,README.md,README,readme.md,readme}",
            "!**/node_modules/*/{test,__tests__,tests,powered-test,example,examples}",
            "!**/node_modules/*.d.ts",
            "!**/node_modules/.bin",
            "!**/*.{iml,o,hprof,orig,pyc,pyo,rbc,swp,csproj,sln,xproj}",
            "!.editorconfig",
            "!**/._*",
            "!**/{.DS_Store,.git,.hg,.svn,CVS,RCS,SCCS,.gitignore,.gitattributes}",
            "!**/{__pycache__,thumbs.db,.flowconfig,.idea,.vs,.nyc_output}",
            "!**/{appveyor.yml,.travis.yml,circle.yml}",
            "!**/{npm-debug.log,yarn.lock,.yarn-integrity,.yarn-metadata.json}",
            "!node_modules/sqlite3/**/*",
            "!node_modules/sqlite3/node_modules/**/*",
            "node_modules/sqlite3/package.json",
            "node_modules/sqlite3/lib/**",
            "node_modules/sqlite3/deps/**",
            "!**/*/*.md",
          ]
        : [
            "**/*",
            "!**/node_modules/*/{CHANGELOG.md,README.md,README,readme.md,readme}",
            "!**/node_modules/*/{test,__tests__,tests,powered-test,example,examples}",
            "!**/node_modules/*.d.ts",
            "!**/node_modules/.bin",
            "!**/*.{iml,o,hprof,orig,pyc,pyo,rbc,swp,csproj,sln,xproj}",
            "!.editorconfig",
            "!**/._*",
            "!**/{.DS_Store,.git,.hg,.svn,CVS,RCS,SCCS,.gitignore,.gitattributes}",
            "!**/{__pycache__,thumbs.db,.flowconfig,.idea,.vs,.nyc_output}",
            "!**/{appveyor.yml,.travis.yml,circle.yml}",
            "!**/{npm-debug.log,yarn.lock,.yarn-integrity,.yarn-metadata.json}",
            "!node_modules/sqlite3/**",
            "!node_modules/sqlite3/node_modules/**/*",
            "node_modules/sqlite3/package.json",
            "node_modules/sqlite3/lib/**",
            "node_modules/sqlite3/deps/**",
            "!**/*/*.md",
          ],
  },
};

2. 代码公证 build/notarize.js

js
// build/notarize.js
const { notarize } = require("@electron/notarize");
const path = require("path");

exports.default = async function notarizing(context) {
  require("dotenv").config({ path: path.join(__dirname, "..", ".env.build") });
  try {
    const appName = context.packager.appInfo.productFilename;
    const { electronPlatformName, appOutDir } = context;
    console.log("[notarizing electronPlatformName]", electronPlatformName);
    console.log("[notarizing appOutDir]", appOutDir);
    if (process.platform !== "darwin") {
      return;
    }

    const appPath = `${appOutDir}/${appName}.app`;
    const appleId = process.env.NotarizeAppleId;
    const appleIdPassword = process.env.NotarizeAppleIdPassword;
    const teamId = process.env.NotarizeTeamId;
    const appBundleId = process.env.NotarizeAppBundleId;

    console.log("[notarizing appPath]", appPath);

    // https://juejin.cn/post/7009179524520738824?searchId=20231129211331F94C9E5ED49AEF27EF65#heading-8
    console.log("[notarizing 公证信息中...]", {
      appBundleId, // appid
      appPath, // 应用的路径 xxx.app 结尾的
      appleId, // 苹果开发者 id
      appleIdPassword: "****-****-****-****", // 应用专用密码
      teamId,
    });

    if (!appleIdPassword) {
      return;
    }

    await notarize({
      appPath,
      appBundleId,
      appleId,
      appleIdPassword,
      teamId,
    });
    console.log("[notarizing 公证成功]");
  } catch (err) {
    console.error("[notarizing 公证失败]", err);
  }
};

plish 文件

需要修改 entitlements.mas.plist 部分内容:

xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>com.apple.security.app-sandbox</key>
    <true/>
    <key>com.apple.security.application-groups</key>
    <array>
      <string>TeamId.AppId</string>
    </array>
    <key>com.apple.application-identifier</key>
    <string>TeamId.AppId</string>
    <key>com.apple.developer.team-identifier</key>
		<string>TeamId</string>
  </dict>
</plist>

TeamId、AppId 部分要改成你自己的,比如 AppId 为 com.hello.yourapp,TeamId 为 xxxxxxx,那么内容为:

xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>com.apple.security.app-sandbox</key>
    <true/>
    <key>com.apple.security.application-groups</key>
    <array>
      <string>xxxxxxx.com.hello.yourapp</string>
    </array>
    <key>com.apple.application-identifier</key>
    <string>xxxxxxx.com.hello.yourapp</string>
    <key>com.apple.developer.team-identifier</key>
		<string>xxxxxxx</string>
  </dict>
</plist>

二、运行项目

1. 环境安装(Mac)

  1. 安装开发工具 VScode https://code.visualstudio.com/download

  2. 安装 node.js https://nodejs.org/dist/v18.17.0/node-v18.17.0.pkg

bash
node -v
v18.17.0
bash
npm -v
9.6.7

2. 全局安装 node-gyp

bash
sudo npm install node-gyp -g
bash
node-gyp -v
v9.4.0

3. 下载 phantomjs-2.0.0-macosx.zip

https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-macosx.zip

解压后放到指定目录 phantomjs-2.1.1-macosx.zip

bash
mkdir -p ~/project/phantomjs-2.1.1-macosx

解压到 ~/project/phantomjs-2.1.1-macosx 目录

bash
unzip ~/Downloads/phantomjs-2.1.1-macosx.zip -d ~/project/phantomjs-2.1.1-macosx

4. 修改环境变量,配置

bash
vim ~/.zshrc

增加下面配置

export NODE_GYP_FORCE_PYTHON="/usr/bin/python3"
export PATH=$PATH:~/project/phantomjs-2.1.1-macosx/bin

修改后记得执行下面命令,使得配置文件生效

bash
source ~/.zshrc

验证 phantomjs 是否安装成功

bash
phantomjs --version

如果输出版本信息,那么就是成功安装了

成功的情况 2.1.1

bash
phantomjs --version
2.1.1

失败的情况,输出 zsh: bad CPU type in executable: ./phantomjs

bash
phantomjs --version
zsh: bad CPU type in executable: ./phantomjs

解决zsh: bad CPU type in executable错误

参考 https://apple.stackexchange.com/questions/408375/zsh-bad-cpu-type-in-executable

  1. 安装 rosetta
bash
softwareupdate --install-rosetta
  1. 命令行兼容 x64 架构的应用
bash
arch -x86_64 zsh
  1. 重新验证 phantomjs
bash
phantomjs --version
2.1.1

5. 安装模块依赖

bash
npm install -f

6. 运行重新构建原生模块

json
{
  "scripts": {
    "electron:build": "npm run build:prod && npm run electron:rebuild && electron-builder build --publish=never",
    "electron:rebuild": "npm run rebuild:mac:x64 && npm run rebuild:mac:arm64",
    "rebuild:mac:x64": "electron-rebuild -f --arch=x64",
    "rebuild:mac:arm64": "electron-rebuild -f --arch=arm64"
  }
}

如果不正确执行下面命令,会导致开发环境调用动态库失败,故需要根据电脑芯片执行不同的命令

如果是 x64 电脑运行这个命令

bash
npm run rebuild:mac:x64

如果是 arm64 (苹果芯片 m1、m2、m3 等)电脑运行下面命令

bash
npm run rebuild:mac:arm64

7. 开发环境启动项目

bash
npm start

三、打包-签名-公证

苹果开发者官网地址:https://developer.apple.com/account/

1. 苹果开发者中间证书

需要安装 Apple PKI 公共密钥基础设施 https://www.apple.com/certificateauthority/

安装下面图片红色方块内的

Alt text

注意(大坑):Apple Worldwide Developer Relations Certification Authority 证书要放在“系统”里面,不然会导致打包 mas-dev 失败或者 mas-dev 的免安装程序(yourname.app) 打不开

Alt text

2. 安装相关证书和可描述文件

需要准备下面 5 个证书

  1. Mac Developer: Zhongyi Li (6CXXXXXXXX)
  2. Developer ID Application: Shenzhen your_company Technology Co., Ltd. (U2XXXXXXXX)
  3. Developer ID Installer: Shenzhen your_company Technology Co., Ltd. (U2XXXXXXXX)
  4. 3rd Party Mac Developer Application: Shenzhen your_company Technology Co., Ltd. (U2XXXXXXXX)即(Mac Application Distribution)
  5. 3rd Party Mac Developer Installer: Shenzhen your_company Technology Co., Ltd. (U2XXXXXXXX)即(Mac Installer Distribution)

alt text

1. Mac Developer: Zhongyi Li (6CXXXXXXXX) 证书

如何获取 Mac Developer 证书
  1. 创建证书请求文件

    • 在您的 Mac 上打开“钥匙串访问”。
    • 选择“钥匙串访问”>“证书助理”>“从证书颁发机构请求证书”。
    • 输入您的电子邮件地址,选择“保存到磁盘”,然后继续以生成证书签名请求(CSR)文件。
  2. 在苹果开发者账户中申请证书

    • 登录您的苹果开发者账户
    • 导航到“Certificates, Identifiers & Profiles (证书、标识符和描述文件)”。 Alt text
    • 选择“Certificates(证书(英文))”并点击“+”来创建新证书。 Alt text
    • 选择适合您需求的证书类型(例如,iOS 开发、Mac 开发等),并上传之前创建的 CSR 文件。 Alt textAlt text
  3. 下载并安装证书

    • 审核通过后,您可以下载证书。 Alt textAlt text
    • 双击下载的证书文件将其安装到您的 Mac 上。 Alt text
    • 打开钥匙串访问 Alt text
Mac Developer 证书的作用

这个证书在打包成 mas-dev 包的时候,需要指定这个证书

2. 获取 Developer ID Application: Shenzhen YourCompany Technology Co., Ltd. (U2XXXXXXXX) 证书

如何获取 Developer ID Application 证书

与获取 Mac Developer 流程一样

Developer ID Application 证书的作用

对代码进行签名,mas 和 mas-dev 的时候 mac 配置项都需要指定该证书进行签名

3. Developer ID Installer: Shenzhen YourCompany Technology Co., Ltd. (U2XXXXXXXX) 证书

如何获取 Developer ID Installer 证书

与获取 Mac Developer 流程一样

Developer ID Installer 证书的作用

如果你的应用计划仅通过苹果应用商店(mas-dev、mas)进行安装,那么可以不需要此证书,如果通过苹果应用商店以外的,那么需要指定该证书对安装包(dmg、pkg)进行签名

4. 3rd Party Mac Developer Application: Shenzhen YourCompany Technology Co., Ltd. (U2XXXXXXXX) 证书

如何获取 3rd Party Mac Developer Application 证书

与获取 Mac Developer 流程一样

3rd Party Mac Developer Application 证书的作用

electorn builder 打包并且使用 Developer ID Application 签名、公证之后,将会使用这个证书进行签名,然后生成 mas 的 pkg 包,只有 mas 的包需要使用这个证书签名,mas-dev 不需要

5. 3rd Party Mac Developer Installer: Shenzhen YourCompany Technology Co., Ltd. (U2XXXXXXXX) 证书

如何获取 3rd Party Mac Developer Installer 证书

与获取 Mac Developer 流程一样

3rd Party Mac Developer Installer 证书的作用

当使用 electron builder 打包 mas 的 pkg 包的时候,需要对 pkg 的包使用该证书进行签名,如果不通过此证书签名,将会无法使用 transporter.app 上传到 testflight

可描述文件安装
  1. 获取 mac 的 uuid 和 udid command + 空格键,输入 系统信息 然后回车就出现这个页面了

    alt text

  2. 打开 开发者 设备列表添加 uuid 和 udid,一定要添加两个 https://developer.apple.com/account/resources/devices/list

    alt text

  3. profiles 下载 YourCompanyMac.Development 描述文件

    alt text

  4. 下载到本地后双击安装 alt text

3. 获取应用程序专用密码-用于公证代码

需要 应用程序专用密码和 appleId ,用于公证 .app 文件的

  1. 访问苹果 id 官网-生成-应用程序专属密码 https://appleid.apple.com

alt text

  1. 输入一个备注

alt text

  1. 拷贝你的应用专属密码 - 关闭窗口就看不到了

alt text

点击完成后你的 appleid 对应的邮箱也会收到一封邮件

alt text

  1. 把你的 appleid 和专属密码添加到 mac 项目的根目录的 .env.build 配置文件的
NotarizeAppleId=苹果id
NotarizeAppleIdPassword=应用专用密码

例如:

# 公证所需的变量
# 苹果id
NotarizeAppleId=your_email@qq.com
# 应用专用密码
NotarizeAppleIdPassword=xxxx-xxxx-xxxx-xxxx

4. 配置环境变量

  1. .env.universal

每一次发布到 testflight 要提升一个版本 修改

故需要 修改 .env.universal 的 VUE_APP_MAC_VERSION 的值

VUE_APP_MAC_VERSION=56
  1. .env.build
  • (1)如果需要打包 mas-dev 的包,那么需要修改 IdentityNameMacDeveloper 为你自己的,IdentityNameMacDeveloper 必须是开发者
  • (2)NotarizeAppleId、NotarizeAppleIdPassword、NotarizeTeamId、NotarizeAppBundleId 为公证所需字段,如果希望仅你自己可以公证,那么需在苹果开发者网站废弃现在的“应用专用密码(NotarizeAppleIdPassword)”,仅在你的电脑修改 NotarizeAppleIdPassword 字段后打包即可
# 为什么使用单独的 .env.build 文件而不把环境变量放到 .env.universal 中?
# 因为放在.env.universal中可以被主进程读取,从而导致密钥被窃取

# 公证所需的变量
# 苹果id
NotarizeAppleId=your_email@qq.com
# 应用专用密码
NotarizeAppleIdPassword=xxxx-xxxx-xxxx-xxxx
# 团队id
NotarizeTeamId=U2XXXXXXXX
# appId
NotarizeAppBundleId=com.xxx.YourCompanyMac

# 代码签名所需要的变量
# 3rd Party Mac Developer Application: Shenzhen YourCompany Technology Co., Ltd. (U2XXXXXXXX)
IdentityName3rdPartyMacDeveloperApplication="Shenzhen YourCompany Technology Co., Ltd. (U2XXXXXXXX)"
# 3rd Party Mac Developer Installer: Shenzhen YourCompany Technology Co., Ltd. (U2XXXXXXXX)
IdentityName3rdPartyMacDeveloperInstaller="3rd Party Mac Developer Installer: Shenzhen YourCompany Technology Co., Ltd. (U2XXXXXXXX)"
# Developer ID Application: Shenzhen YourCompany Technology Co., Ltd. (U2XXXXXXXX)
IdentityNameDeveloperIDApplication="Shenzhen YourCompany Technology Co., Ltd. (U2XXXXXXXX)"
# Mac Developer: Zhongyi Li (6CXXXXXXXX)
IdentityNameMacDeveloper="Zhongyi Li (6CXXXXXXXX)"

5. 执行命令实现自动打包、签名和公证

mas 是 "Mac App Store" 的简写

  1. 同时打包 mas-dev 和 mas 的包
bash
npm run build

会依次执行 2 和 3 的命令。

  1. 仅打包 mas-dev (开发环境测试苹果沙盒环境、可以直接双击打开)
bash
npm run build:dev

打包后会在 dist_electron/mas-dev-universal 目录中生成 yourappname.app,可以双击打开,如果打不开,那么需检查可描述文件是否正确安装,是否 mac 设备的配置 uuid 和 udid

  1. 仅打包 mas (必须在 testflight 或 apple store 下载后方可打开)

如果仅希望打包和上传到 testflight 那么你需要这个命令,这个命令会比较长时间(15 分钟左右)

bash
npm run build:prod

打包后会在 dist_electron/mas-universal 目录中生成yourappname.appyourappname-mac-universal-1.1.18(56).pkg,其中yourappname-mac-universal-1.1.18(56).pkg是我们需要的,后面把yourappname-mac-universal-1.1.18(56).pkg上传到苹果 apple connect 即可,该目录下的yourappname.app无法直接打开是正常的

注意:

  • 打包过程中不要打开 VPN 翻墙,否则会出现时间戳错误

  • 首次打包会出现 访问钥匙串 提示 - 要点击始终允许

    alt text

四、测试和上传到 testflight

  1. 苹果应用商店 搜索 “transporter.app” 安装并打开
  2. 使用你的 apple id 登陆,你的 apple id 必须是“开发者”,否则无法登陆
  3. 直接把打包好的yourappname-mac-universal-1.1.18(56).pkg 拖拽进去
  4. 等待检查完成,点击“交付”,即可上传
  5. 上传成功需要等待一会(15 分钟左右)才可以在 apple store connect 上看到上传的文件,上传成功后立即看不到是正常的
  6. 预计等待 30 分钟到 1 个小时,苹果审核通过,那么就可以在 testflight 测试,审核通过会有邮件通知你(apple id 的邮件)
  7. 苹果应用商店 搜索 “testflight.app” 打开就可以看到你的测试应用,如果有版本更新会在这里进行下载、安装、打开然后进行上线前的测试,如果你的 testflight 没有权限,那么需要管理者邀请你加入 testflight

五、上架 到 apple store

testflight 测试通过后,可以在 apple store connect 上提交你的应用,苹果一般在 1 ~ 3 个工作日审核通过就可以在 apple store 下载了。有问题会给出问题的地方,修改后重新提交即可。