写点什么

MASA MAUI Plugin (十)iOS 消息推送(原生 APNS 方式)

  • 2023-03-08
    浙江
  • 本文字数:4381 字

    阅读完需:约 14 分钟

背景

MAUI 的出现,赋予了广大.Net 开发者开发多平台应用的能力,MAUI 是 Xamarin.Forms 演变而来,但是相比 Xamarin 性能更好,可扩展性更强,结构更简单。但是 MAUI 对于平台相关的实现并不完整。所以 MASA 团队开展了一个实验性项目,意在对微软 MAUI 的补充和扩展


项目地址https://github.com/BlazorComponent/MASA.Blazor/tree/feature/Maui/src/Masa.Blazor.Maui.Plugin


每个功能都有单独的 demo 演示项目,考虑到 app 安装文件体积(虽然 MAUI 已经集成裁剪功能,但是该功能对于代码本身有影响),届时每一个功能都会以单独的 nuget 包的形式提供,方便测试,现在项目才刚刚开始,但是相信很快就会有可以交付的内容啦。

前言

本系列文章面向移动开发小白,从零开始进行平台相关功能开发,演示如何参考平台的官方文档使用 MAUI 技术来开发相应功能。

介绍

Apple 推送通知服务(Apple Push Notification service),简称 APNs。与之前 Android 使用个推不同,由于 APNs 国内可用,所以我们可以直接使用 APNs 来实现远程消息推送,不依赖其他第三方组件和服务。我们这里推送使用的是 p8 证书,p8 证书相对 p12 证书来讲,更灵活,而且没有 p12 证书有效期 1 年的限制。

一、实现方式

一、申请 p8 证书

https://developer.apple.com/


1、登录开发者中心,点击右上角 Account,找到 Keys 管理。



2、在顶部点击+号。



3、勾选 APNs 服务,并输入 Key 名称,下一步 Continue。



4、点击 Register。



5、记录 Key ID,并下载证书,得到 AuthKey_xxxxxxxxxx.p8 证书文件。



6、获取 Team ID,Account 界面点击 Membership details



二、编写 MAUI 实现代码

参考官方文档:https://developer.apple.com/documentation/usernotifications/registering_your_app_with_apns


1、首先需要先开启 App 消息推送的能力


我们新建一个 iOSPush 文件夹,并在文件夹下面新建 MauiBlazor 项目 iOSPushSample(由于受打包长度影响,项目名称和文件夹名称,我这里尽量简短。路径长度超过 255 会导致编译时提示部分文件找不到。)我们找到 Platforms->iOS->Info.plist 文件,双击用默认的 iOS 清单编辑器打开,勾选“启用后台模式”和“远程通知”。这项操作会在 Info.plist 文件中添加如下信息:


  <key>UIBackgroundModes</key>  <array>    <string>remote-notification</string>  </array>
复制代码



在开发环境,你需要以下的额外配置。


如果你的项目已经使用了 Entitlements.plist 文件,双击打开改文件,并勾选“推送通知”。



如果没有这个文件,那么新建这个文本文件,内容如下:


<?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>aps-environment</key>  <string>development</string></dict></plist>
复制代码


注意:这项配置的目的是在 development 环境支持推送,如果在项目发布的时候,MAUI 不会自动清除,需要手动关闭或注释掉这项配置,否则会报错。


2、编写实现代码


参考文档 https://developer.apple.com/documentation/usernotifications/registering_your_app_with_apns


我们首先需要将 App 注册到 APNs 并获取唯一的 device token。在 iOS 中需要调用 UIApplication 中通过 registerForRemoteNotifications() 方法,实现注册,如果注册成功,就可以在 delegate application(_:didRegisterForRemoteNotificationsWithDeviceToken:) 方法中获取 device token。如果注册失败,会触发 application(_:didFailToRegisterForRemoteNotificationsWithError:) 方法。


