如何在 Xamarin Forms iOS-App 中接收 ASPN 令牌

How to receive the ASPN Token in Xamarin Forms iOS-App

我按照 this 教程在我的 Xamarin-Forms 应用程序中实现推送通知(尤其是 iOS 部分)。现在我的问题是,当我按下注册按钮时,我收到错误消息“无法为 APNS 解析令牌”。 在调试模式下单步执行代码,我可以验证 DeviceInstallationService 中的 Token 属性 确实是 null。 所以我退后一步,发现 Token 仅通过 AppDelegate.cs 中的 RegisteredForRemoteNotification 设置,但是当我 运行 App.

时从未调用此方法

这是一些代码:App-Delegate

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Foundation;
using Notes.iOS.Extensions;
using Notes.iOS.Services;
using Notes.Services;
using UIKit;
using UserNotifications;
using Xamarin.Essentials;

using System.Collections.Generic;
using System.Linq;

using Syncfusion.SfCalendar.XForms.iOS;

namespace Notes.iOS
{
    [Register("AppDelegate")]
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
    {
        IPushDemoNotificationActionService _notificationActionService;
        INotificationRegistrationService _notificationRegistrationService;
        IDeviceInstallationService _deviceInstallationService;

        IPushDemoNotificationActionService NotificationActionService
            => _notificationActionService ??
                (_notificationActionService =
                ServiceContainer.Resolve<IPushDemoNotificationActionService>());

        INotificationRegistrationService NotificationRegistrationService
            => _notificationRegistrationService ??
                (_notificationRegistrationService =
                ServiceContainer.Resolve<INotificationRegistrationService>());

        IDeviceInstallationService DeviceInstallationService
            => _deviceInstallationService ??
                (_deviceInstallationService =
                ServiceContainer.Resolve<IDeviceInstallationService>());

        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();
            Bootstrap.Begin(() => new DeviceInstallationService());
            if (DeviceInstallationService.NotificationsSupported)
            {
                UNUserNotificationCenter.Current.RequestAuthorization(
                    UNAuthorizationOptions.Alert |
                    UNAuthorizationOptions.Badge |
                    UNAuthorizationOptions.Sound,
                    (approvalGranted, error) =>
                    {
                        if (approvalGranted && error == null)
                            RegisterForRemoteNotifications();
                    });
            }
            LoadApplication(new App());
            using (var userInfo = options?.ObjectForKey(
                UIApplication.LaunchOptionsRemoteNotificationKey) as NSDictionary)
                ProcessNotificationActions(userInfo);
            return base.FinishedLaunching(app, options);
        }

        void RegisterForRemoteNotifications()
        {
            MainThread.BeginInvokeOnMainThread(() =>
            {
                var pushSettings = UIUserNotificationSettings.GetSettingsForTypes(
                    UIUserNotificationType.Alert |
                    UIUserNotificationType.Badge |
                    UIUserNotificationType.Sound,
                    new NSSet());

                UIApplication.SharedApplication.RegisterUserNotificationSettings(pushSettings);
                UIApplication.SharedApplication.RegisterForRemoteNotifications();
            });
        }

        Task CompleteRegistrationAsync(NSData deviceToken)
        {
            DeviceInstallationService.Token = deviceToken.ToHexString();
            return NotificationRegistrationService.RefreshRegistrationAsync();
        }

        void ProcessNotificationActions(NSDictionary userInfo)
        {
            if (userInfo == null)
                return;

            try
            {
                var actionValue = userInfo.ObjectForKey(new NSString("action")) as NSString;

                if (!string.IsNullOrWhiteSpace(actionValue?.Description))
                    NotificationActionService.TriggerAction(actionValue.Description);
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }

        public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
            => CompleteRegistrationAsync(deviceToken).ContinueWith((task)
                => { if (task.IsFaulted) throw task.Exception; });

        public override void ReceivedRemoteNotification(
            UIApplication application,
            NSDictionary userInfo)
            => ProcessNotificationActions(userInfo);

        public override void FailedToRegisterForRemoteNotifications(
            UIApplication application,
            NSError error)
            => Debug.WriteLine(error.Description);
    }
}

DeviceInstallationService:

using System;
using Notes.Models;
using Notes.Services;
using UIKit;

namespace Notes.iOS.Services
{
    public class DeviceInstallationService : IDeviceInstallationService
    {
        const int SupportedVersionMajor = 13;
        const int SupportedVersionMinor = 0;
        public string Token { get; set; }

        public bool NotificationsSupported
            => UIDevice.CurrentDevice.CheckSystemVersion(SupportedVersionMajor, SupportedVersionMinor);

        public string GetDeviceId()
            => UIDevice.CurrentDevice.IdentifierForVendor.ToString();

        public DeviceInstallation GetDeviceInstallation(params string[] tags)
        {
            if (!NotificationsSupported)
                throw new Exception(GetNotificationsSupportError());

            if (string.IsNullOrWhiteSpace(Token))
                throw new Exception("Unable to resolve token for APNS");

            var installation = new DeviceInstallation
            {
                InstallationId = GetDeviceId(),
                Platform = "apns",
                PushChannel = Token
            };

            installation.Tags.AddRange(tags);

            return installation;
        }

        string GetNotificationsSupportError()
        {
            if (!NotificationsSupported)
                return $"This app only supports notifications on iOS {SupportedVersionMajor}.{SupportedVersionMinor} and above. You are running {UIDevice.CurrentDevice.SystemVersion}.";

            if (Token == null)
                return $"This app can support notifications but you must enable this in your settings.";


            return "An error occurred preventing the use of push notifications";
        }
    }
}

如您所见,这确实是 1:1 示例代码,唯一不同的是我的项目名为 Notes。

我跳过了 Firebase 和 Android-Part,因为到目前为止我只需要 iOS 的推送通知,据我所知,这些对于 iOS 不是必需的。

感谢您的帮助!

检查 RegisteredForRemoteNotification 是否未被调用的一些要点:

  1. 打开 Entitlements.plist 并确保在 Entitlements 选项卡中查看时选中 Enable Push Notifications。然后,确保在源选项卡中查看时将 APS 环境设置设置为开发。

  2. 确保您在真实设备而不是模拟器中测试远程通知。模拟器不支持远程通知。

  3. 确保您同意接收通知权限。

  4. 确保您使用的证书已启用推送通知功能。

参考:configuring-the-remote-notifications-environment

您可以在您的 App Delegate 中查看以下函数返回的消息 AppDelegate.cs

public override void FailedToRegisterForRemoteNotifications(
    UIApplication application, 
    NSError error)

例如

no valid “aps-environment” entitlement string found for application