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

Posted

技术标签:

【中文标题】如何在 Xamarin Forms iOS-App 中接收 ASPN 令牌【英文标题】:How to receive the ASPN Token in Xamarin Forms iOS-App 【发布时间】:2020-11-04 22:15:23 【问题描述】:

我按照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 部分,因为到目前为止我只需要 iOS 的推送通知,而且据我所知,这些对于 iOS 来说并不是必需的。

感谢您的帮助!

【问题讨论】:

【参考方案1】:

检查RegisteredForRemoteNotification是否未被调用的几点:

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

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

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

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

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

【讨论】:

感谢您的帮助!问题是,我在模拟器设备上尝试了该应用程序。使用真实设备修复它。 @SebSeb 你能接受答案吗(点击这个答案左上角的☑️),以便我们可以帮助更多有同样问题的人:)。 这里有同样的问题:模拟器崩溃并出现同样的错误,但真实设备可以工作。但是现在 App Store 团队因为崩溃而拒绝了我们的应用程序。您是如何在商店中获得您的应用程序的?或者有什么方法可以修复注册?【参考方案2】:

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

public override void FailedToRegisterForRemoteNotifications(
    UIApplication application, 
    NSError error)

例如

找不到应用程序的有效“aps-environment”权利字符串

【讨论】:

以上是关于如何在 Xamarin Forms iOS-App 中接收 ASPN 令牌的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Xamarin.Forms.Maps(无 Xamarin.Forms.GoogleMaps)在地图中应用样式或更改颜色

如何在 Xamarin.Forms 中获取/检测屏幕大小?

在 Xamarin.Forms 中录制语音后如何保存音频文件

如何在 xamarin.forms 中获取状态栏高度?

如何在 Xamarin.Forms 的 Grid 中启用边框

如何删除 Xamarin.Forms 导航栏?