Уведомления
Для работы пуш‑уведомлений необходимо включить и настроить их отправку на сервере edna. Настройка выполняется в АРМ администратора:
- Подключение мобильного чата iOS → Настройка пуш-уведомлений — загрузка ключа
.p8или сертификата.p12, Bundle ID, тип окружения (production/sandbox) - Шаблоны пуш-уведомлений — JSON-шаблон APNs payload и доступные плейсхолдеры (
${id},${externalId},${shortMessage},${clientId})
Если у вас нет доступа в АРМ — напишите на support@edna.ru.
Передача токена
В SDK достаточно передать из приложения токен устройства:
Можно использовать до инициализации экземпляра ChatCenterUISDK
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
ChatCenterUISDK.setDeviceToken(deviceToken)
}
Настройка приложения
Если пуши в приложении ранее не использовались, необходимо предварительно настроить их в вашем аккаунте Apple и самом приложении.
Для работы didRegisterForRemoteNotificationsWithDeviceToken необходим запрос разрешений на получение уведомлений в приложении:
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, _ in
guard granted else {
return
}
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
registerForRemoteNotifications() должен вызываться на главном потоке — оборачивайте вызов в DispatchQueue.main.async.
Проверка пушей
Для определённых действий (например, переход по пушу в чат) может потребоваться метод для определения принадлежности пуша к ChatCenter:
static func isChatCenterNotification(_ userInfo: [String: Any]) -> Bool
Параметры:
userInfo: Словарь с информацией о Push-уведомлении от APNS.
Возвращает: Bool — true, если уведомление относится к ChatCenterUI SDK, иначе false.
Пример использования:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
if let userInfo = userInfo as? [String: Any],
ChatCenterUISDK.isChatCenterNotification(userInfo) {
// Это уведомление от ChatCenterUI SDK, обрабатываем его
// ...
} else {
// Это другое уведомление
// ...
}
}
Сценарии обработки уведомлений
Поведение SDK зависит от состояния чата в момент получения пуша:
| Сценарий | Состояние приложения | Что использовать |
|---|---|---|
| Экран чата открыт | Активное | handleNotification(userInfo:) — проверка состояния (см. ограничение ниже) |
| Чат закрыт, пользователь авторизован | Активное / фон | getChat(userInfo:) + показать UIViewController |
| Приложение неактивно | Background / terminated | Обработать в didReceiveRemoteNotification, вызвать getChat(userInfo:) при выходе в foreground |
func handleNotification(userInfo: [AnyHashable: Any]) throws(ChatCenterUIError)
Параметры:
userInfo: Словарь с информацией о Push-уведомлении. Должен быть предварительно проверен черезisChatCenterNotification.
Выбрасывает: ChatCenterUIError.chatNotOpened — если экран чата не отображается либо если передан пустой userInfo.
Поведение по веткам:
| Условие | Что делает метод |
|---|---|
| Экран чата не отображается | Бросает ChatCenterUIError.chatNotOpened — приложение переключается на getChat(userInfo:) |
Экран чата отображается, userInfo не пуст | Возвращается без действий — подстановка контекста в открытый чат в текущей версии не выполняется |
Экран чата отображается, userInfo == [:] | Бросает ChatCenterUIError.chatNotOpened |
При открытом экране чата с непустым userInfo метод не подставляет контекст уведомления в текущую сессию — он возвращается без действий. Если контекст обязательно должен показаться пользователю, закройте экран чата и откройте его заново через getChat(userInfo:).
Корректная обработка обоих исходов:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
guard let userInfoDict = userInfo as? [String: Any],
ChatCenterUISDK.isChatCenterNotification(userInfoDict) else {
completionHandler(.noData)
return
}
do {
try chatCenterSDK?.handleNotification(userInfo: userInfo)
completionHandler(.newData)
} catch ChatCenterUIError.chatNotOpened {
openChatFromNotification(userInfo: userInfo)
completionHandler(.newData)
} catch {
print("Ошибка обработки уведомления: \(error)")
completionHandler(.failed)
}
}
Открытие чата по пушу
func getChat(userInfo: [AnyHashable: Any]? = nil) throws(ChatCenterUIError) -> UIViewController
Метод возвращает UIViewController чата, который приложение показывает через собственный навигационный стек.
Параметры:
userInfo: Опциональный словарь Push-уведомления. Если передан — открывается как контекст уведомления (см. ниже). Без аргумента возвращает контроллер чата.
Выбрасывает: ChatCenterUIError.userNotAuthorized — если перед вызовом не был выполнен authorize(user:).
Параметр userInfo передаётся в SDK как контекст уведомления, по которому открывается чат. Поведение контекста определяется типом пуша, сконфигурированным на стороне edna:
- Массовая рассылка edna: в поле ввода чата появится цитата уведомления, а следующее отправленное сообщение уйдёт в привязке к этой кампании.
- Стандартный пуш о новом сообщении: чат откроется без дополнительных действий.
func openChatFromNotification(userInfo: [AnyHashable: Any]) {
guard let chatCenterSDK else { return }
do {
let chatController = try chatCenterSDK.getChat(userInfo: userInfo)
navigationController?.pushViewController(chatController, animated: true)
} catch {
print("Ошибка открытия чата: \(error)")
}
}
Если экран чата уже отображается, используйте handleNotification(userInfo:) — он не вызовет повторного открытия чата.
Cold start: пуш по убитому приложению
Если приложение полностью завершено (terminated) и пользователь нажимает на уведомление, iOS запускает приложение и передаёт userInfo через launchOptions[.remoteNotification] — метод didReceiveRemoteNotification в этом случае не вызывается. Сохраните userInfo в AppDelegate и обработайте после готовности UI:
final class AppDelegate: UIResponder, UIApplicationDelegate {
var pendingNotification: [String: Any]?
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Сохраняем payload — будем обрабатывать после авторизации
if let userInfo = launchOptions?[.remoteNotification] as? [String: Any],
ChatCenterUISDK.isChatCenterNotification(userInfo) {
self.pendingNotification = userInfo
}
return true
}
}
После того как стартовый flow завершил authorize(user:), откройте чат с сохранённым userInfo:
func didFinishStartupAndAuthorize() {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate,
let userInfo = appDelegate.pendingNotification else { return }
do {
let controller = try chatCenterSDK.getChat(userInfo: userInfo)
navigationController?.pushViewController(controller, animated: true)
} catch {
print("Не удалось открыть чат по уведомлению: \(error)")
}
appDelegate.pendingNotification = nil
}
getChatДо вызова authorize(user:) метод getChat(userInfo:) бросит .userNotAuthorized. Поэтому обработку отложенного userInfo всегда выполняйте после того, как пользователь авторизован — не в didFinishLaunchingWithOptions напрямую.
Если вы ожидали цитату уведомления в поле ввода чата, но её нет — проверьте формат payload в шаблонах пуш-уведомлений. Поля для iOS размещаются на уровне aps, а не в data. Если шаблон оформлен корректно, но цитата не приходит — обратитесь в поддержку edna: формирование payload для массовых рассылок происходит на стороне edna, а не в мобильном приложении.