jpskill.com
🛠️ 開発・MCP コミュニティ

push-notifications

AndroidのFCMやiOSのAPNsといったプッシュ通知設定、通知チャンネル、ペイロード処理、フォアグラウンド/バックグラウンド動作、リッチ通知などを扱い、効果的なプッシュ通知を実装するSkill。

📜 元の英語説明(参考)

Push notification patterns - FCM setup for Android, APNs for iOS, notification channels, payload handling, foreground/background behavior, and rich notifications.

🇯🇵 日本人クリエイター向け解説

一言でいうと

AndroidのFCMやiOSのAPNsといったプッシュ通知設定、通知チャンネル、ペイロード処理、フォアグラウンド/バックグラウンド動作、リッチ通知などを扱い、効果的なプッシュ通知を実装するSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o push-notifications.zip https://jpskill.com/download/16438.zip && unzip -o push-notifications.zip && rm push-notifications.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/16438.zip -OutFile "$d\push-notifications.zip"; Expand-Archive "$d\push-notifications.zip" -DestinationPath $d -Force; ri "$d\push-notifications.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して push-notifications.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → push-notifications フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

プッシュ通知のパターン

Android / Firebase Cloud Messaging

Firebase の設定

google-services.jsonapp/ ディレクトリに追加し、依存関係を設定します。

// build.gradle.kts (project)
plugins {
    id("com.google.gms.google-services") version "4.4.0" apply false
}

// build.gradle.kts (app)
plugins {
    id("com.google.gms.google-services")
}

dependencies {
    implementation(platform("com.google.firebase:firebase-bom:32.7.0"))
    implementation("com.google.firebase:firebase-messaging-ktx")
}

FirebaseMessagingService の実装

class MyFirebaseMessagingService : FirebaseMessagingService() {

    override fun onNewToken(token: String) {
        // ターゲット設定のために、トークンをサーバーに送信します
        TokenRepository.syncTokenToServer(token)
    }

    override fun onMessageReceived(message: RemoteMessage) {
        // データメッセージは常にここに到達します
        val data = message.data
        val title = data["title"] ?: message.notification?.title ?: return
        val body = data["body"] ?: message.notification?.body ?: ""

        when (data["type"]) {
            "chat" -> showChatNotification(title, body, data)
            "promo" -> showPromoNotification(title, body, data)
            else -> showDefaultNotification(title, body)
        }
    }

    private fun showDefaultNotification(title: String, body: String) {
        val intent = Intent(this, MainActivity::class.java).apply {
            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        }
        val pendingIntent = PendingIntent.getActivity(
            this, 0, intent, PendingIntent.FLAG_IMMUTABLE
        )

        val notification = NotificationCompat.Builder(this, CHANNEL_DEFAULT)
            .setSmallIcon(R.drawable.ic_notification)
            .setContentTitle(title)
            .setContentText(body)
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setContentIntent(pendingIntent)
            .setAutoCancel(true)
            .build()

        val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
        manager.notify(System.currentTimeMillis().toInt(), notification)
    }

    companion object {
        const val CHANNEL_DEFAULT = "default"
        const val CHANNEL_CHAT = "chat_messages"
        const val CHANNEL_PROMO = "promotions"
    }
}

通知チャネル (Android 8+)

object NotificationChannels {
    fun createAll(context: Context) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return

        val manager = context.getSystemService(NotificationManager::class.java)

        val defaultChannel = NotificationChannel(
            "default",
            "General",
            NotificationManager.IMPORTANCE_DEFAULT
        ).apply {
            description = "General notifications"
        }

        val chatChannel = NotificationChannel(
            "chat_messages",
            "Chat Messages",
            NotificationManager.IMPORTANCE_HIGH
        ).apply {
            description = "New chat messages"
            enableVibration(true)
            enableLights(true)
        }

        val promoChannel = NotificationChannel(
            "promotions",
            "Promotions",
            NotificationManager.IMPORTANCE_LOW
        ).apply {
            description = "Promotional offers and deals"
        }

        manager.createNotificationChannels(listOf(defaultChannel, chatChannel, promoChannel))
    }
}

Application.onCreate()NotificationChannels.createAll(this) を呼び出します。

データメッセージと通知メッセージの比較

側面 Notification Message Data Message
フォアグラウンド onMessageReceived onMessageReceived
バックグラウンド システムトレイ (自動) onMessageReceived
カスタマイズ性 限定的 完全な制御
ペイロードキー "notification": {} "data": {}

ベストプラクティス: 表示動作を完全に制御するには、データ専用メッセージを使用します。

トークンの登録とサーバーとの同期

