Формы Xamarin: покрыл ли я базы на iOS Push-уведомлениях? - PullRequest
1 голос
/ 17 мая 2019

Проблема: разное поведение в 3 разных контекстах

Хорошо, так хорошо, в iOS кажется, что с Push-уведомлениями могут происходить три вещи:

  1. При получении Push-уведомления, когда приложение не на переднем плане
    • что-то появляется в Центре уведомлений
    • если приложение открывается нажатием на уведомление, вызывается или AppDelegate.DidReceiveRemoteNotification(...) или AppDelegate.ReceivedRemoteNotification(...), очевидно, в зависимости от того, какое из них реализовано (??).
    • если приложение открыто без нажатия на уведомление, вызывается only AppDelegate.WillEnterForeground(...), без какого-либо явного упоминания об уведомлении, и ничего больше не подтверждает, что уведомление был получен.
  2. При получении Push-уведомления, когда приложение находится на на переднем плане, оно вызывает UNUserNotificationCenterDelegate, если оно есть, для выполнения UNUserNotificationCenterDelegate.WillPresentNotification(...).

Подход: маршрутизация к одному методу из всех контекстов

Итак, чтобы покрыть все базы с помощью Push, мне нужно реализовать что-то во всех трех методах: AppDelegate.DidReceiveRemoteNotification(...) / AppDelegate.ReceivedRemoteNotification(...), AppDelegate.WillEnterForeground(...) и UNUserNotificationCenterDelegate .WillPresentNotification(...).

Вот несколько заглушек, чтобы показать мой подход ко всему этому.

Сначала я создал пользовательский UNUserNotificationCenterDelegate со статическим членом Shared:

public class IncomingNotificationHandler : UNUserNotificationCenterDelegate
{
    public static IncomingNotificationHandler Shared = new IncomingNotificationHandler();

    ...
 }

Во-вторых, внутри этого класса я создал обработчик, к которому я могу направлять в каждом случае (опять же, это просто заглушка для целей отладки):

    //sets all parameters to null by default, so it can be called from methods
    //that don't know anything about notifications:

    public void HandleNotificationsIfAny(UIApplication application = null, 
        NSDictionary userInfo = null, 
        Action<UIBackgroundFetchResult> completionHandler = null)
    {
        //checks if userInfo is null, and logs its conclusions about that:

        if (userInfo == null)
        {
            //In the null case, we can get pending notifications from 
            //UNUserNotificationCenter:

            UNNotification[] pendingNotifications = new UNNotification[] { };
            UNUserNotificationCenter.Current.GetDeliveredNotifications(returnedValue => pendingNotifications = returnedValue);

            //Then we log the number of pending notifications:

            Debug.WriteLine("IncomingNotificationHandler: HandleNotificationsIfAny(...): delivered notification count: " + pendingNotifications.Length);

            //And make note of where this was probably called from:

            Debug.WriteLine("IncomingNotificationHandler: HandleNotificationsIfAny(...): may have been called from this.WillPresentNotification(...) OR AppDelegate.WillEnterForeground(...)");
            return;
            });
        }
        else
        {
            //In the non-null case, we log the userInfo

            Debug.WriteLine("IncomingNotificationHandler: HandleNotificationsIfAny(...): just got info: " + userInfo);

            //And make note of where this was probably called from:

            Debug.WriteLine("IncomingNotificationHandler: HandleNotificationsIfAny(...): may have been called from AppDelegate.DidReceiveRemoteNotification(...)");
        }
    }

В-третьих, внутри того же класса я реализовал единственный метод, необходимый для UNUserNotificationCenterDelegate, и перенаправил его на обработчик:

    public override void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
    {
        HandleNotificationsIfAny();
    }

