DevGang
Авторизоваться

Push-уведомления с использованием Firebase, Node.js, Flutter/Dart

Если вы начинающий разработчик или впервые знакомитесь с этой концепцией, то наверняка задавались вопросом: "Как обеспечить удаленное взаимодействие двух или более устройств в режиме реального времени?". Если вы очень умный человек, то, возможно, вы сами придумали несколько решений. Ваши решения могут заключаться в следующем:

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

Использование веб-сокетов: веб-сокеты используются в большинстве коммуникаций реального времени, таких как каналы обмена текстовыми, аудио- и видеосообщениями. Так что, возможно, этот вариант окажется для вас очень надежным.

Можно даже придумать какую-нибудь грубую технику, которая будет заключаться в периодическом запросе к API внутри приложения для получения новых данных. Хотя все эти способы обмена данными в реальном времени являются допустимыми и стандартными для отрасли, их применение зависит от конкретного случая использования в приложении.

Если данные могут передаваться между устройствами описанным выше способом, то что же тогда представляют собой push-уведомления? Почему они являются критически важной функцией в современных приложениях? Как можно реализовать push-уведомления в приложении? В этой статье мы дадим чёткие ответы на эти вопросы.

Что такое Push-уведомление?

Большинство мобильных приложений работает под управлением операционной системы Android или iOS. Каждое из устройств, работающих под управлением этих операционных систем, имеет уникальный идентификатор. Этот идентификатор принято называть маркером устройства. В каждой операционной системе есть служба, которая может удаленно взаимодействовать с каждым устройством, работающим под управлением этой операционной системы. В iOS таким сервисом является сервис Apple Push Notification (APNs), а в Android - сервис Firebase Cloud Messaging (FCM).

Push-уведомления включаются в приложении, когда приложение отправляет данные (которые также включают токен устройства, на которое направлено приложение) в службу push-уведомлений (APN или FCM). Служба push-уведомлений, в свою очередь, отправляет данные на конкретное устройство, чтобы вызвать на нем отображение уведомления независимо от того, запущено ли приложение на устройстве или нет.

Отметим, что push-уведомления могут быть реализованы и в других операционных системах, помимо Android и iOS, но в контексте данной статьи мы остановимся только на использовании push-уведомлений в Android и iOS.

Зачем использовать Push-уведомления в современных приложениях?

Push-уведомления стали очень важной функцией для большинства мобильных и веб-приложений. Почему это так? Вот некоторые причины:

Оповещение: Как и следует из названия, push-уведомление уведомляет или предупреждает пользователя приложения о входящих или уже полученных данных.

Маркетинг: push-уведомления используются в качестве маркетингового инструмента для информирования пользователей приложения о предлагаемых услугах или других рекламных сообщениях.

Удержание пользователей: Некоторые пользователи склонны забывать о приложении, которым они редко пользуются. Стратегические оповещения от таких приложений помогают пользователю вернуться к приложению.

Вовлечение: Приложения для социальных сетей и другие "ищущие вовлеченности" приложения могут использовать возможности push-уведомлений для повышения вовлеченности в приложение и привлечения пользователя к работе с приложением.

Одним из интересных моментов в push-уведомлениях является то, что целевой пользователь может быть зарегистрированным пользователем приложения, а может и не быть. Если приложение работает на устройстве пользователя и пользователь включил функцию push-уведомлений, то информация может быть передана на это устройство. Кроме того, для получения push-уведомления не требуется, чтобы приложение было активно открыто (или использовалось). Это одна из наиболее весомых причин использования push-уведомлений. Различные методы удаленного обмена данными, о которых говорилось ранее, требуют активного использования приложения для успешной работы. Например, я не узнаю о получении сообщения в чате Whatsapp, если у меня не открыт Whatsapp. Но с помощью push-уведомления я могу получить уведомление о получении сообщения Whatsapp (даже если телефон находится в режиме ожидания), прежде чем открою Whatsapp, чтобы просмотреть содержимое сообщения или продолжить общение с отправителем. Таким образом, push-уведомление можно использовать в сочетании с другими методами обмена данными в приложении.

Как реализовать push-уведомления в приложении?

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

Необходимые условия

Прежде чем мы сможем полностью приступить к работе с программной реализацией push-уведомлений, необходимо настроить учетную запись firebase. Если у вас нет учетной записи firebase, вы можете создать ее здесь. Учетная запись firebase - это то же самое, что и учетная запись google. Поэтому если у вас есть учетная запись google, вы можете приступать к работе.

