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
- Сообщения доставляются посредством службы apns и обрабатываются самим устройством в фоновом режиме. startService можно вызвать только один раз, чтобы зарегистрировать клиенсткий токен.
- Необходимо настроить ваше приложение для работы с apns в панели управления Pushed (см. статью)[https://pushed.ru/docs/apns/]
- Убедитесь, что приложение имеет разрешения Push Notifications и Background Modes -> Remote Notifications
Настройка Notification Service Extension (iOS)
Для автоматического подтверждения получения сообщений (endpoint /confirm
) обязательно добавьте Notification Service Extension в ваше iOS-приложение. Расширение перехватывает входящий push до его показа системой и отправляет подтверждение от имени пользователя.
Шаги настройки:
- Откройте проект в Xcode и выберите
File ▸ New ▸ Target…
, затем в списке iOS выберите Notification Service Extension. - Задайте имя, например
AppNotiService
, и завершите мастер. Xcode создаст файлNotificationService.swift
. - Замените содержимое созданного файла на следующий код:
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)")
}
}
- В Signing & Capabilities включите Keychain Sharing для основного приложения и расширения и убедитесь, что в обоих целей указана одна и та же access-group. Это даст расширению доступ к
clientToken
, сохранённому библиотекой в Keychain. - Убедитесь, что расширение подписывается той же командой разработчика (Team) и имеет bundle identifier в том же пространстве, например
com.yourapp.notification-service
. - Соберите приложение. При получении 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, используя предоставленные данные.