Четвертый и последний, внутри AppDelegate, я перенаправил на один и тот же обработчик обоими соответствующими методами:

 //I prefer using DidReceiveRemoteNotification because in my experience 
 //the other one is sometimes not reliable:

 public override void DidReceiveRemoteNotification(UIApplication application, 
        NSDictionary userInfo, 
        Action<UIBackgroundFetchResult> completionHandler)
    {              
        //Simply passing on all the parameters called in this method:

         IncomingNotificationHandler.Shared.HandleNotificationsIfAny(application, userInfo, completionHandler);
     }


 //WillEnterForeground also calls the handler without any parameters
 //because it doesn't automatically know anything about notifications:

  public override void WillEnterForeground(UIApplication application)
    {
        IncomingNotificationHandler.Shared.HandleNotificationsIfAny();
    }

С учетом этого, я думаю, что я обрабатываю событие уведомления одинаково, независимо от того, как мое приложение уведомлено об этом, и даже когда оно вообще не предупреждено.

Кто-нибудь знает, покрыл ли я это сейчас, или есть ли другие случаи, которые мне нужно обработать?

1 Ответ

0 голосов
/ 20 мая 2019

Для первого сценария: AppDelegate.ReceivedRemoteNotification

Он отражает целевой метод c: application:didReceiveRemoteNotification:, но это событие устарело с iOS 10: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623117-application?language=objc. Так что я думаю, что нет необходимости обрабатывать это событие.

Для второго сценария: AppDelegate.DidReceiveRemoteNotification

Вы по-прежнему можете использовать его для обработки уведомлений сейчас, если вы еще не внедрили UNUserNotificationCenter и обратите внимание, что оно действительно только после iOS 7+. Более того, это событие будет вызываться, когда приложение находится на переднем плане, а если ваше приложение находится в фоновом режиме, это событие вызывается только тогда, когда пользователь щелкает уведомление, чтобы открыть ваше приложение. И нет никакого доступа к информации уведомления, если пользователь нажимает значок, чтобы открыть приложение.

Я не думаю, что обработка AppDelegate.WillEnterForeground - это хороший подход, так как он будет вызываться каждый раз, когда приложение переходит с фона на передний план, даже если уведомлений нет.

Для сценария: UNUserNotificationCenterDelegate

Эту функцию можно использовать только после iOS 10. После того, как вы реализовали ее на устройстве iOS 10+, DidReceiveRemoteNotification и ReceivedRemoteNotification никогда не сработают. WillPresentNotification будет вызываться, когда приложение находится на переднем плане. DidReceiveNotificationResponse будет запущено, когда приложение находится в фоновом режиме, и пользователь нажимает на уведомления, чтобы открыть его.

В заключение, если вы хотите легко обработать уведомление, достаточно 1030 *. Если вы хотите использовать новые функции UNUserNotificationCenter, AppDelegate.DidReceiveRemoteNotification и UNUserNotificationCenter должны быть задействованы. Предыдущий для устройств iOS 7+ и более поздний для устройств iOS 10+.

Обновление:

Для iOS 10+ вы можете использовать UNUserNotificationCenter.Current.GetDeliveredNotifications для получения уведомлений, которые все еще отображаются в Центре уведомлений. И если вы хотите только поддерживать iOS версии 10 и выше. Я думаю, что UNUserNotificationCenter достаточно, нет необходимости реализовывать AppDelegate.DidReceiveRemoteNotification(...) или AppDelegate.ReceivedRemoteNotification(...).

  • Если приложение находится в фоновом / убитом состоянии, и пользователь нажимает на уведомление, чтобы откройте приложение, будет вызван DidReceiveNotificationResponse.
  • Если пользователь нажимает значок, чтобы открыть ваше приложение, и приложение должно быть убито, вы должны поместите свой логический код в FinishedLaunching.
  • Если пользователь нажимает значок чтобы открыть приложение и приложение в фоновом режиме, вы можете обрабатывать WillEnterForeground как и раньше.
  • Если приложение на переднем плане, ручка WillPresentNotification.
...