我的项目背景
MAUI的呈现,赋予了宽广Net开发者开发多平台利用的能力,MAUI 是Xamarin.Forms演变而来,然而相比Xamarin性能更好,可扩展性更强,构造更简略。然而MAUI对于平台相干的实现并不残缺。所以MASA团队发展了一个实验性我的项目,意在对微软MAUI的补充和扩大,
我的项目地址:https://github.com/BlazorComp...
每个性能都有独自的demo演示我的项目,思考到app安装文件体积(尽管MAUI曾经集成裁剪性能,然而该性能对于代码自身有影响),届时每一个性能都会以独自的nuget包的模式提供,不便测试,当初我的项目才刚刚开始,然而置信很快就会有能够交付的内容啦。
前言
本系列文章面向挪动开发小白,从零开始进行平台相干性能开发,演示如何参考平台的官网文档应用MAUI技术来开发相应性能。
介绍
之前两篇文章咱们实现了安卓蓝牙BLE的相干性能,本文咱们将IOS的BLE性能实现一下。
,思考到Swift语法对于c#开发人员更敌对,本文示例代码参考Swift,相干代码来自苹果开发者官网
https://developer.apple.com/d...
开发步骤
批改我的项目
在Masa.Blazor.Maui.Plugin.Bluetooth我的项目中的Platforms->iOS文件夹下,增加一个局部类MasaMauiBluetoothService,在安卓中有BluetoothManager,在ios中对应的是CBCentralManager,然而不同有安卓还有个适配器Adapter的概念,在ios中对于设施扫描、连贯和治理外围设备的对象,都是通过CBCentralManager间接治理的,咱们看一下他的初始化办法
init( delegate: CBCentralManagerDelegate?, queue: DispatchQueue?, options: [String : Any]? = nil)
delegate:接管核心事件的委托。相当于咱们在装置中实现的DevicesCallback
queue:用于调度核心角色事件的调度队列。如果该值为 nil,则地方管理器将应用主队列分派核心角色事件。这个咱们能够简略的了解为和安卓的UI线程或者后盾线程对应,更详尽的阐明请参考
https://developer.apple.com/d...
options:配置信息,咱们这里只用到了ShowPowerAlert,代表蓝牙设施如果不可用,给用户提示信息。就好比你用了不符合标准的数据线,iphone会给你提醒是一个意思。
public static partial class MasaMauiBluetoothService { private static BluetoothDelegate _delegate = new(); public static CBCentralManager _manager = new CBCentralManager(_delegate, DispatchQueue.DefaultGlobalQueue, new CBCentralInitOptions { ShowPowerAlert = true, }); private sealed class BluetoothDelegate : CBCentralManagerDelegate { private readonly EventWaitHandle _eventWaitHandle = new(false, EventResetMode.AutoReset); public List<BluetoothDevice> Devices { get; } = new(); public void WaitOne() { Task.Run(async () => { await Task.Delay(5000); _eventWaitHandle.Set(); }); _eventWaitHandle.WaitOne(); } public override void DiscoveredPeripheral(CBCentralManager central, CBPeripheral peripheral, NSDictionary advertisementData, NSNumber RSSI) { System.Diagnostics.Debug.WriteLine("OnScanResult"); if (!Devices.Contains(peripheral)) { Devices.Add(peripheral); } } [Preserve] public override void UpdatedState(CBCentralManager central) { } } }
咱们将MasaMauiBluetoothService批改为动态类,
咱们自定义的BluetoothDelegate 继承自CBCentralManagerDelegate,篇幅问题咱们这里先只重写DiscoveredPeripheral和 UpdatedState,咱们这次的演示不须要实现UpdatedState,然而这里的重写必须先放上去,否则调试过程会呈现上面的报错
ObjCRuntime.ObjCException: 'Objective-C exception thrown. Name: NSInvalidArgumentException Reason: -[Masa_Blazor_Maui_Plugin_Bluetooth_MasaMauiBluetoothService_BluetoothDelegate centralManagerDidUpdateState:]: unrecognized selector sent to instance 0x284bfe200
另外有一点须要特地留神,这个UpdatedState办法我没有实现的代码,那么我就须要增加一个[Preserve],这样是为了避免链接器 在生成nuget包的时候把这个办法帮我优化掉。
实现发现左近设施性能,_eventWaitHandle和安卓一样,我这里只是实现了一个异步转同步不便间接通过Devices拿到后果,如果小伙伴不喜爱前期我会增加不阻塞的形式。
这里之所以能够Devices.Contains和Devices.Add是因为咱们在BluetoothDevice类中实现了隐式转换
如下是iOS目录下BluetoothDevice.ios.cs的局部代码
partial class BluetoothDevice { ... private BluetoothDevice(CBPeripheral peripheral) { _peripheral = peripheral; } public static implicit operator BluetoothDevice(CBPeripheral peripheral) { return peripheral == null ? null : new BluetoothDevice(peripheral); } public static implicit operator CBPeripheral(BluetoothDevice device) { return device._peripheral; } ...
ios扫描外围设备是通过scanForPeripherals
咱们持续在MasaMauiBluetoothService增加一个扫描附件设施的办法,咱们看一下Swift的文档
func scanForPeripherals( withServices serviceUUIDs: [CBUUID]?, options: [String : Any]? = nil)
serviceUUIDs:代表须要过滤的服务UUID,相似安卓的scanFilter对象。
option:提供扫描的选项,咱们这里用到了AllowDuplicatesKey,该值指定扫描是否应在不反复筛选的状况下运行
咱们参照实现以下咱们的PlatformScanForDevices办法
private static async Task<IReadOnlyCollection<BluetoothDevice>> PlatformScanForDevices() { if (!_manager.IsScanning) { _manager.ScanForPeripherals(new CBUUID[] { }, new PeripheralScanningOptions { AllowDuplicatesKey = true }); await Task.Run(() => { _delegate.WaitOne(); }); _manager.StopScan(); _discoveredDevices = _delegate.Devices.AsReadOnly(); } return _discoveredDevices; }
通过 _cbCentralManager.IsScanning来判断是否处于扫描状态,如果没有,那就就通过ScanForPeripherals扫描外围设备,扫描5秒之后(BluetoothDelegate 外部管制)通过StopScan进行扫描,并通过 _discoveredDevices 保留后果。
咱们还需实现PlatformIsEnabledIsEnabled和PlatformCheckAndRequestBluetoothPermission办法,用来在扫描之前查看蓝牙是否可用并且曾经通过用户受权
public static bool PlatformIsEnabledIsEnabled() { return _manager.State == CBManagerState.PoweredOn; } public static async Task<PermissionStatus> PlatformCheckAndRequestBluetoothPermission() { PermissionStatus status = await Permissions.CheckStatusAsync<BluetoothPermissions>(); if (status == PermissionStatus.Granted) return status; if (status == PermissionStatus.Denied && DeviceInfo.Platform == DevicePlatform.iOS) { // Prompt the user to turn on in settings // On iOS once a permission has been denied it may not be requested again from the application return status; } status = await Permissions.RequestAsync<BluetoothPermissions>(); return status; } private class BluetoothPermissions : Permissions.BasePlatformPermission { protected override Func<IEnumerable<string>> RequiredInfoPlistKeys => () => new string[] { "NSBluetoothAlwaysUsageDescription", "NSBluetoothPeripheralUsageDescription" }; public override Task<PermissionStatus> CheckStatusAsync() { EnsureDeclared(); return Task.FromResult(GetBleStatus()); } private PermissionStatus GetBleStatus() //Todo:Needs to be replenished { var status = _cbCentralManager.State; return status switch { CBManagerState.PoweredOn=> PermissionStatus.Granted, CBManagerState.Unauthorized => PermissionStatus.Denied, CBManagerState.Resetting => PermissionStatus.Restricted, _ => PermissionStatus.Unknown, }; } }
在PlatformIsEnabledIsEnabled办法中通过 _cbCentralManager.State == CBManagerState.PoweredOn 来判断蓝牙是否可用。该状态一共有如下枚举,从字面意思很好了解
**Unknown**, //手机没有辨认到蓝牙**Resetting**, //手机蓝牙已断开连接**Unsupported**, //手机蓝牙性能没有权限**Unauthorized**, //手机蓝牙性能没有权限**PoweredOff**,//手机蓝牙性能敞开**PoweredOn** //蓝牙开启且可用
权限查看这里和安卓有一些区别,在重写的RequiredInfoPlistKeys办法中指定了须要查看的蓝牙权限,BasePlatformPermission的EnsureDeclared办法用来查看是否在Info.plist文件增加了须要的权限,GetBleStatus办法通过 _cbCentralManager 的状态,来查看受权状况。
咱们在Masa.Blazor.Maui.Plugin.Bluetooth的根目录增加局部类MasaMauiBluetoothService.cs,向使用者提供ScanForDevicesAsync等办法,办法外部通过PlatformScanForDevices来调用具体平台的实现。
public static partial class MasaMauiBluetoothService { private static IReadOnlyCollection<BluetoothDevice> _discoveredDevices; public static Task<IReadOnlyCollection<BluetoothDevice>> ScanForDevicesAsync() { return PlatformScanForDevices(); } public static bool IsEnabled() { return PlatformIsEnabledIsEnabled(); } public static async Task<PermissionStatus> CheckAndRequestBluetoothPermission() { return await PlatformCheckAndRequestBluetoothPermission(); } }
应用
右键Masa.Blazor.Maui.Plugin.Bluetooth我的项目,点击打包,生成一个nuget包,在Masa.Blazor.Maui.Plugin.BlueToothSample我的项目中离线装置即可,代码的应用与安卓齐全一样,只是权限配置形式不同
在Masa.Blazor.Maui.Plugin.BlueToothSample我的项目的Platforms->iOS->Info.plist中增加蓝牙相干权限
<key>NSBluetoothAlwaysUsageDescription</key> <string>App required to access Bluetooth</string> <key>NSBluetoothPeripheralUsageDescription</key> <string>App required to access Bluetooth</string>
NSBluetoothAlwaysUsageDescription对应iOS 13以上版本,对于iOS 13之前的版本,须要将NSBluetoothAlwaysUsageDescription和NSBluetoothPeripheralUsageDescription同时增加。
蓝牙扫描的成果和安卓机是齐全一样的,这里就不展现了。前文详情
iOS调试及谬误排查
目前在windows的vs环境调试MAUI的ios程序,是不须要mac电脑反对的,数据线连上后会显示一个本地设施,然而你依然须要一个开发者账号,vs会调用apple开发者api主动帮你配置好须要的证书。
1、如果没有显示查看Xamarin->iOS设置,热重启是否开启
2、调试过程如果提醒相似
Could not find executable for C:\Users\xxx\AppData\Local\Temp\hbjayi2h.ydn
找不到文件的状况,右键抉择清理我的项目即可,如果无奈解决手动删除bin和obj目录重试
3、调试过程如果app无端退出,排查一下思考APP的启动和调试断点工夫,iOS要求所有办法必须在17秒之内返回,否则iOS零碎将进行该利用
4、调试过程呈现Deploy Error: An Lockdown error occurred. The error code was "MuxError"的谬误,请查看你的数据线,从新插拔或者更换原装线。
本文到此结束。
如果你对咱们MASA感兴趣,无论是代码奉献、应用、提 Issue,欢送分割咱们
WeChat:MasaStackTechOps
QQ:7424099