class TokenRepository(private val api: ApiService) {
    suspend fun syncTokenToServer(token: String) {
        val deviceInfo = DeviceInfo(
            token = token,
            platform = "android",
            appVersion = BuildConfig.VERSION_NAME,
            locale = Locale.getDefault().toLanguageTag()
        )
        api.registerPushToken(deviceInfo)
    }

    suspend fun getAndSyncToken() {
        val token = FirebaseMessaging.getInstance().token.await()
        syncTokenToServer(token)
    }
}

通知の許可 (Android 13+)

val requestPermissionLauncher = registerForActivityResult(
    ActivityResultContracts.RequestPermission()
) { isGranted ->
    if (isGranted) {
        // 許可された場合、トークンを同期します
    }
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}

iOS / APNs

プッシュ通知の機能

Xcode で有効にします: Target > Signing & Capabilities > + Push Notifications。 Background Modes > Remote notifications も有効にします。

UNUserNotificationCenter の設定と許可

import UserNotifications

final class NotificationManager: NSObject, UNUserNotificationCenterDelegate {
    static let shared = NotificationManager()

    func requestAuthorization() async -> Bool {
        let center = UNUserNotificationCenter.current()
        center.delegate = self
        do {
            let granted = try await center.requestAuthorization(
                options: [.alert, .badge, .sound, .provisional]
            )
            if granted {
                await MainActor.run {
                    UIApplication.shared.registerForRemoteNotifications()
                }
            }
            return granted
        } catch {
            return false
        }
    }

    // フォアグラウンドでの通知表示
    func userNotificationCenter(
        _ center: UNUserNo
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

Push Notification Patterns

Android / Firebase Cloud Messaging

Firebase Setup

Add google-services.json to the app/ directory and configure dependencies:

// build.gradle.kts (project)
plugins {
    id("com.google.gms.google-services") version "4.4.0" apply false
}

// build.gradle.kts (app)
plugins {
    id("com.google.gms.google-services")
}

dependencies {
    implementation(platform("com.google.firebase:firebase-bom:32.7.0"))
    implementation("com.google.firebase:firebase-messaging-ktx")
}

FirebaseMessagingService Implementation

class MyFirebaseMessagingService : FirebaseMessagingService() {

    override fun onNewToken(token: String) {
        // Send token to your server for targeting
        TokenRepository.syncTokenToServer(token)
    }

    override fun onMessageReceived(message: RemoteMessage) {
        // Data messages always arrive here
        val data = message.data
        val title = data["title"] ?: message.notification?.title ?: return
        val body = data["body"] ?: message.notification?.body ?: ""

        when (data["type"]) {
            "chat" -> showChatNotification(title, body, data)
            "promo" -> showPromoNotification(title, body, data)
            else -> showDefaultNotification(title, body)
        }
    }

    private fun showDefaultNotification(title: String, body: String) {
        val intent = Intent(this, MainActivity::class.java).apply {
            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        }
        val pendingIntent = PendingIntent.getActivity(
            this, 0, intent, PendingIntent.FLAG_IMMUTABLE
        )

        val notification = NotificationCompat.Builder(this, CHANNEL_DEFAULT)
            .setSmallIcon(R.drawable.ic_notification)
            .setContentTitle(title)
            .setContentText(body)
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setContentIntent(pendingIntent)
            .setAutoCancel(true)
            .build()

        val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
        manager.notify(System.currentTimeMillis().toInt(), notification)
    }

    companion object {
        const val CHANNEL_DEFAULT = "default"
        const val CHANNEL_CHAT = "chat_messages"
        const val CHANNEL_PROMO = "promotions"
    }
}

Notification Channels (Android 8+)

object NotificationChannels {
    fun createAll(context: Context) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return

        val manager = context.getSystemService(NotificationManager::class.java)

        val defaultChannel = NotificationChannel(
            "default",
            "General",
            NotificationManager.IMPORTANCE_DEFAULT
        ).apply {
            description = "General notifications"
        }

        val chatChannel = NotificationChannel(
            "chat_messages",
            "Chat Messages",
            NotificationManager.IMPORTANCE_HIGH
        ).apply {
            description = "New chat messages"
            enableVibration(true)
            enableLights(true)
        }

        val promoChannel = NotificationChannel(
            "promotions",
            "Promotions",
            NotificationManager.IMPORTANCE_LOW
        ).apply {
            description = "Promotional offers and deals"
        }

        manager.createNotificationChannels(listOf(defaultChannel, chatChannel, promoChannel))
    }
}

Call NotificationChannels.createAll(this) in Application.onCreate().

Data vs Notification Messages

Aspect Notification Message Data Message
Foreground onMessageReceived onMessageReceived
Background System tray (auto) onMessageReceived
Customizable Limited Full control
Payload key "notification": {} "data": {}

Best practice: Use data-only messages for full control over display behavior.

Token Registration and Server Sync

class TokenRepository(private val api: ApiService) {
    suspend fun syncTokenToServer(token: String) {
        val deviceInfo = DeviceInfo(
            token = token,
            platform = "android",
            appVersion = BuildConfig.VERSION_NAME,
            locale = Locale.getDefault().toLanguageTag()
        )
        api.registerPushToken(deviceInfo)
    }

