Уведомления
Отображение нативных системных уведомлений с кнопками действий и полем ввода текста
Введение
Wails предоставляет комплексную кроссплатформенную систему уведомлений для настольных приложений. Этот сервис позволяет отображать нативные системные уведомления и поддерживает:
- Простые уведомления с заголовком, подзаголовком и основным текстом
- Интерактивные уведомления с кнопками действий и текстовыми ответами
- Многократно используемые категории уведомлений для действий
- Пользовательские звуки (стандартные, отключённые или заданные по имени)
- Вложения (изображения на всех платформах; аудио и видео в macOS)
- Группировку связанных уведомлений по
ThreadID - Приоритет с помощью
InterruptionLevel(passive/active/timeSensitive/critical) - Доставку по расписанию (нативными средствами в macOS; с помощью внутрипроцессного таймера в Windows и Linux)
- Обновление уже отправленного уведомления по идентификатору
Если платформа не поддерживает новое необязательное поле, связанная с ним функциональность корректно упрощается; матрицу поддержки отдельных возможностей см. в разделе Особенности платформ.
Основы использования
Создание сервиса
Сначала инициализируйте сервис уведомлений:
import "github.com/wailsapp/wails/v3/pkg/application"
import "github.com/wailsapp/wails/v3/pkg/services/notifications"
// Create a new notification service
notifier := notifications.New()
//Register the service with the application
app := application.New(application.Options{
Services: []application.Service{
application.NewService(notifier),
},
})Разрешение на уведомления
Для показа уведомлений в macOS требуется разрешение пользователя. Запросите разрешение и проверьте его наличие:
authorized, err := notifier.CheckNotificationAuthorization()
if err != nil {
// Handle authorization error
}
if authorized {
// Send notifications
} else {
// Request authorization
authorized, err = notifier.RequestNotificationAuthorization()
}В Windows и Linux этот вызов всегда возвращает true.
Типы уведомлений
Простые уведомления
Отправьте пользователям простое уведомление с уникальным идентификатором, заголовком, необязательным подзаголовком (в macOS и Linux) и основным текстом:
notifier.SendNotification(notifications.NotificationOptions{
ID: "unique-id",
Title: "New Calendar Invite",
Subtitle: "From: Jane Doe", // Optional
Body: "Tap to view the event",
})
Интерактивные уведомления
Отправьте уведомление с кнопками действий и полями ввода текста. Для таких уведомлений необходимо сначала зарегистрировать категорию уведомлений:
// Define a unique category id
categoryID := "unique-category-id"
// Define a category with actions
category := notifications.NotificationCategory{
ID: categoryID,
Actions: []notifications.NotificationAction{
{
ID: "OPEN",
Title: "Open",
},
{
ID: "ARCHIVE",
Title: "Archive",
Destructive: true, /* macOS-specific */
},
},
HasReplyField: true,
ReplyPlaceholder: "message...",
ReplyButtonTitle: "Reply",
}
// Register the category
notifier.RegisterNotificationCategory(category)
// Send an interactive notification with the actions registered in the provided category
notifier.SendNotificationWithActions(notifications.NotificationOptions{
ID: "unique-id",
Title: "New Message",
Subtitle: "From: Jane Doe",
Body: "Are you able to make it?",
CategoryID: categoryID,
})Ответы на уведомления
Обработайте взаимодействия пользователя с уведомлениями:
notifier.OnNotificationResponse(func(result notifications.NotificationResult) {
response := result.Response
fmt.Printf("Notification %s was actioned with: %s\n", response.ID, response.ActionIdentifier)
if response.ActionIdentifier == "TEXT_REPLY" {
fmt.Printf("User replied: %s\n", response.UserText)
}
if data, ok := response.UserInfo["sender"].(string); ok {
fmt.Printf("Original sender: %s\n", data)
}
// Emit an event to the frontend
app.Event.Emit("notification", result.Response)
})Настройка уведомлений
Пользовательские метаданные
В простые и интерактивные уведомления можно включать пользовательские данные:
notifier.SendNotification(notifications.NotificationOptions{
ID: "unique-id",
Title: "New Calendar Invite",
Subtitle: "From: Jane Doe", // Optional
Body: "Tap to view the event",
Data: map[string]interface{}{
"sender": "jane.doe@example.com",
"timestamp": "2025-03-10T15:30:00Z",
}
})Пользовательский звук
Используйте Sound, чтобы управлять звуком при доставке уведомления. Если оставить значение nil, будет воспроизведён стандартный звук платформы; задайте Silent: true, чтобы отключить звук; задайте Name, чтобы воспроизвести звук с указанным именем или из комплекта приложения.
// Silent
notifier.SendNotification(notifications.NotificationOptions{
ID: "silent-id",
Title: "Background sync complete",
Sound: ¬ifications.NotificationSound{Silent: true},
})
// Named sound
notifier.SendNotification(notifications.NotificationOptions{
ID: "named-id",
Title: "New message",
Body: "Tap to read",
Sound: ¬ifications.NotificationSound{Name: "Ping"},
})Разрешение значения Name на каждой платформе:
- macOS — значение
Nameпередаётся в[UNNotificationSound soundNamed:]; аудиофайл должен находиться в каталогеLibrary/Soundsкомплекта приложения. - Windows — если значение
Nameуже начинается сms-winsoundevent:илиms-appx:, оно используется без изменений; в противном случае оно помещается вms-winsoundevent:и используется как имя встроенного события всплывающего уведомления (см. документацию Microsoft по схеме всплывающих уведомлений<audio>). - Linux — значение передаётся как подсказка freedesktop
sound-name; воспроизведение зависит от активной службы уведомлений и звуковой темы.
Вложения
Attachments добавляет к уведомлению медиафайлы. macOS поддерживает несколько вложений с медиафайлами любого типа; Windows и Linux обрабатывают первое вложение типа «изображение».
notifier.SendNotification(notifications.NotificationOptions{
ID: "image-id",
Title: "Photo uploaded",
Body: "Tap to view",
Attachments: []notifications.NotificationAttachment{
{
ID: "preview",
Path: "/absolute/path/to/image.png",
// Type hint:
// macOS: UTI like "public.png" / "public.audio" (often inferred)
// Windows: "hero" | "appLogoOverride" | "inline" (default "inline")
// Linux: ignored
Type: "inline",
},
},
})Path должен содержать абсолютный путь в файловой системе. macOS также принимает URL-адреса file://.
Прикрепление файла из комплекта приложения
ОС считывает вложение с диска при доставке уведомления, поэтому значение Path должно разрешаться в реальный файл на компьютере конечного пользователя. Для ресурса из комплекта приложения (значка или изображения, встроенного с помощью go:embed) нельзя жёстко задать постоянный абсолютный путь: файл находится внутри исполняемого файла, а не в известном расположении на диске. Один раз при запуске запишите его в каталог, доступный для записи, и передайте полученный путь:
import (
_ "embed"
"os"
"path/filepath"
)
//go:embed assets/preview.png
var previewPNG []byte
// Materialise the embedded asset to a stable path the OS can read.
previewPath := filepath.Join(os.TempDir(), "myapp-preview.png")
if err := os.WriteFile(previewPath, previewPNG, 0o644); err != nil {
// handle error
}
notifier.SendNotification(notifications.NotificationOptions{
ID: "image-id",
Title: "Photo uploaded",
Body: "Tap to view",
Attachments: []notifications.NotificationAttachment{
{ID: "preview", Path: previewPath, Type: "inline"},
},
})У предоставленного пользователем или загруженного файла уже есть реальный путь на диске, поэтому его можно передать непосредственно в Path, пропустив этот шаг. Возможность передавать байты вложения из памяти может появиться в одном из будущих выпусков.
Цепочки и группировка
ThreadID объединяет связанные уведомления, позволяя ОС сворачивать их в Центре уведомлений / Центре действий.
notifier.SendNotification(notifications.NotificationOptions{
ID: "msg-42",
Title: "Jane Doe",
Body: "Are you free for lunch?",
ThreadID: "chat:jane.doe",
})Уровень прерывания
InterruptionLevel управляет приоритетом уведомления. Используйте одну из экспортируемых констант:
notifier.SendNotification(notifications.NotificationOptions{
ID: "alert-id",
Title: "Server down",
Body: "Investigate immediately",
InterruptionLevel: notifications.InterruptionLevelTimeSensitive,
})| Константа | Значение | Назначение |
|---|---|---|
InterruptionLevelPassive |
"passive" |
Тихая доставка: экран не включается и стандартный звук не воспроизводится |
InterruptionLevelActive |
"active" |
Стандартный уровень |
InterruptionLevelTimeSensitive |
"timeSensitive" |
Обходит режим «Фокусирование» / «Не беспокоить», если это разрешено |
InterruptionLevelCritical |
"critical" |
Обходит режим фокусирования и беззвучный режим; в macOS требуется право Critical Alert (без него уровень незаметно понижается) |
Соответствие платформам:
- macOS — задаёт
UNNotificationContent.interruptionLevel. Дляcriticalтребуется macOS 12+ и право Critical Alert. - Windows — сопоставляется с атрибутом всплывающего уведомления
<toast scenario="...">. - Linux — сопоставляется с подсказкой freedesktop
urgency.
Отложенная доставка
Schedule откладывает доставку. Задайте ровно одно из значений: DelaySeconds (количество секунд от текущего момента) или At (время Unix в секундах, UTC).
// Deliver in 60 seconds
notifier.SendNotification(notifications.NotificationOptions{
ID: "reminder-id",
Title: "Stand up and stretch",
Schedule: ¬ifications.NotificationSchedule{DelaySeconds: 60},
})
// Deliver at an absolute time
notifier.SendNotification(notifications.NotificationOptions{
ID: "alarm-id",
Title: "Meeting in 5 minutes",
Schedule: ¬ifications.NotificationSchedule{At: time.Now().Add(time.Hour).Unix()},
})Обновление уведомлений
UpdateNotification заменяет активное уведомление с тем же ID:
notifier.UpdateNotification(notifications.NotificationOptions{
ID: "download-id",
Title: "Download complete",
Body: "report.pdf is ready",
})Поведение на разных платформах:
- macOS —
UNUserNotificationCenterавтоматически устраняет дубликаты по идентификатору, поэтому существующее уведомление обновляется на месте. - Linux — для замены предыдущего уведомления используется параметр D-Bus
replaces_id. - Windows — сейчас уведомление повторно доставляется как новое. Для полноценной замены на месте требуется поддержка
tag/groupв вышестоящем проектеwintoast.
Особенности платформ
Уведомления в macOS:
- Требуют разрешения пользователя
- Требуют, чтобы приложение было упаковано и подписано (а для распространения — нотариально заверено)
- Используют стандартный системный внешний вид уведомлений
- Поддерживают
Subtitle - Поддерживают ввод текста пользователем (ответы)
- Поддерживают параметр действия
Destructive - Поддерживают несколько
Attachmentsлюбого типа мультимедиа (изображения, аудио и видео) - Поддерживают
ThreadIDдля группировки в Центре уведомлений - Поддерживают все значения
InterruptionLevel(дляcriticalтребуется право Critical Alert) - Поддерживают нативную отложенную доставку, сохраняющуюся после перезапуска приложения
- Автоматически устраняют дубликаты вызовов
UpdateNotificationпоID - Автоматически учитывают тёмный или светлый режим
Уведомления в Windows:
- Используют системные стили всплывающих уведомлений Windows через бэкенд
wintoast - Адаптируются к настройкам темы Windows
- Поддерживают ввод текста пользователем (ответы)
- Поддерживают дисплеи с высокой плотностью пикселей
- Не поддерживают
Subtitle - Поддерживают одно изображение
Attachmentс подсказкой размещенияhero,appLogoOverrideилиinline(по умолчанию —inline) - Поддерживают
ThreadIDдля группировки в Центре уведомлений - Поддерживают
InterruptionLevelчерез атрибут всплывающего уведомленияscenario - Поддерживают отложенную доставку с помощью внутрипроцессного таймера — запланированные уведомления будут потеряны, если приложение завершит работу до доставки
UpdateNotificationсейчас повторно доставляет уведомление как новое (полноценная замена на месте ожидает поддержкиtag/groupв вышестоящем проектеwintoast)
В Linux уведомления используют интерфейс D-Bus org.freedesktop.Notifications. Для работы уведомлений совместимый демон уведомлений должен быть запущен.
Уведомления в Linux:
- Следуют теме рабочего окружения
- Располагаются в соответствии с правилами рабочего окружения
- Поддерживают
Subtitle(для демонов, которые не отображают его отдельно, он добавляется к тексту уведомления) - Не поддерживают ввод текста пользователем (он не входит в спецификацию freedesktop)
- Поддерживают одно изображение
Attachmentчерез подсказкуimage-path - Поддерживают
ThreadID(обрабатывается службой уведомлений, если она это поддерживает) Sound.Nameпередаётся как подсказкаsound-name; будет ли воспроизводиться звук, зависит от активной службы уведомлений и звуковой темы- Сопоставляют
InterruptionLevelс подсказкой freedesktopurgency - Поддерживают доставку по расписанию с помощью внутрипроцессного таймера — если приложение завершит работу до момента доставки, запланированные уведомления будут потеряны
UpdateNotificationиспользует параметр D-Busreplaces_id, чтобы заменить предыдущее уведомление на месте
Рекомендации
- Проверяйте наличие разрешения и запрашивайте его:
- В macOS требуется разрешение пользователя
- Делайте уведомления понятными и лаконичными:
- Используйте информативные заголовки, подзаголовки, текст и названия действий
- Правильно обрабатывайте ответы на уведомления:
- Проверяйте ответы на уведомления на наличие ошибок
- Предоставляйте обратную связь о действиях пользователя
- Учитывайте соглашения платформы:
- Следуйте принятым на платформе шаблонам уведомлений
- Учитывайте системные настройки
- В Linux учитывайте зависимость от службы уведомлений:
- Проверяйте ошибку, возвращаемую
SendNotification: отсутствие службы уведомлений приводит к ошибке D-Bus - В документации пакета или README приложения следует указать, что требуется служба уведомлений, совместимая с freedesktop
Примеры
Ознакомьтесь с этим примером:
Справочник API
Управление службой
| Метод | Описание |
|---|---|
New() |
Создаёт новую службу уведомлений |
Разрешение на уведомления
| Метод | Описание |
|---|---|
RequestNotificationAuthorization() |
Запрашивает разрешение на отображение уведомлений (macOS) |
CheckNotificationAuthorization() |
Проверяет текущий статус разрешения на уведомления (macOS) |
Отправка уведомлений
| Метод | Описание |
|---|---|
SendNotification(options NotificationOptions) |
Отправляет простое уведомление |
SendNotificationWithActions(options NotificationOptions) |
Отправляет интерактивное уведомление с действиями |
UpdateNotification(options NotificationOptions) |
Обновляет находящееся в обработке уведомление по ID (см. раздел «Обновление уведомлений») |
Категории уведомлений
| Метод | Описание |
|---|---|
RegisterNotificationCategory(category NotificationCategory) |
Регистрирует категорию уведомлений для многократного использования |
RemoveNotificationCategory(categoryID string) |
Удаляет ранее зарегистрированную категорию |
Управление уведомлениями
| Метод | Описание |
|---|---|
RemoveAllPendingNotifications() |
Удаляет все ожидающие уведомления (только в macOS и Linux) |
RemovePendingNotification(identifier string) |
Удаляет указанное ожидающее уведомление (только в macOS и Linux) |
RemoveAllDeliveredNotifications() |
Удаляет все доставленные уведомления (только в macOS и Linux) |
RemoveDeliveredNotification(identifier string) |
Удаляет указанное доставленное уведомление (только в macOS и Linux) |
RemoveNotification(identifier string) |
Удаляет уведомление (только в Linux) |
Обработка событий
| Метод | Описание |
|---|---|
OnNotificationResponse(callback func(result NotificationResult)) |
Регистрирует функцию обратного вызова для ответов на уведомления |
Структуры и типы
NotificationOptions
type NotificationOptions struct {
ID string // Unique identifier for the notification (required)
Title string // Main notification title (required)
Subtitle string // Subtitle text (macOS and Linux only)
Body string // Main notification content
CategoryID string // Category identifier for interactive notifications
Data map[string]interface{} // Custom data to associate with the notification
// Sound controls the sound played on delivery. nil = platform default.
Sound *NotificationSound
// Attachments are media files shown alongside the notification.
// macOS supports multiple attachments of any media type;
// Windows and Linux honour the first image-typed attachment.
Attachments []NotificationAttachment
// ThreadID groups related notifications together in
// Notification Center / Action Center / the Linux notification daemon.
ThreadID string
// InterruptionLevel controls priority. One of "passive",
// "active" (default), "timeSensitive", "critical".
InterruptionLevel string
// Schedule defers delivery. macOS uses a native trigger that survives
// restarts; Windows and Linux use an in-process timer that does NOT.
Schedule *NotificationSchedule
}NotificationSound
type NotificationSound struct {
Silent bool // If true, no sound is played
Name string // Named/bundled sound (see "Custom Sound" above)
}NotificationAttachment
type NotificationAttachment struct {
ID string // Optional identifier
Path string // Absolute filesystem path (macOS also accepts file:// URLs)
// Type is an optional placement/UTI hint:
// macOS: UTI like "public.png" / "public.audio" (often inferred)
// Windows: "hero" | "appLogoOverride" | "inline" (default "inline")
// Linux: ignored (always image-path hint)
Type string
}NotificationSchedule
// Exactly one of DelaySeconds or At must be set. At is Unix seconds (UTC).
type NotificationSchedule struct {
DelaySeconds int // Seconds from now until delivery
At int64 // Absolute Unix timestamp (seconds, UTC)
}Константы InterruptionLevel
const (
InterruptionLevelPassive = "passive"
InterruptionLevelActive = "active" // default
InterruptionLevelTimeSensitive = "timeSensitive"
InterruptionLevelCritical = "critical"
)NotificationCategory
type NotificationCategory struct {
ID string // Unique identifier for the category
Actions []NotificationAction // Button actions for the notification
HasReplyField bool // Whether to include a text input field
ReplyPlaceholder string // Placeholder text for the input field
ReplyButtonTitle string // Text for the reply button
}NotificationAction
type NotificationAction struct {
ID string // Unique identifier for the action
Title string // Button text
Destructive bool // Whether the action is destructive (macOS-specific)
}NotificationResponse
type NotificationResponse struct {
ID string // Notification identifier
ActionIdentifier string // Action that was triggered
CategoryID string // Category of the notification
Title string // Title of the notification
Subtitle string // Subtitle of the notification
Body string // Body text of the notification
UserText string // Text entered by the user
UserInfo map[string]interface{} // Custom data from the notification
}NotificationResult
type NotificationResult struct {
Response NotificationResponse // Response data
Error error // Any error that occurred
}