push-notification-setup
About
This skill provides complete implementation guidance for cross-platform push notifications using Firebase Cloud Messaging and Apple Push Notification service. It covers notification setup, handling, and backend integration for both iOS and Android applications. Use it when implementing real-time user notifications, deep linking, or engagement features in mobile apps.
Documentation
Push Notification Setup
Overview
Implement comprehensive push notification systems for iOS and Android applications using Firebase Cloud Messaging and native platform services.
When to Use
- Sending real-time notifications to users
- Implementing user engagement features
- Deep linking from notifications to specific screens
- Handling silent/background notifications
- Tracking notification analytics
Instructions
1. Firebase Cloud Messaging Setup
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
export async function initializeFirebase() {
try {
if (Platform.OS === 'ios') {
const permission = await messaging().requestPermission();
if (permission === messaging.AuthorizationStatus.AUTHORIZED) {
console.log('iOS notification permission granted');
}
}
const token = await messaging().getToken();
console.log('FCM Token:', token);
await saveTokenToBackend(token);
messaging().onTokenRefresh(async (newToken) => {
await saveTokenToBackend(newToken);
});
messaging().onMessage(async (remoteMessage) => {
console.log('Notification received:', remoteMessage);
showLocalNotification(remoteMessage);
});
messaging().setBackgroundMessageHandler(async (remoteMessage) => {
if (remoteMessage.data?.type === 'sync') {
syncData();
}
});
messaging()
.getInitialNotification()
.then((remoteMessage) => {
if (remoteMessage) {
handleNotificationOpen(remoteMessage);
}
});
messaging().onNotificationOpenedApp((remoteMessage) => {
handleNotificationOpen(remoteMessage);
});
} catch (error) {
console.error('Firebase initialization failed:', error);
}
}
export async function saveTokenToBackend(token) {
try {
const response = await fetch('https://api.example.com/device-tokens', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
token,
platform: Platform.OS,
timestamp: new Date().toISOString()
})
});
if (!response.ok) {
console.error('Failed to save token');
}
} catch (error) {
console.error('Error saving token:', error);
}
}
function handleNotificationOpen(remoteMessage) {
const { data } = remoteMessage;
if (data?.deepLink) {
navigationRef.navigate(data.deepLink, JSON.parse(data.params || '{}'));
}
}
2. iOS Native Setup with Swift
import UIKit
import UserNotifications
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
requestNotificationPermission()
if let remoteNotification = launchOptions?[.remoteNotification] as? [AnyHashable: Any] {
handlePushNotification(remoteNotification)
}
return true
}
func requestNotificationPermission() {
UNUserNotificationCenter.current().requestAuthorization(
options: [.alert, .sound, .badge]
) { granted, error in
if granted {
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
}
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
print("Device Token: \(token)")
saveTokenToBackend(token: token)
}
func application(
_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error
) {
print("Failed to register: \(error)")
}
func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler:
@escaping (UNNotificationPresentationOptions) -> Void
) {
let userInfo = notification.request.content.userInfo
if #available(iOS 14.0, *) {
completionHandler([.banner, .sound, .badge])
} else {
completionHandler([.sound, .badge])
}
handlePushNotification(userInfo)
}
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
let userInfo = response.notification.request.content.userInfo
handlePushNotification(userInfo)
completionHandler()
}
private func handlePushNotification(_ userInfo: [AnyHashable: Any]) {
if let deepLink = userInfo["deepLink"] as? String {
NotificationCenter.default.post(
name: NSNotification.Name("openDeepLink"),
object: deepLink
)
}
}
private func saveTokenToBackend(token: String) {
let urlString = "https://api.example.com/device-tokens"
guard let url = URL(string: urlString) else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let body: [String: Any] = ["token": token, "platform": "ios"]
request.httpBody = try? JSONSerialization.data(withJSONObject: body)
URLSession.shared.dataTask(with: request).resume()
}
}
3. Android Setup with Kotlin
// AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application>
<service
android:name=".services.MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
</manifest>
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
class MyFirebaseMessagingService : FirebaseMessagingService() {
override fun onNewToken(token: String) {
super.onNewToken(token)
println("FCM Token: $token")
saveTokenToBackend(token)
}
override fun onMessageReceived(remoteMessage: RemoteMessage) {
super.onMessageReceived(remoteMessage)
val title = remoteMessage.notification?.title ?: "Notification"
val body = remoteMessage.notification?.body ?: ""
val deepLink = remoteMessage.data["deepLink"] ?: ""
if (remoteMessage.notification != null) {
showNotification(title, body, deepLink)
}
}
private fun showNotification(title: String, message: String, deepLink: String = "") {
val channelId = "default_channel"
createNotificationChannel(channelId)
val intent = Intent(this, MainActivity::class.java).apply {
if (deepLink.isNotEmpty()) {
data = android.net.Uri.parse(deepLink)
}
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
}
val pendingIntent = PendingIntent.getActivity(
this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
val notification = NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText(message)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.build()
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(System.currentTimeMillis().toInt(), notification)
}
private fun createNotificationChannel(channelId: String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
channelId,
"Default Channel",
NotificationManager.IMPORTANCE_DEFAULT
)
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
private fun saveTokenToBackend(token: String) {
println("Saving token to backend: $token")
}
}
4. Flutter Implementation
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
class NotificationHandler {
static Future<void> initialize(NavigatorState navigator) async {
final settings = await FirebaseMessaging.instance.requestPermission(
alert: true,
sound: true,
badge: true,
);
if (settings.authorizationStatus == AuthorizationStatus.authorized) {
print('Notification permission granted');
}
final token = await FirebaseMessaging.instance.getToken();
print('FCM Token: $token');
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Received: ${message.notification?.title}');
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
_handleDeepLink(navigator, message.data);
});
final initialMessage = await FirebaseMessaging.instance.getInitialMessage();
if (initialMessage != null) {
_handleDeepLink(navigator, initialMessage.data);
}
}
static void _handleDeepLink(NavigatorState navigator, Map<String, dynamic> data) {
final deepLink = data['deepLink'] as String?;
if (deepLink != null) {
navigator.pushNamed(deepLink);
}
}
}
Best Practices
✅ DO
- Request permission before sending notifications
- Implement token refresh handling
- Use different notification channels by priority
- Validate tokens regularly
- Track notification delivery
- Implement deep linking
- Handle notifications in all app states
- Use silent notifications for data sync
- Store tokens securely on backend
- Provide user notification preferences
- Test on real devices
❌ DON'T
- Send excessive notifications
- Send without permission
- Store tokens insecurely
- Ignore notification failures
- Send sensitive data in payload
- Use notifications for spam
- Forget to handle background notifications
- Make blocking calls in handlers
- Send duplicate notifications
- Ignore user preferences
Quick Install
/plugin add https://github.com/aj-geddes/useful-ai-prompts/tree/main/push-notification-setupCopy and paste this command in Claude Code to install this skill
GitHub 仓库
Related Skills
subagent-driven-development
DevelopmentThis skill executes implementation plans by dispatching a fresh subagent for each independent task, with code review between tasks. It enables fast iteration while maintaining quality gates through this review process. Use it when working on mostly independent tasks within the same session to ensure continuous progress with built-in quality checks.
algorithmic-art
MetaThis Claude Skill creates original algorithmic art using p5.js with seeded randomness and interactive parameters. It generates .md files for algorithmic philosophies, plus .html and .js files for interactive generative art implementations. Use it when developers need to create flow fields, particle systems, or other computational art while avoiding copyright issues.
executing-plans
DesignUse the executing-plans skill when you have a complete implementation plan to execute in controlled batches with review checkpoints. It loads and critically reviews the plan, then executes tasks in small batches (default 3 tasks) while reporting progress between each batch for architect review. This ensures systematic implementation with built-in quality control checkpoints.
cost-optimization
OtherThis Claude Skill helps developers optimize cloud costs through resource rightsizing, tagging strategies, and spending analysis. It provides a framework for reducing cloud expenses and implementing cost governance across AWS, Azure, and GCP. Use it when you need to analyze infrastructure costs, right-size resources, or meet budget constraints.
