Перейти к основному содержимому

React Native библиотека

Pushed React Native - интеграция Pushed.ru и React Native

Обзор

Этот пакет позволяет интегрировать службу push-уведомлений Pushed.ru в ваше приложение React Native.

Примеры использования

Вы можете посмотреть пример использования запустив приложение example из этого репозитория.

Инструкция по использованию

Выполните следующие шаги, чтобы использовать пакет pushed-react-native в своем приложении React Native.

1. Установите библиотеку

Запустите следующую команду, чтобы установить библиотеку:

npm install github:PushedLab/Pushed.Messaging.ReactNative

2. Импортируйте необходимые методы и типы

Импортируйте необходимые методы и типы из библиотеки:

import {
startService,
stopService,
PushedEventTypes,
Push,
} from 'pushed-react-native';

3. Подпишитесь на событие PushedEventTypes.PUSH_RECEIVED

Подпишитесь на событие PUSH_RECEIVED для обработки входящих push-уведомлений:

import React, { useEffect } from 'react';
import { NativeEventEmitter, NativeModules } from 'react-native';
import { displayNotification } from './Notifee';

useEffect(() => {
const eventEmitter = new NativeEventEmitter(NativeModules.PushedReactNative);
const eventListener = eventEmitter.addListener(
PushedEventTypes.PUSH_RECEIVED,
(push: Push) => {
console.log(push);
displayNotification(push.title, push.body);
}
);

// Remove the listener when the component unmounts
return () => {
eventListener.remove();
};
}, []);

4. Запустите сервис приема сообщений

Используйте функцию startService для запуска foreground-службы, отвечающей за прием сообщений: Для связи приложения с клиентским токеном необходимо передать идентификатор приложения. Его можно получить в личном кабинете admin.multipushed.ru → Приложения → поле «Идентификатор».

const handleStart = () => {
console.log('Starting Pushed Service');
startService('PushedService', 'YOUR_APPLICATION_ID').then((newToken) => {
console.log(`Service has started: ${newToken}`);
});
};

5. Остановите сервис приема сообщений

Используйте функцию stopService для остановки foreground-службы, отвечающей за прием сообщений:

const handleStop = () => {
stopService().then((m) => {
console.log(m);
});
};

Особенности работы в iOS

  1. Сообщения доставляются посредством службы apns и обрабатываются самим устройством в фоновом режиме. startService можно вызвать только один раз, чтобы зарегистрировать клиенсткий токен.
  2. Необходимо настроить ваше приложение для работы с apns в панели управления Pushed (см. статью)[https://pushed.ru/docs/apns/]
  3. Убедитесь, что приложение имеет разрешения Push Notifications и Background Modes -> Remote Notifications

Настройка Notification Service Extension (iOS)

Для автоматического подтверждения получения сообщений (endpoint /confirm) обязательно добавьте Notification Service Extension в ваше iOS-приложение. Расширение перехватывает входящий push до его показа системой и отправляет подтверждение от имени пользователя.

Шаги настройки:

  1. Откройте проект в Xcode и выберите File ▸ New ▸ Target…, затем в списке iOS выберите Notification Service Extension.
  2. Задайте имя, например AppNotiService, и завершите мастер. Xcode создаст файл NotificationService.swift.
  3. Замените содержимое созданного файла на следующий код:
import UserNotifications
import Foundation
import Security

@objc(NotificationService)
class NotificationService: UNNotificationServiceExtension {

var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?

override func didReceive(
_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
) {
self.contentHandler = contentHandler
bestAttemptContent = request.content.mutableCopy() as? UNMutableNotificationContent

guard let bestAttemptContent else {
contentHandler(request.content)
return
}

NSLog("[Extension] didReceiveNotificationRequest called with userInfo: \(request.content.userInfo)")

// Всегда пытаемся подтвердить сообщение
if let messageId = request.content.userInfo["messageId"] as? String {
NSLog("[Extension] Confirming message with ID: \(messageId)")
confirmMessage(messageId: messageId)
} else {
NSLog("[Extension] No messageId found for confirmation")
}

// Always send the content to system
contentHandler(bestAttemptContent)
}

override func serviceExtensionTimeWillExpire() {
// Вызывается прямо перед тем, как система завершит работу расширения.
// Используйте это как возможность доставить ваш "лучший" вариант измененного контента,
// в противном случае будет использована исходная полезная нагрузка push-уведомления.
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
}

// MARK: - Keychain Access Methods

/// Получение токена из Keychain
private func getTokenFromKeychain() -> String? {
var query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccount: "pushed_token",
kSecAttrService: "pushed_messaging_service",
kSecReturnData: true,
kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlock,
kSecAttrSynchronizable: false
]

var ref: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &ref)

guard status == errSecSuccess, let data = ref as? Data else {
NSLog("[Extension Keychain] Failed to get token from Keychain, status: \(status)")
return nil
}

let token = String(data: data, encoding: .utf8)
NSLog("[Extension Keychain] Successfully \(token) retrieved token from Keychain")
return token
}