Теперь, когда у вас есть учетная запись google, вы можете перейти к консоли firebase. В консоли Firebase необходимо создать новый проект или использовать существующий проект (если он уже есть).

Нажмите кнопку Add Project (Добавить проект) и заполните форму для создания нового проекта. Убедитесь, что все данные введены точно. После заполнения каждой формы нажмите кнопку Continue (Продолжить) и перейдите к созданию проекта.

После создания проекта можно загрузить json-файл учетной записи сервиса. Для этого перейдите в консоль firebase, в консоли щелкните your_project_name > project overview settings(icon) > project settings > service accounts > generate new private key > generate key.

Обратите внимание, что вы должны загрузить этот ключ и хранить его в надежном месте. Вы не сможете извлечь этот ключ, а если он будет потерян, вам придется сгенерировать другой ключ. Создание нескольких ключей не имеет серьезных последствий, ваше приложение будет работать нормально.

Внутренняя реализация push-уведомлений

Мы рассмотрим реализацию push-уведомлений на бэкенде с помощью node.js. Если вы знакомы с JavaScript, это будет легко сделать. Нам потребуется добавить в проект один крупный пакет:

  • Firebase admin: Firebase admin - это пакет ядра firebase, который помогает нам работать со многими службами firebase. Одним из необходимых нам сервисов firebase является сервис обмена сообщениями firebase. Обратите внимание, что ранее мы уже рассказывали о том, что Firebase Cloud messaging - это служба push-уведомлений для устройств Android, и у вас может возникнуть вопрос: "А если я хочу отправлять push-уведомления на устройства iOS, нужен ли мне другой пакет?". - Нет, не нужен! Пакет firebase messaging поможет нам в работе с push-уведомлениями как для Android, так и для iOS. Он будет пинговать сервисы push-уведомлений для Android и iOS и доставлять уведомление на соответствующее устройство. После установки firebase admin настройте его в своем проекте. Конфигурация может выглядеть следующим образом:
// in your main.js or app.js file
const admin = require('firebase-admin');
// download your service account credential from firebase
// and add it to a config folder in your project
const serviceAccount = require('../config/serviceAccountKey.json');

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: 'https://yourFirebaseProjectName.firebaseio.com',
});

К этому моменту я хочу предположить, что вы настроили свой проект node.js и готовы к интеграции push-уведомлений. Я не буду вдаваться в подробности других настроек проекта, а сосредоточусь только на функции push-уведомлений. Теперь давайте приступим к выполнению следующих шагов:

  • Напишите функцию отправки уведомления: Эта функция будет асинхронной и потребует таких параметров, как идентификатор пользователя, url изображения (для уведомления с изображением), заголовок, тело, идентификатор ресурса. Идентификатор ресурса - это идентификатор базы данных информации, которую вы хотите отправить. Например, если вы отправляете уведомление о сообщении, то идентификатором ресурса будет идентификатор сообщения. Идентификатор ресурса важен потому, что при открытии уведомления может потребоваться выполнить какой-либо запрос к внешнему интерфейсу. Этот запрос может быть выполнен с использованием идентификатора ресурса или другого параметра, который используется при запросе данных в собственном бэкенде. Тело - это более длинный текст, который будет показан пользователю при раскрытии уведомления. Обратите внимание, что все эти параметры, кроме заголовка, являются необязательными.
//this is your service.js file or some file where you want to handle push notification
const admin = require('firebase-admin');

async sendPushNotification(title, body, userId, imageUrl, resourceId){}
  • Запросите идентификатор пользователя, чтобы получить дополнительные данные о пользователе для уведомления: Для отправки push-уведомления нам необходима дополнительная информация. Нам нужны такие данные, как токен устройства и количество значков. Количество значков не является обязательным параметром. Он будет важен только для устройств на базе iOS. Значок используется для обозначения количества непрочитанных уведомлений для пользователя. Токен - это токен устройства. Он может быть получен с самого устройства и сохранен в коллекции пользователей в базе данных. Когда мы будем обсуждать фронтенд-реализацию push-уведомлений, мы поговорим о том, как получить токен устройства. Отметим, что в рамках данной статьи значок и токен устройства будут храниться в коллекции пользователей. В некоторых случаях эти данные могут храниться в собственной коллекции вместе с другой информацией. Выбор полностью зависит от вас.
//this is your service.js file or some file where you want to handle push notification
const admin = require('firebase-admin');

