Реалізація локальних сповіщень у мобільному додатку
Локальні сповіщення — єдиний тип, який не вимагає сервера взагалі. Додаток сам планує їх через системний API: у конкретний час, за тригером календаря або при вході в геозону. Типові застосування — нагадування, трекери звичок, события в офлайн-режимі.
iOS: UNUserNotificationCenter
Весь API з iOS 10+ — через UNUserNotificationCenter. Три типи тригерів:
import UserNotifications
// 1. За часом (через N секунд)
let content = UNMutableNotificationContent()
content.title = "Нагадування про зустріч"
content.body = "Зустріч з командою через 15 хвилин"
content.sound = .default
content.badge = 1
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 900, repeats: false)
let request = UNNotificationRequest(identifier: "meeting-reminder-123",
content: content,
trigger: trigger)
UNUserNotificationCenter.current().add(request)
// 2. За датою/часом (повторюється кожен день о 9:00)
var dateComponents = DateComponents()
dateComponents.hour = 9
dateComponents.minute = 0
let dailyTrigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
// 3. За геозоною
let region = CLCircularRegion(center: CLLocationCoordinate2D(latitude: 50.45, longitude: 30.52),
radius: 200,
identifier: "office-zone")
region.notifyOnEntry = true
region.notifyOnExit = false
let geoTrigger = UNLocationNotificationTrigger(region: region, repeats: false)
Ідентифікатор запиту (identifier) — важливий. Використовуйте його для оновлення або скасування сповіщення:
// Оновити (перезаписати існуюче)
UNUserNotificationCenter.current().add(updatedRequest) // той же identifier
// Скасувати
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ["meeting-reminder-123"])
// Переглянути запланованих
UNUserNotificationCenter.current().getPendingNotificationRequests { requests in
print("Pending: \(requests.count)")
}
Ліміт iOS — 64 запланованих сповіщення на додаток. При перевищенні старі молча видаляються. Для трекерів зі сотнями нагадувань потрібна стратегія: тримати в черзі ближайші N, перепланувати решту при відкритті додатку.
Android: NotificationCompat + AlarmManager / WorkManager
На Android немає єдиного «notification scheduler» — потрібно самостійно зберігати час, будити додаток через AlarmManager і тоді показувати сповіщення.
// Планування через AlarmManager (точний час — вимагає SCHEDULE_EXACT_ALARM на Android 12+)
val alarmManager = context.getSystemService(AlarmManager::class.java)
val intent = Intent(context, NotificationReceiver::class.java).apply {
putExtra("title", "Нагадування про зустріч")
putExtra("body", "Зустріч через 15 хвилин")
putExtra("notification_id", 123)
}
val pendingIntent = PendingIntent.getBroadcast(context, 123, intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
triggerAtMillis,
pendingIntent
)
NotificationReceiver — BroadcastReceiver, який будує та показує сповіщення:
class NotificationReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val notification = NotificationCompat.Builder(context, "reminders_channel")
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(intent.getStringExtra("title"))
.setContentText(intent.getStringExtra("body"))
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
.build()
NotificationManagerCompat.from(context)
.notify(intent.getIntExtra("notification_id", 0), notification)
}
}
Важливо: AlarmManager скидається при перезавантаженні пристрою. Потрібно перехопити BOOT_COMPLETED broadcast та перепланувати всі активні нагадування з локальної БД (Room).
Для Android 12+ точні алармі вимагають дозволу SCHEDULE_EXACT_ALARM. Для повторюваних нагадувань без точного часу — краще WorkManager з PeriodicWorkRequest, він пережить reboot автоматично.
Геозонні сповіщення
На iOS геозонні сповіщення — через UNLocationNotificationTrigger (див. вище). На Android — через GeofencingClient:
val geofence = Geofence.Builder()
.setRequestId("office-zone")
.setCircularRegion(50.45, 30.52, 200f)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER)
.build()
val request = GeofencingRequest.Builder()
.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
.addGeofence(geofence)
.build()
geofencingClient.addGeofences(request, geofencePendingIntent)
Геозонні сповіщення вимагають дозволу ACCESS_FINE_LOCATION та на Android 10+ — ACCESS_BACKGROUND_LOCATION. Останній — окремий запит, користувач повинен явно вибрати «Дозволити завжди» у налаштуваннях.
Терміни
Реалізація локальних сповіщень за часом та розкладом, геозонні тригери, коректна обробка reboot та лімітів — 3–6 робочих днів в залежності від платформ.







