文章目录
- 1. 要求
- 1.1 无需Apple开发者账号
- 1.2 最新版mac系统
- 1.3 最新版Xcode
- 2. 配对Mac
- 3. 配置开发证书
- 3.1 创建一个名为MTClient的Xcode项目
- 3.2 找到签名证书
- 3.3 配置签名
- 3.4 配置标识符
- 4. 真机调试
- 4.1 设置应用首屏 Launch Screen
- 4.2 设置应用图标
- 5. 问题
- 5.1 DI异常
- 该问题的解决
- 使用`<UserInterpreter>`
- 探索MAUI
- 曙光
- 5.2 其他报错
- 5.3 Entitlements权利
1. 要求
1.1 无需Apple开发者账号
我这里开发的应用仅是用于调试、体验IOS应用开发流程,并没有发布到AppSotre的计划,所以没有申请一年$99的开发者账号。
但没加入开发者计划也有一些使用限制:
- 你的应用签名有效期只有一周,到期之后需要重新把手机插到电脑上重新安装一次。
- 不能分发应用,多台手机的话只能一个一个装
(如果你已经有了开发者账号,则可以参考这篇文章 Maui劝退:用windows直接真机调试iOS,无须和Mac配对,无需mac电脑)
1.2 最新版mac系统
macOS版本:15.0以上。
你需要有一台装了macOS的电脑,可以是黑苹果或白苹果。(我是找了台不用的dell电脑,花了50装了最新的黑果系统)
系统最好是最新版,因为.net 9只支持macOS 15以上的版本。参见:Target Framework
1.3 最新版Xcode
AppStore安装最新的Xcode,xamarin/maui/avalonia开发的ios应用都依赖xcode。
2. 配对Mac
这一步比较简单,确保开发机和mac在同一个网段,vs就会自动查找到对应的mac。
然后点击“connect”即可,会自动在mac上安装需要的work load,大概需要十几分钟。
我这里已经连接过了所以显示“disconnect”
3. 配置开发证书
总体上可以参考这篇略过时的文章:如何在iPhone或iPad上构建和运行应用程序。
也可以直接参考我的步骤:
3.1 创建一个名为MTClient的Xcode项目
Xcode中首先登录你的AppleID,然后创建一个名为MTClient的IOS项目。
创建有三个信息比较重要:
- Product Name:这里的MTClient是我应用的名字,你也可以改成其它的。
- Organization Identifier:组织名,你用ApppleID登陆Xcode时会提示是否创建一个组织,这里自动填充的GymOrg就是我之前创建的组织。当然这里也可以用其他的组织。
- Bundle Identifier:核心,牢记,这个相当于你应用的标识符,后续所有的签名及证书都是基于此标识符。
创建完成后,把手机插到mac上调试下看是否能正常跑起来。
3.2 找到签名证书
打开SpotLight,输入KeyChainAccess:
打开钥匙串访问.app,搜索关键词develop找到刚创建的证书。
将此证书的名字复制下来备用。
3.3 配置签名
首先将项目属性 Bundle Signing Scheme改为Manual Provisioning
:
然后打开项目的.csproj
,添加<CodesignKey>
,值就是之前拷贝的证书字符串。
3.4 配置标识符
签名配置好了,还要配置对应的应用标识符。签名+标识符才是完整的一套。
打开info.plist
将Bundle Identifier改为之前我们配置的值即可。
4. 真机调试
这个没什么好说的,rebuild下项目,然后调试设备选择你的IOS真机,接下来就是正常的调试步骤。
4.1 设置应用首屏 Launch Screen
默认首屏显示的是一个白底黑字你应用名字的一个页面。
你可以修改LaunchScreen.xib
文件内容进行调整:
同样你可以修改info.plist
的选项以关闭首次打开时显示首屏页面:
(但设置为not set后可以影响你app的展示尺寸)
4.2 设置应用图标
打开info.plist
,在这里设置应用图标:
需要多个不同宽高的图标:
可使用在线工具一次性全部生成。
5. 问题
5.1 DI异常
报错 Attempting to JIT compile method 'Microsoft.Extensions.DependencyInjection.ServiceProvider :
Received unhandled Objective-C exception that was marshalled from a managed exception: Attempting to JIT compile method 'Microsoft.Extensions.DependencyInjection.ServiceProvider Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions:BuildServiceProvider (Microsoft.Extensions.DependencyInjection.IServiceCollection,Microsoft.Extensions.DependencyInjection.ServiceProviderOptions)' while running in aot-only mode. See https://learn.microsoft.com/xamarin/ios/internals/limitations for more information.(System.ExecutionEngineException)at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection )at MessageTransferClient.App.OnFrameworkInitializationCompleted() in H:\MyProgram\SomePractice\Avaloni
此问题只出现在IOS真机上,IOS模拟器、Windows、安卓都没有问题。原因是因为IOS真机不允许JIT,所以只能用AOT,但并非所有的.net 特性都支持AOT,所以就会报错。
AOT使用的一些限制具体请参考:Xamarin.iOS 的限制
该问题的解决
使用<UserInterpreter>
但并不意味着所有AOT不支持的特性你都不能使用,可以参考iOS 和 Mac Catalyst 上的 Mono 解释器官方文档,在csproj中添加<UseInterpreter>
。这样对于AOT不支持的特性就可以使用Mono解释器执行,缺点就是运行速度会慢一点而且也不是100%解决问题。
然而尝试使用Mono解释器后并没有解决此问题,报错变成了:
Error: VTable setup of type Microsoft.Extensions.DependencyInjection.ServiceProvider failed.
解释器无法构造出DI类型所需要的函数表,网上搜了一圈也没找到如何解决。
后续我又尝试使用SimpleInject等对AOT友好的DI或者自己手写DI,但我所用到的SignalR.Client这个库严重依赖MSDI,并且无法替换。
探索MAUI
我曾经一度怀疑.net for ios是个鸡肋,于是在github上搜索现成的基于SignalR的MAUI开发的app,看是否都能正常工作。搜到一个之后真机调试运行了没有发现任何问题,DI也完全正常。这难道是Avalonia自身的问题?
于是乎又经历了一番尝试:将.net版本改为与MAUI中的一致、将所有用的nuget package改为与MAUI中的一致、检查plist是否一致,一番操作之后然而并没有什么用,同样的报错。
此问题遂搁置,等周末再研究。
曙光
于是来到了今天上午,在尝试继续解决这个问题前,我升级了下VS2022的版本,从17.10升级到了目前的最新版17.14.11。
神奇的是DI就这样完全好用了,期间一个星期我没有改动过任何代码,就这么升级一下VisualStuido就解决了。不确定最终问题原因是出现了VisualStudio里还是依赖的.net9 workload里。
关于JIT问题的两个参考链接:
- https://github.com/AvaloniaUI/Avalonia/discussions/19267
- https://github.com/AvaloniaUI/Avalonia/issues/9934
5.2 其他报错
开发过程中如果你遇到了像这种奇奇怪怪本不应该报错的报错,clean–>rebuild下你的IOS项目就可以了。
5.3 Entitlements权利
在尝试解决前面的DI问题时,我猜测可能是权利问题(但其实并不是)。所以又检索了macOS Catalina 公证以及对 .NET 下载和项目的影响这篇文章,了解了下权利。简单来说所有你准备在IOS或MAC平台上进行分发的软件都需要具备对应的权利。
但Avalonia模板创建出来的默认应用,并没有添加.net对应的权利,所以适时可以将权利添加到Entitlements.plist
文件中。