async sendPushNotification(title, body, userId, imageUrl, resourceId){
  // the function below is a db call. replace with your respective db query
  const user = await findUserById(userId);

  const token = user.token;
  let badge = user.badge ?? 0; // if user.badge is null badge is assigned a 0 value

  if(token === null || token === undefined){
    throw new Error('this is an invalid token');
  }
  badge++; // increament the badge.
}
  • Подготовьте объект сообщения push-уведомления: Если обратиться к официальной документации firebase messaging, то можно заметить, что объект сообщения имеет тип TokenMessage | TopicMessage | ConditionMessage. И все три типа расширяются от типа BaseMessage. Вы можете обратиться к официальной документации Firebase, чтобы узнать все возможные параметры объекта Firebase. Подготовим сценарий использования нашего уведомления.
//this is your service.js file or some file where you want to handle push notification
const admin = require('firebase-admin');

async sendPushNotification(title, body, userId, imageUrl, resourceId){
  // the function below is a db call. replace with your respective db query
  const user = await findUserById(userId);

  const token = user.token;
  let badge = user.badge ?? 0; // if user.badge is null badge is assigned a 0 value

  if(token === null || token === undefined || token.length < 2){
    throw new Error('this is an invalid token');
  }
  badge++
  const message = {
          notification: {
            title,
            body,
          },
          android: {
            notification: {
              channel_id: 'MESSAGE_CHANNEL',// *
              icon: 'message_icon', // *
              tag: 'message', // *
              image: imageUrl,
            },
          },
          apns: {
            payload: {
              aps: {
                badge,
                sound: 'chime.caf',
              },
            },
          },
          data: {
            click_action: 'FLUTTER_NOTIFICATION_CLICK', // *
            type: 'MESSAGE', // *
            resourceId,
          },
          token,
       };
}

Примечание: Все параметры в приведенном выше фрагменте кода с символом "*" могут быть изменены в соответствии с требованиями вашего приложения. Подробнее о каждом параметре можно прочитать в документации Firebase.

  • Отправьте push-уведомление: Используйте пакет администрирования firebase для отправки push-уведомления, как показано ниже.
//this is your service.js file or some file where you want to handle push notification
const admin = require('firebase-admin');

async sendPushNotification(title, body, userId, imageUrl, resourceId){
  // the function below is a db call. replace with your respective db query
  const user = await findUserById(userId);

  const token = user.token;
  let badge = user.badge ?? 0; // if user.badge is null badge is assigned a 0 value

  if(token === null || token === undefined || token.length < 2){
    throw new Error('this is an invalid token');
  }
  badge++
  const message = {
          notification: {
            title,
            body,
          },
          android: {
            notification: {
              channel_id: 'MESSAGE_CHANNEL',// *
              icon: 'message_icon', // *
              tag: 'message', // *
              image: imageUrl,
            },
          },
          apns: {
            payload: {
              aps: {
                badge,
                sound: 'chime.caf',
              },
            },
          },
          data: {
            click_action: 'FLUTTER_NOTIFICATION_CLICK', // *
            type: 'MESSAGE', // *
            resourceId,
          },
          token,
       };

    await admin.messaging().send(message);
}
  • Окончательная проверка: Здесь, возможно, потребуется обновить количество значков в коллекции пользователя и выполнить некоторую работу над ошибками.
//this is your service.js file or some file where you want to handle push notification
const admin = require('firebase-admin');

async sendPushNotification(title, body, userId, imageUrl, resourceId){
  // the function below is a db call. replace with your respective db query
  const user = await findUserById(userId);

  const token = user.token;
  let badge = user.badge ?? 0; // if user.badge is null badge is assigned a 0 value

  if(token === null || token === undefined || token.length < 2){
    throw new Error('this is an invalid token');
  }
  badge++
  const message = {
          notification: {
            title,
            body,
          },
          android: {
            notification: {
              channel_id: 'MESSAGE_CHANNEL',// *
              icon: 'message_icon', // *
              tag: 'message', // *
              image: imageUrl,
            },
          },
          apns: {
            payload: {
              aps: {
                badge,
                sound: 'chime.caf',
              },
            },
          },
          data: {
            click_action: 'FLUTTER_NOTIFICATION_CLICK', // *
            type: 'MESSAGE', // *
            resourceId,
          },
          token,
       };

    await admin.messaging().send(message);

  // update user badge count.
  // this is a db call. replace this method with your appropraite db update method
  await user.update({badge});
}