private func confirmMessage(messageId: String) {
NSLog("[Extension Confirm] Starting message confirmation for messageId: \(messageId)")

// Получаем токен только из Keychain
guard let clientToken = getTokenFromKeychain(), !clientToken.isEmpty else {
NSLog("[Extension Confirm] ERROR: clientToken is empty or not found in Keychain")
return
}

NSLog("[Extension Confirm] Using client token for authentication")

// Создаем Basic Auth: clientToken:messageId
let credentials = "\(clientToken):\(messageId)"
guard let credentialsData = credentials.data(using: .utf8) else {
NSLog("[Extension Confirm] ERROR: Could not encode credentials")
return
}
let basicAuth = "Basic \(credentialsData.base64EncodedString())"

// Используем эндпоинт v2 из основной библиотеки для консистентности
guard let url = URL(string: "https://pub.multipushed.ru/v2/confirm?transportKind=Apns") else {
NSLog("[Extension Confirm] ERROR: Invalid URL")
return
}

var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue(basicAuth, forHTTPHeaderField: "Authorization")

NSLog("[Extension Confirm] Sending confirmation request to: \(url.absoluteString)")

let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
NSLog("[Extension Confirm] Request error: \(error.localizedDescription)")
return
}

guard let httpResponse = response as? HTTPURLResponse else {
NSLog("[Extension Confirm] ERROR: No HTTPURLResponse")
return
}

let status = httpResponse.statusCode
let responseBody = data.flatMap { String(data: $0, encoding: .utf8) } ?? "<no body>"

if (200..<300).contains(status) {
NSLog("[Extension Confirm] SUCCESS - Status: \(status), Body: \(responseBody)")
} else {
NSLog("[Extension Confirm] ERROR - Status: \(status), Body: \(responseBody)")
}
}

task.resume()
NSLog("[Extension Confirm] Confirmation request sent for messageId: \(messageId)")
}
}
  1. В Signing & Capabilities включите Keychain Sharing для основного приложения и расширения и убедитесь, что в обоих целей указана одна и та же access-group. Это даст расширению доступ к clientToken, сохранённому библиотекой в Keychain.
  2. Убедитесь, что расширение подписывается той же командой разработчика (Team) и имеет bundle identifier в том же пространстве, например com.yourapp.notification-service.
  3. Соберите приложение. При получении push-уведомления система запустит расширение, код библиотеки отправит запрос подтверждения, после чего уведомление будет показано пользователю.

Важно: payload пуша должен содержать поле messageId. Без него подтверждение отправлено не будет.

Описание методов и типов библиотеки pushed-react-native

startService(serviceName: string): Promise<string>

Эта функция запускает службу push-уведомлений.

  • Параметры:
  • serviceName: строка, представляющая имя запускаемой службы.
  • Возвращаемое значение:
  • Promise<string>, который резолвится токеном устройства, который понадобится при отправке пуша. См. пример.

stopService(): Promise<string>

Эта функция останавливает службу push-уведомлений.

  • Возвращаемое значение:
  • Promise<string>, который резолвится сообщением, указывающим, что служба остановлена.

PushedEventTypes

Это перечисление содержит типы событий, с которыми работает система Pushed.ru

  • Перечисляемые значения:
  • PUSH_RECEIVED: представляет тип события при получении push-уведомления.

Push

Это класс, представляющий push-уведомление, он является оберткой произвольного словаря key-value

  • Constructor:
  • constructor(data: { [key: string]: string })
    • Cоздает новый экземпляр Push, используя предоставленные данные.