背景
最近接到需要编写一个Unity版本的SDK供游戏工程师使用,并上架应用到Mac App Store。之前从未有过Unity开发和Mac App上架的经验,所以写文章记录自己整个开发过程,以备需要时查看。
我们都知道在Mac OS上安装的应用,有两个下载渠道:
- 官方渠道:Mac App Store
- 非官方渠道:公司官网等非Mac App Store来源
我们本身也有在iOS App Store上架的应用,根据苹果官方文档的描述,我们可以直接将iOS 应用平滑迁移到Mac上,仅仅只需要在【App Store Connect - App - 价格与销售范围】上勾选 "提供此App 至 mac os"上,见下图:
但我们希望能够获得Mac App Store的官方流量和推荐,所以选择为当前应用增加mac os平台,发布完全适配Mac平台的新应用。于是,就有了Unity接入Mac内购和Mac签名的开发过程。
这篇文章主要讲述了我帮助游戏客户端进行Mac App的代码和安装包签名,以及对苹果相关官方文档的学习,希望可以帮助到大家。
1、签名过程
注意:如果使用Xcode直接上传到Mac App Store则不用签名和公证。
1.1、使用unity编译后修改info.plist文件
1)App Uses Non-Exempt Encryption :设置为 NO,这样在Apple Connect Strore - App - 构建版本处,就不需要每次都确认"出口合规证明";
vbnet
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
2)检查 Bundle OS Type code 的值:其value值是一个四字母的代码,用于指示包类型。对于应用程序,代码是APPL;对于框架,是FMWK;对于捆绑包,则是BNDL。默认值是从捆绑扩展名中推导出来的,如果无法推导,则默认值是BNDL。
vbnet
<key>CFBundlePackageType</key>
<string>APPL</string>
3)App Category(LSApplicationCategoryType) :设置应用程序的类型
vbnet
<key>LSApplicationCategoryType</key>
<string>public.app-category.games</string>
4)LSMinimumSystemVersion:设置App在 macOS 中运行所需的最低操作系统版本。
vbnet
<key>LSMinimumSystemVersion</key>
<string>13.0.0</string>
1.2、创建并修改.entitlements文件
使用Xcode创建一个和你项目同名的Mac工程,不需要配置该项目的描述文件和证书,仅仅只需要添加App Sandbox Capability。
我们项目是一个游戏项目,所以按照项目功能,勾选了网络和文件读写。
打开生成的App.entitlements,添加TeamID和AppID
vbnet
<key>com.apple.developer.team-identifier</key>
<string>TeamID</string>
<key>com.apple.application-identifier</key>
<string>AppID</string>
补充:上传到Mac App Store应用都必须开启沙盒功能,验证签名后的app是否开启沙盒功能,苹果提供了两种方式。
1)活动监视器查看
2)命令行查看
css
% codesign -dvvv --entitlements - <path to your app>
1.3、在.app/Contents路径下添加发布环境的.provisionprofile文件
注意: 必须.app/Contents
路径下,文件名也必须是embedded.provisionprofile
。
从Mac App Store 或 TestFlight下载后的应用,.app/Contents就不会显示embedded.provisionprofile文件了。
1.4、签名指令
ruby
# 1. 对~/XXX.app/Contents/PlugIns路径下的bundle进行代码签名,注意不可以使用--entitlements选项,注意这里使用的是:3rd Party Mac Developer Application证书
# codesign -f -s <CertificationName> <BundlePath>
$ codesign -f -s '3rd Party Mac Developer Application: AAA' "XXX.app/Contents/PlugIns/unitypurchasing.bundle"
# 终端会打印:XXX.app/Contents/PlugIns/unitypurchasing.bundle: replacing existing signature
# 2. 对~/XXX.app/Contents/Frameworks路径下的库文件进行代码签名,注意不可以使用--entitlements选项,注意这里使用的是:3rd Party Mac Developer Application证书
# codesign -f -s <CertificationName> <FrameworkPath>
$ codesign -f -s '3rd Party Mac Developer Application: AAA' "XXX.app/Contents/Frameworks/libmonobdwgc-2.0.dylib"
# 终端会打印:XXX.app/Contents/Frameworks/libmonobdwgc-2.0.dylib: replacing existing signature
# 3. 对.app进行代码签名,注意这里使用的是:3rd Party Mac Developer Application证书
# codesign -f -s <CertificationName> --entitlements <EntitlementsPath> <AppPath>
$ codesign -f -s '3rd Party Mac Developer Application: AAA' --entitlements "/Users/XXX/Desktop/App.entitlements" "/Users/XXX/Desktop/XXX.app"
# 终端会打印:XXX.app: replacing existing signature
# 4. 生成安装包并进行安装包签名,注意这里使用的是:3rd Party Mac Developer Installer证书
# productbuild --component <AppPath> /Applications --sign <CertificationName> <PkgPath>
$ productbuild --component /Users/XXX/Desktop/XXX.app /Applications --sign "3rd Party Mac Developer Installer: AAA" /Users/XXX/Desktop/XXX.pkg
# 终端会打印:
productbuild: Adding component at /Users/XXX/Desktop/XXX.app
productbuild: Signing product with identity "3rd Party Mac Developer Installer: AAA" from keychain /Users/XXX/Library/Keychains/login.keychain-db
productbuild: Adding certificate "Apple Worldwide Developer Relations Certification Authority"
productbuild: Adding certificate "Apple Root CA"
productbuild: Wrote product to /Users/XXX/Desktop/XXX.pkg
productbuild: Supported OS versions: [Min: 13.0.0, Before: None]
补充: 是在 "钥匙串访问" 中复制粘贴的证书名称
2、 签名过程遇到的问题
1、Asset validation failed (90869) Invalid bundle. The "NTSDK.app" bundle supports arm64 but not Intel-based Mac computers. Your build must include the x86_64 architecture to support Intel-based Mac computers. To support arm64 only, your macOS deployment target must be 12.0 or higher. For details, view: developer.apple.com/documentati.... (ID: 3d0f10fb-1a41-4d45-8b40-d1227cdd3f4b)
原因: Apple要求如果应用只支持Apple Silicn,macOS deployment target必须 >=12.0,而我们Unity Build的Mac app设置是10.13.0。
解决: 修改Info.plist文件中, LSMinimumSystemVersion 对应的value值,按照需求设置,只要大于12.0就行。
vbnet
<key>LSMinimumSystemVersion</key>
<string>13.0.0</string>
2、Asset validation failed (90236) Missing required icon. The application bundle does not contain an icon in ICNS format, containing both a 512x512 and a 512x512@2x image. For further assistance, see the Human Interface Guidelines at developer.apple.com/design/huma.... (ID: e0440ff7-4601-41cc-aeb4-7ea1e32560d2)
原因: 缺失应用图标
解决: 按照要求添加即可,在这里推荐一款 "免费生成iPhone、iPad、Mac OS平台应用Icon的Mac App:AppIcon Maker(xcAsset Creator)"
3、ITMS-90889: Cannot be used with TestFlight because the bundle at 'MyApp.app' is missing a provisioning profile. Main bundles are expected to have provisioning profiles in order to be eligible for TestFlight.
原因: .app/Contents路径下缺失发布环境的.provisionprofile文件。
解决:
1)在.entitlements
文件中添加TeamID和AppID
2)在XXX.app/Contents/
路径下添加 embedded.provisionprofile
文件
下面是我对苹果官方文档的学习和理解:
3、了解Mac 签名
3.1 公证Mac App
1)通过Mac App Store 分发的 Mac App,无须公证,因为在提交过程App Store已经对其进行了相同的安全检查。
2)非Mac App Store 分发的 Mac App,需要公证,否则会提示用户"应用可能会损害电脑"等,
3)问答:Mac 公证服务团队答疑
3.2 代码签名
-
所有的Mac App,都必须进行代码签名。
- 如果使用Unity 编译导出.app文件,则需要手动为代码签名,也就是为.app进行签名
-
根据
file
指令,查看对应的文件是否为main executable
,A main executable saysMach-O ... executable
.arduino$ file /Applications/XXX.app/Contents/PlugIns/AkSoundEngine.bundle/Contents/MacOS/AkSoundEngine /Applications/XXX.app/Contents/PlugIns/AkSoundEngine.bundle/Contents/MacOS/AkSoundEngine: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit bundle x86_64] [arm64] /Applications/XXX.app/Contents/PlugIns/AkSoundEngine.bundle/Contents/MacOS/AkSoundEngine (for architecture x86_64): Mach-O 64-bit bundle x86_64 /Applications/DynastyLegends2.app/Contents/PlugIns/AkSoundEngine.bundle/Contents/MacOS/AkSoundEngine (for architecture arm64): Mach-O 64-bit bundle arm64 # 根据打印,可知AkSoundEngine不是main executable
-
需要从内向外签署代码,也就是如果组件 A 依赖于组件 B,则先签署 B,然后再签署 A。
举例:
DaemonWithApp
产品有以下内容:ConfigApp.app
,Core.framework
,Share.appex
, andDaemon
,签名顺序应为:Core.framework
、Share.appex
、ConfigApp.app
,daemon是独立的,签名顺序随意。 -
代码签名可以包含App权限文件 .entitlements ,当 macOS 运行一个进程时,entitlements文件会授予该进程可执行文件在代码签名所包含的权限。
-
嵌入distribution provisioning profile:
在entitlements声明的权限,一般必须都需要在distribution provisioning profile中进行授权。当然也有一些不需要在distribution provisioning profile授权,就可以在entitlements中应用的权限。
注意:一定要将distribution provisioning profile文件放在 XXX.app/Contents/ 路径下,且修改文件名为embedded.provisionprofile
-
ini
% codesign -d -vv <filePath> codesign -d -vv /Applications/NTSDK.app/Contents/PlugIns/unitypurchasing.bundle Executable=/Applications/NTSDK.app/Contents/PlugIns/unitypurchasing.bundle/Contents/MacOS/unitypurchasing Identifier=com.unity.purchasing.unitypurchasing Format=bundle with Mach-O universal (x86_64 arm64) CodeDirectory v=20500 size=1200 flags=0x10000(runtime) hashes=28+5 location=embedded Signature size=4844 Authority=TestFlight Beta Distribution Authority=Apple Worldwide Developer Relations Certification Authority Authority=Apple Root CA Info.plist entries=26 TeamIdentifier=7A34BX8C5J Runtime Version=11.0.0 Sealed Resources version=2 rules=13 files=1 Internal requirements count=1 size=116
-
shell
% codesign -s <CodeSigningIdentity> <PathToExecutable> # 举例子: % codesign -f -s '3rd Party Mac Developer Application: HK Taihe Interactive Limited (7A34BX8C5J)' "/Users/taihe/Desktop/NTSDK.app/Contents/Frameworks/libmonobdwgc-2.0.dylib" /Users/taihe/Desktop/NTSDK.app/Contents/Frameworks/libmonobdwgc-2.0.dylib: replacing existing signature % codesign -f -s '3rd Party Mac Developer Application: HK Taihe Interactive Limited (7A34BX8C5J)' "/Users/taihe/Desktop/NTSDK.app/Contents/Frameworks/UnityPlayer.dylib" /Users/taihe/Desktop/NTSDK.app/Contents/Frameworks/UnityPlayer.dylib: replacing existing signature % codesign -f -s '3rd Party Mac Developer Application: HK Taihe Interactive Limited (7A34BX8C5J)' "/Users/taihe/Desktop/NTSDK.app/Contents/Frameworks/libMonoPosixHelper.dylib" % codesign -f -o runtime -s '3rd Party Mac Developer Application: HK Taihe Interactive Limited (7A34BX8C5J)' --entitlements "/Users/taihe/Desktop/App.entitlements" "/Users/taihe/Desktop/NTSDK.app" /Users/taihe/Desktop/NTSDK.app: replacing existing signature
说明:
1、不能对库代码进行包含权限文件(.entitlements)的代码签名,它会阻止可执行文件的程序运行,导致mac app打开就crash。
2、不能使用
sudo
运行codesign
,因为codesign
在签署代码时依赖用户账户信息,使用sudo
等工具会更改用户账户,从而造成一些意外情况.3、如果要签署包含权限的可执行文件,需要添加
--entitlements <entitlementsPath>
,其中<entitlementsPath>
是为该可执行文件创建的权限文件的路径。4、如果使用Developer ID 进行代码签名,需添加
--timestamp
选项,以包含安全时间戳。5、如果使用Developer ID 进行代码签名,需添加
-o runtime
选项,启用加固运行时。 -
签署代码时,不要在 codesign 中使用
--deep
选项。--deep
会对其签署的每个代码项应用相同的entitlements。例如,如果你有一个内嵌命令行工具的应用程序,而应用程序和工具需要不同的权限,那么 codesign --deep 就会对两者应用相同的权限。
3.3 决定以什么样的格式分发Mac App
在Mac 上安装的应用支持四种格式:
-
从 Mac App Store上下载的直接就是.app
-
从非 Mac App Store上下载安装的可能是:
以下3种格式可以嵌套
-
Zip archive (
.zip
) : 在执行压缩执行前,需要先对包内容签名; -
Installer package (
.pkg
):需要根据分发渠道进行签名- 发布到Mac App Store,使用
3rd Party Mac Developer Installer: <TeamID>
,对安装包进行签名 - 发布到非Mac App Store,使用
Developer ID Installer: <TeamID>
, 对安装包进行签名
- 发布到Mac App Store,使用
-
描述可执行文件的权限信息的文件,我一般叫它权限文件,内容以key-value形式展示。在对应用程序进行代码签名时,Xcode 会将权限文件、开发者账户中的信息以及其他项目信息结合起来,为应用程序应用一组最终的权限。
4、参考链接:
Packaging Mac software for distribution
Resolving Library Loading Problems