Важно использовать try-catch в теле функции. Это позволит правильно обрабатывать ошибки, не нарушая работу сервера. Одна из ошибок, которую можно обработать, - это недействительный токен устройства. Возможно, потребуется удалить токен из хранилища в базе данных. Это поможет впоследствии обратиться к фронтенду для получения точного токена устройства.

  • Напишите еще одну функцию для сброса количества значков: После доставки уведомления пользователь нажимает на уведомление, чтобы просмотреть его. На фронтенде нам необходимо сбросить счетчик значков в базе данных, чтобы получить нулевое значение. Это поможет нам управлять просмотренными уведомлениями. Этот счетчик значков в основном актуален для приложений на iOS.
async resetBadgeCount(userId){
  // this is a db call, replace with appropriate query to fetch user from db
  const user = await findUserById(userId);

  // this is a db update method. update the badge property of the user to 0
  await user.update({badge: 0});
}

Эти функции push-уведомлений, написанные нами для бэкенда, могут вызываться в других сервисных функциях приложения, либо могут быть переданы в API. Если они подключены к API, это означает, что push-уведомления могут быть вызваны через вызов API.

Функцию resetBadgeCount можно вынести в API, поскольку она будет вызываться на фронтенде, когда пользователь нажмет на уведомление. Вы можете настроить API самостоятельно.

Теперь, когда мы завершили внутреннюю реализацию функции push-уведомлений, пришло время сосредоточиться на её внешней реализации в вашем приложении.

Фронтенд-реализация push-уведомлений

Поскольку мы обсуждали push-уведомления в мобильных приложениях, нам необходимо рассмотреть различные подходы к фронтенду для push-уведомлений. Либо мы будем использовать нативный язык, такой как Java или Swift, либо гибридный язык, такой как Flutter и React native. В этой статье мы остановимся только на Flutter. Если вы обратите внимание на нашу реализацию бэкенда, то увидите FLUTTER_NOTIFICATION_CLICK в качестве одного из значений объекта сообщения push-уведомления. Это связано с тем, что мы используем Flutter для фронт-энда. Пожалуйста, следуйте документации Firebase по настройке клиента. Это очень простой процесс.

Итак, вот следующие шаги для реализации фронтенда в Flutter:

  • Установите пакеты Flutter: Вам потребуется установить следующие пакеты:
  1. Firebase core: Прежде чем использовать Firebase, необходимо установить в приложение пакет firebase core.
  2. Firebase Messaging: Это основной пакет, необходимый нам для работы функции push-уведомлений.
  3. Flutter Local Notification: Этот пакет поможет настроить фронтенд на получение push-уведомлений нужным образом. Он работает в связке с пакетом Firebase messaging.
  4. Flutter App Badger: Этот пакет будет использоваться для сброса значка уведомлений в приложениях для iOS.
  • Добавьте новое приложение firebase: В учетной записи firebase перейдите в настройки проекта и добавьте новое приложение. Выберите значок iOS, чтобы добавить приложение для iOS.
  • Загрузите файл info.plist: Файл GoogleService.info.plist автоматически генерируется внутри проекта после создания приложения для iOS. Загрузите этот файл. Он понадобится вам в вашем проекте.
  • Добавьте в проект файл info.plist: Откройте папку ios каталога проекта в Xcode. Перетащите загруженный файл во вложенную папку Runner. В появившемся диалоговом окне убедитесь, что в свойстве Destination установлен флажок Copy items if needed, а в поле Add to targets выбран Runner. Затем нажмите кнопку Finish.
  • Загрузите файл google-services.json: Скачайте файл google-services.json, перетащите его в папку your_project_directory > android > app, нажмите кнопку Next, затем следуйте инструкциям на странице. Обязательно выполняйте все инструкции.
  • Создайте класс Flutter Local Notification и настройте локальное уведомление: В одном из служебных файлов можно создать класс для установки и настройки локальных уведомлений.
// import all relevant packages here

class LocalNotificationService {
  static final localNotification = FlutterLocalNotificationsPlugin();