    suspend fun getAndSyncToken() {
        val token = FirebaseMessaging.getInstance().token.await()
        syncTokenToServer(token)
    }
}

Notification Permission (Android 13+)

val requestPermissionLauncher = registerForActivityResult(
    ActivityResultContracts.RequestPermission()
) { isGranted ->
    if (isGranted) {
        // Permission granted, sync token
    }
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}

iOS / APNs

Push Notification Capability

Enable in Xcode: Target > Signing & Capabilities > + Push Notifications. Also enable Background Modes > Remote notifications.

UNUserNotificationCenter Setup and Permissions

import UserNotifications

final class NotificationManager: NSObject, UNUserNotificationCenterDelegate {
    static let shared = NotificationManager()

    func requestAuthorization() async -> Bool {
        let center = UNUserNotificationCenter.current()
        center.delegate = self
        do {
            let granted = try await center.requestAuthorization(
                options: [.alert, .badge, .sound, .provisional]
            )
            if granted {
                await MainActor.run {
                    UIApplication.shared.registerForRemoteNotifications()
                }
            }
            return granted
        } catch {
            return false
        }
    }

    // Foreground notification display
    func userNotificationCenter(
        _ center: UNUserNotificationCenter,
        willPresent notification: UNNotification
    ) async -> UNNotificationPresentationOptions {
        return [.banner, .badge, .sound]
    }

    // Notification tap handling
    func userNotificationCenter(
        _ center: UNUserNotificationCenter,
        didReceive response: UNNotificationResponse
    ) async {
        let userInfo = response.notification.request.content.userInfo
        handleNotificationPayload(userInfo)
    }

    private func handleNotificationPayload(_ userInfo: [AnyHashable: Any]) {
        guard let type = userInfo["type"] as? String else { return }
        switch type {
        case "chat":
            let chatId = userInfo["chat_id"] as? String ?? ""
            DeepLinkRouter.shared.destination = .chat(id: chatId)
        case "product":
            let productId = userInfo["product_id"] as? String ?? ""
            DeepLinkRouter.shared.destination = .product(id: productId)
        default:
            break
        }
    }
}

AppDelegate Registration

func application(
    _ application: UIApplication,
    didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
    let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
    Task { await TokenService.shared.syncToken(token) }
}

func application(
    _ application: UIApplication,
    didFailToRegisterForRemoteNotificationsWithError error: Error
) {
    print("Push registration failed: \(error.localizedDescription)")
}

Notification Service Extension (Rich Notifications)

Create a new target: File > New > Target > Notification Service Extension.

class NotificationService: UNNotificationServiceExtension {
    var contentHandler: ((UNNotificationContent) -> Void)?
    var bestAttemptContent: UNMutableNotificationContent?

    override func didReceive(
        _ request: UNNotificationRequest,
        withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
    ) {
        self.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

        guard let content = bestAttemptContent,
              let imageURLString = content.userInfo["image_url"] as? String,
              let imageURL = URL(string: imageURLString) else {
            contentHandler(request.content)
            return
        }

        downloadImage(from: imageURL) { attachment in
            if let attachment = attachment {
                content.attachments = [attachment]
            }
            contentHandler(content)
        }
    }

    override func serviceExtensionTimeWillExpire() {
        if let content = bestAttemptContent {
            contentHandler?(content)
        }
    }
}

Provisional Authorization (iOS 12+)

Provisional auth delivers notifications quietly to Notification Center without prompting:

let granted = try await center.requestAuthorization(options: [.alert, .sound, .provisional])

Users can then promote to prominent delivery or turn off from the notification itself.


Testing

# Android - send test via Firebase CLI
firebase messaging:send --project=my-project --json '{
  "message": {
    "token": "DEVICE_TOKEN",
    "data": { "type": "chat", "title": "Test", "body": "Hello" }
  }
}'

# iOS - local notification for testing UI
let content = UNMutableNotificationContent()
content.title = "Test Notification"
content.body = "This is a local test."
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
let request = UNNotificationRequest(identifier: "test", content: content, trigger: trigger)
try await UNUserNotificationCenter.current().add(request)