我的项目背景

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,篇幅问题咱们这里先只重写DiscoveredPeripheralUpdatedState,咱们这次的演示不须要实现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.ContainsDevices.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 保留后果。
咱们还需实现PlatformIsEnabledIsEnabledPlatformCheckAndRequestBluetoothPermission办法,用来在扫描之前查看蓝牙是否可用并且曾经通过用户受权

        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办法中指定了须要查看的蓝牙权限,BasePlatformPermissionEnsureDeclared办法用来查看是否在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之前的版本,须要将NSBluetoothAlwaysUsageDescriptionNSBluetoothPeripheralUsageDescription同时增加。

蓝牙扫描的成果和安卓机是齐全一样的,这里就不展现了。前文详情

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