  static Future<void> initialize()async{
    // configure android to use the app icon for notification
    const AndroidInitializationSettings initializationSettingsAndroid =
        AndroidInitializationSettings('@mipmap/ic_launcher');

    // this function would be used for iOS apps when notification is received
    // it can be configured as needed.
    void onDidReceiveLocalNotification(
        int? id, String? title, String? body, String? payload) async {
      print('the notification arrived ');
    }

    // darwin settings is for iOS and MacOS
    final DarwinInitializationSettings initializationSettingsDarwin =
        DarwinInitializationSettings(
      requestSoundPermission: true,
      requestBadgePermission: true,
      requestAlertPermission: true,
      onDidReceiveLocalNotification: onDidReceiveLocalNotification,
    );

    // android and ios settings are thus embedded together
    final InitializationSettings settings = InitializationSettings(
      android: initializationSettingsAndroid,
      iOS: initializationSettingsDarwin,
    );

    // initialize the plugin with the configured settings.
    await localNotification.initialize(
      settings,
    );

  }
}
  • Инициализируйте службу уведомлений: В файле main.dart инициализируйте службу уведомлений flutter.
Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );

  // this is where the notification service was initialized
  await LocalNotificationService.initialize();

  runApp(MyApp());
}
  • Создайте класс firebase messaging и настройте firebase messaging: Для различных событий уведомлений необходимо инициализировать Firebase messaging.
// import all necessary packages

// this function was written out of the class intentionally.
// if written in a class, it might cause a null check bug within the app.
Future<void> _backgroundMsgHandler(RemoteMessage? message) async {
  // you can specify how you want to handle bugs in your app when messages...
  // are received and the app is in background mode.
}

class FirebaseMessagingService {
  static final firebaseInstance = FirebaseMessaging.instance;

  static Future<void> init() async {
    /**
     * When the app is completely closed (not in the background)
     * and opened directly from the push notification
     */
    FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage? event) {
      // specify how you want to handle the notification
    });

    //Background
    FirebaseMessaging.onBackgroundMessage(_backgroundMsgHandler);

    /**
     * When the app is open and it receives a push notification
     */
    FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
      // specify how you want to handle the notification
    });

    /**
     * When the app is in the background and is opened directly
     * from the push notification.
     */
    FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
      // specify how you want to handle the notification
    });

  }

  // this function handles app permissions 
  Future<void> grantAppPermission() async {
    NotificationSettings settings = await firebaseInstance.requestPermission(
      alert: true,
      announcement: true,
      badge: true,
      provisional: false,
      sound: true,
    );

    // handle final permission settings from user as appropriate
    if (settings.authorizationStatus == AuthorizationStatus.authorized) {
      print('User granted permission');
    } else if (settings.authorizationStatus ==
        AuthorizationStatus.provisional) {
      print('User granted provisional permission');
    } else {
      print('User declined or has not accepted permission');
    }

  }

}

Для каждого события сообщения могут обрабатываться по-разному. Для одних сообщений может потребоваться действие перенаправления, для других - всплывающий диалог. Каким бы ни было ваше предпочтение, вы можете реализовать его в своем приложении.

  • Инициализируйте обмен сообщениями firebase на главной странице: В точке входа приложения инициализируйте firebase messaging.
class Home extends StatefulWidget {
  const Home({Key? key}) : super(key: key);

  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  Future<void> initFM() async {
    await FirebaseMessagingService.grantAppPermission();
    await FirebaseMessagingService.init();
  }

  @override
  void initState() {
    initFM();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Container();
  }

}
  • Обновите локальную службу уведомлений: Необходимо обновить локальную службу уведомлений, чтобы добавить функцию сброса количества значков для iOS-устройств.
// import the flutter app badger package
// import http package

class LocalNotificationService {
  ..//all previously written codes 

  static Future<void> resetBadgeCountToZero(Store<AppState> store) async {
     // call the api to reset badge count on the db.
      http.get(Uri.parse('backend_url_to_reset_badge'));

    // this updates the UI badge for iOS
    FlutterAppBadger.removeBadge();
  }

}

Эта функция resetBadgeCountToZero может быть вызвана в методе firebase init для каждого из событий уведомления по мере необходимости.

Push-уведомления - это, безусловно, очень интересная функция. Чем больше вы с ней работаете, тем удобнее ее реализовывать. Обратите внимание, что вы можете отправлять push-уведомления непосредственно из панели Firebase, не взаимодействуя с бэкендом. Это в основном для целей разработки и тестирования. Она также используется во время работы приложения для отправки широковещательных уведомлений нескольким пользователям. Однако в данной статье речь идет об использовании бэкенда node.js, поскольку он отвечает потребностям большинства разработчиков.

Ресурсы

Следующие ресурсы могут помочь вам в настройке push-уведомлений для вашего приложения:

  1. Firebase core Package
  2. Firebase Messaging Package
  3. Консоль Firebase
  4. Flutter App Badger Package
  5. Flutter Local Notification Package

Источник:

#NodeJS #Flutter #Android #IOS
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться