expo-notifications
expo-notifications ile push ve local notification kurulumu. iOS (APNs) + Android (FCM) credentials, permission flow, channels (Android 8+), categories ve scheduled notifications.
Ne Yapar
expo-notificationssetup ve permission istegi- Expo Push Token alma + backend'e gonderme
- iOS APNs Auth Key ve Android FCM Service Account
- Notification categories + action buttons
- Scheduled / repeating notifications
- Channels (Android 8+) ve importance levels
- Background notification handler ve silent push
Kurulum
npx expo install expo-notifications expo-device expo-constants
app.json:
{
"expo": {
"plugins": [
["expo-notifications", {
"icon": "./assets/notification-icon.png",
"color": "#ffffff",
"sounds": ["./assets/notification-sound.wav"]
}]
],
"ios": {
"infoPlist": {
"UIBackgroundModes": ["remote-notification"]
}
},
"android": {
"googleServicesFile": "./google-services.json"
}
}
}
Permission + Token
import * as Notifications from "expo-notifications";
import * as Device from "expo-device";
import Constants from "expo-constants";
import { Platform } from "react-native";
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: true,
}),
});
export async function registerForPushNotificationsAsync(): Promise<string | undefined> {
if (!Device.isDevice) {
console.warn("Push notifications require a physical device");
return;
}
if (Platform.OS === "android") {
await Notifications.setNotificationChannelAsync("default", {
name: "Default",
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: "#FF231F7C",
});
}
const { status: existing } = await Notifications.getPermissionsAsync();
let finalStatus = existing;
if (existing !== "granted") {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== "granted") return;
const projectId = Constants.expoConfig?.extra?.eas?.projectId;
const token = (await Notifications.getExpoPushTokenAsync({ projectId })).data;
return token;
}
iOS APNs Credentials
EAS uzerinden:
eas credentials -p ios
# > Push Notifications: Setup
# > APNs Auth Key (.p8)
App Store Connect > Keys > APNs Auth Key:
- Tek key tum app'lerde kullanilabilir
- Bir kez indirilebilir — yedek tut
Android FCM Credentials
# 1. Firebase Console > Project Settings > Service Accounts
# 2. "Generate new private key" → google-services.json indir
# 3. Proje koklerine koy
# 4. eas credentials -p android > Service Account upload
app.json:
{
"android": { "googleServicesFile": "./google-services.json" }
}
google-services.json.gitignore'a EKLE (commit etme; EAS Secrets veya CI'da inject).
Local & Scheduled Notification
// Hemen
await Notifications.scheduleNotificationAsync({
content: { title: "Test", body: "Hello", sound: true },
trigger: null,
});
// 60 sn sonra
await Notifications.scheduleNotificationAsync({
content: { title: "Hatirla", body: "Su iciver" },
trigger: { seconds: 60 },
});
// Belirli saat
await Notifications.scheduleNotificationAsync({
content: { title: "Sabah" },
trigger: { hour: 9, minute: 0, repeats: true },
});
// Calendar trigger (iOS only ileri)
await Notifications.scheduleNotificationAsync({
content: { title: "Pazartesi" },
trigger: { weekday: 2, hour: 9, minute: 0, repeats: true },
});
Categories + Action Buttons
await Notifications.setNotificationCategoryAsync("message", [
{
identifier: "reply",
buttonTitle: "Yanitla",
textInput: { submitButtonTitle: "Gonder", placeholder: "Mesaj..." },
},
{
identifier: "mark_read",
buttonTitle: "Okundu",
options: { opensAppToForeground: false },
},
]);
await Notifications.scheduleNotificationAsync({
content: { title: "Yeni mesaj", body: "...", categoryIdentifier: "message" },
trigger: null,
});
Action handler:
Notifications.addNotificationResponseReceivedListener((response) => {
const action = response.actionIdentifier;
const userInput = (response as any).userText;
// ...
});
Android Channels (8+)
await Notifications.setNotificationChannelAsync("messages", {
name: "Mesajlar",
importance: Notifications.AndroidImportance.HIGH,
vibrationPattern: [0, 250, 250, 250],
lightColor: "#FF231F7C",
sound: "default",
});
await Notifications.setNotificationChannelAsync("silent", {
name: "Sessiz bildirimler",
importance: Notifications.AndroidImportance.LOW,
});
Android 8+ icin channel ZORUNLU. Channel yoksa bildirim gosterilmez.
Push Gonderme (Expo Push Service)
curl -X POST https://exp.host/--/api/v2/push/send \
-H "Content-Type: application/json" \
-d '{
"to": "ExponentPushToken[xxx]",
"title": "Selam",
"body": "Test",
"sound": "default",
"categoryId": "message",
"data": { "screen": "/posts/42" }
}'
Backend SDK: expo-server-sdk (Node.js).
Deep Link via Notification
const responseListener = Notifications.addNotificationResponseReceivedListener((res) => {
const path = res.notification.request.content.data?.screen;
if (path) router.push(path);
});
Best Practices
- Permission isteme zamanlamasi: feature gerektiginde, app acilir acilmaz degil
- Channel'lar her zaman setup (Android 8+)
- Silent push badge update icin (
contentAvailable: true) - Token yenilenmesini dinle:
addPushTokenListener - Backend'de token sakla (user + device key)
- Test cihazi: gercek cihaz zorunlu (simulator push almaz)
- Sound dosyalari plugin'de tanimla, build'e gomulsun
Sik Hata Kaliplari
- iOS push gelmiyor: APNs Auth Key eksik, capability "Push Notifications" yok
- Android push gelmiyor:
google-services.jsoneksik, FCM Service Account yanlis - Channel yok → Android 8+ sessiz reddediyor
UIBackgroundModes: ["remote-notification"]yok → silent push gelmez- Production'da
Notifications.scheduleNotificationAsynctrigger: nullyerine{ seconds: 1 }→ daha guvenli - Permission request flood → kullanici reddeder, bir daha ac demek soyle
Hard Refusal
- Kullanici onayi olmadan permission abuse (her saat sor)
- Spam/yaniltici push iceriği (ASC + Play Store kurali)
- Health/finance verisini sifresiz push payload'da gondermek
- Marketing push'u opt-out olmadan zorunlu kilmak
- Tracking ID'sini push token ile cross-reference etmek (GDPR)
Cikti Formati
- Kurulum komutlari (kopya-yapistir)
- Permission flow kodu
- iOS + Android credentials adimlari
- Channel ve category setup
- Test komutu (curl)
- Risk: permission abuse, store kurali