MASA MAUI Plugin (十)iOS訊息推送(原生APNS方式)
背景
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證書
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,歡迎聯絡我們
- Blazor在IoT領域的前端實踐 @.NET開發者日
- MASA MAUI Plugin (十)iOS訊息推送(原生APNS方式)
- MASA MAUI Plugin (九)Android相簿多選照片(使用Android Jetpack套件庫)
- MASA MAUI Plugin (八)Android相簿多選照片(Intent 方式)
- MASA Stack 1.0 釋出會講稿——生態篇
- MASA Stack 1.0 釋出會講稿——實踐篇
- MASA Stack 1.0 釋出會講稿——產品篇
- MASA Stack 1.0 釋出會講稿——趨勢篇
- MASA MAUI Plugin (七)應用通知角標(小紅點)Android iOS
- .NET現代化應用開發 - CQRS&類目管理程式碼剖析
- MASA MAUI Plugin 安卓藍芽低功耗(二)藍芽通訊
- MASA MAUI Plugin 安卓藍芽低功耗(一)藍芽掃描
- MASA MAUI Plugin 安卓藍芽低功耗(二)藍芽通訊
- MASA MAUI Plugin 安卓藍芽低功耗(一)藍芽掃描
- MASA Framework的分散式鎖設計
- MAUI Masa Blazor 開發介面跟隨系統主題切換的App
- MAUI Masa Blazor 開發介面跟隨系統主題切換的App
- MAUI Masa Blazor 開發帶自動更新功能的安卓App
- 開篇-開啟全新的.NET現代應用開發體驗
- 怎麼樣的框架對於開發者是友好的?