Swift代码func application(_ application: UIApplication,           didFinishLaunchingWithOptions launchOptions:           [UIApplicationLaunchOptionsKey: Any]?) -> Bool {   // Override point for customization after application launch.you’re            UIApplication.shared.registerForRemoteNotifications()   return true}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { self.sendDeviceTokenToServer(data: deviceToken)}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { // Try again later.}
复制代码


我们开始编写代码

向 APNs 注册设备

首先在 iOS->AppDelegate.cs 类中,重写 FinishedLaunching 方法,在应用启动之后进行注册。


    [Register("AppDelegate")]    public class AppDelegate : MauiUIApplicationDelegate    {        protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();        public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)        {            UNUserNotificationCenter center = UNUserNotificationCenter.Current;
var options = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Sound | UNAuthorizationOptions.CriticalAlert; // Request notification permissions from the user center.RequestAuthorization(options, (bool success, NSError error) => { // Handle approval } ); UIApplication.SharedApplication.RegisterForRemoteNotifications(); return base.FinishedLaunching(application, launchOptions); } }
复制代码


应用启动时应立即请求通知权限,方法是将以下代码添加到 FinishedLaunchingAppDelegate 所需通知类型 (UNAuthorizationOptions) 的方法:


UNUserNotificationCenter 仅适用于 iOS 10+,但是考虑到基本没人用低于 10 的版本了,这里我们就不做版本检查了


用户可以批准应用的三个不同级别的通知请求:横幅显示-Alert 。声音警报-Sound 。对应用图标进行错误设置-CriticalAlert。


请求权限结束后我们通过 UIApplication.SharedApplication.RegisterForRemoteNotifications(); 向 APNs 注册。注册成功后我们通过 application:didRegisterForRemoteNotificationsWithDeviceToken: 方法获取 device token,但是由于这个方法是在 UIApplication 下,但是我们的 AppDelegate 是继承自 MauiUIApplicationDelegate ,默认没有这个方法,我们可以通过 Export 特性,导出我们需要的方法,继续在 AppDelegate 中添加


        [Export("application:didFailToRegisterForRemoteNotificationsWithError:")]        public void FailedToRegisterForRemoteNotifications(UIKit.UIApplication application, NSError error)        {            Console.WriteLine("FailedToRegisterForRemoteNotifications");        }                [Export("application:didRegisterForRemoteNotificationsWithDeviceToken:")]        public void RegisteredForRemoteNotifications(UIKit.UIApplication application,            Foundation.NSData deviceToken)        {            var token = ExtractToken(deviceToken);            Preferences.Default.Set("PushToken", token);            Console.WriteLine(token);        }
private string ExtractToken(NSData deviceToken) { if (deviceToken.Length == 0) return null; var result = new byte[deviceToken.Length]; System.Runtime.InteropServices.Marshal.Copy(deviceToken.Bytes, result, 0, (int)deviceToken.Length); return BitConverter.ToString(result).Replace("-", ""); }
复制代码


ExtractToken 是将返回的 deviceToken 解析为推送可用的字符串。我们通过 Preferences.Default.Set 将 token 存储起来,方便在登录等业务中使用。

接收远程推送

接收推送的消息是通过**application:didReceiveRemoteNotification:fetchCompletionHandler:**实现的


        [Export("application:didReceiveRemoteNotification:fetchCompletionHandler:")]        public void DidReceiveRemoteNotification(UIKit.UIApplication application, NSDictionary userInfo, Action<UIKit.UIBackgroundFetchResult> completionHandler)        {            foreach (var item in userInfo)            {                var alertMsg = ((NSDictionary)item.Value)["alert"];                Console.WriteLine($"alertMsg:{alertMsg}");            }            Console.WriteLine("DidReceiveRemoteNotification");        }
复制代码


其实我们在方法内部不需要写任何代码,就可以实现基本的推送功能。但如果想处理推送过来的消息,可以通过 NSDictionary 类型 userInfo 中拿到。这里示例从 alert 中拿到具体的消息内容,并打印。

三、编写演示代码

我们修改 Index.razor,通过点击按钮获取设备 Token


@page "/"
<button @onclick="GetDeviceToken">Get Device Token</button><text>@deviceToken</text>@code{ private string deviceToken { get; set; } private void GetDeviceToken() { deviceToken= Preferences.Default.Get("PushToken",string.Empty); }}
复制代码

四、服务端测试

我们可以通过个推的在线测试工具,配置好 p8 证书和其他参数。



我这里写了一个命令行的测试程序,这里使用了第三方的 PushNotifications.Server 包


using PushNotifications.Server.Apple;
Console.WriteLine("Hello, World!");IApnsClient apnsClient = new ApnsClient(new ApnsJwtOptions{ CertContent = "-----BEGIN PRIVATE KEY-----\r\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\r\n-----END PRIVATE KEY-----", KeyId = "LUxxxxxxxx", TeamId = "V4xxxxxxxx", BundleId = "com.iOSPush.Sample", UseSandbox = true});
var apnsRequest = new ApnsRequest(ApplePushType.Alert) .AddToken("47006118F8xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") .AddSound() .AddAlert("MASA", $"Message @ {DateTime.Now}", $"Message @ {DateTime.Now}");var apnsResponse = await apnsClient.SendAsync(apnsRequest);Console.ReadKey();
复制代码


五、演示效果



可以看出,应用打开的状态不会收到推送,后台运行或者划掉关闭都可以收到通知。


如果你对我们的 MASA Framework 感兴趣,无论是代码贡献、使用、提 Issue,欢迎联系我们 WeChat:MasaStackTechOpsQQ:7424099

发布于: 刚刚阅读数: 4
用户头像

还未添加个人签名 2021-10-26 加入

MASA技术团队官方账号,我们专注于.NET现代应用开发解决方案,Wechat:MasaStackTechOps ,Website:www.masastack.com

评论

发布
暂无评论
MASA MAUI Plugin (十)iOS消息推送(原生APNS方式)_blazor_MASA技术团队_InfoQ写作社区