Skip to content
Skip
3k

Push Notifications

A Skip framework for cross-platform push notifications on iOS and Android.

  • iOS: Uses the native UserNotifications framework and APNs.
  • Android: Communicates directly with Google Mobile Services (GMS) via the C2DM registration intent protocol to obtain FCM tokens, requiring only that GMS (Google Play Services) is installed on the device.

Add the dependency to your Package.swift file:

let package = Package(
name: "my-package",
dependencies: [
.package(url: "https://source.skip.dev/skip-notify.git", "0.0.0"..<"2.0.0"),
],
targets: [
.target(name: "MyTarget", dependencies: [
.product(name: "SkipNotify", package: "skip-notify")
])
]
)
import SkipNotify
do {
// On Android, pass your Firebase project's numeric sender ID.
// On iOS, the parameter is ignored (APNs uses bundle ID + entitlements).
let token = try await SkipNotify.shared.fetchNotificationToken(
firebaseProjectNumber: "123456789"
)
print("Push token: \(token)")
// Send this token to your backend server to target this device
} catch {
print("Failed to get push token: \(error)")
}

Finding your Firebase project number: Open the Firebase console, select your project, go to Project settings > Cloud Messaging, and copy the Sender ID (a numeric string like "123456789012").

On iOS, the returned token is the APNs device token as a hex string. On Android, the returned token is an FCM registration token — the same token that FirebaseMessaging.getInstance().token would produce.

Before requesting a token on Android, you can check whether Google Mobile Services is available:

if SkipNotify.shared.isGMSAvailable {
let token = try await SkipNotify.shared.fetchNotificationToken(
firebaseProjectNumber: "123456789"
)
} else {
print("GMS not available: \(SkipNotify.shared.gmsStatusDescription)")
}
PropertyiOSAndroid (with GMS)Android (without GMS)
isGMSAvailablefalsetruefalse
gmsStatusDescription"GMS not applicable (iOS)""GMS available""GMS not available"

Standard APNs registration flow:

  1. Calls UIApplication.shared.registerForRemoteNotifications()
  2. Receives the device token via the didRegisterForRemoteNotificationsWithDeviceToken app delegate callback (bridged through NotificationCenter)
  3. Returns the token as a hex-encoded string

Your app delegate must forward the token and error callbacks to NotificationCenter. Skip projects created with skip init or skip create include this automatically. For custom setups, add the forwarding calls documented in the source.

SkipNotify communicates directly with Google Mobile Services using the C2DM registration intent protocol — the same underlying mechanism that the firebase-messaging SDK uses internally.

The registration flow:

  1. Availability check: Verifies com.google.android.gms is installed and enabled on the device
  2. Register a BroadcastReceiver: Listens for the com.google.android.c2dm.intent.REGISTRATION response broadcast
  3. Send the registration intent: Sends com.google.android.c2dm.intent.REGISTER to GMS with:
    • app: A PendingIntent that GMS uses to verify the calling app’s package identity
    • sender: The Firebase project number (sender ID)
    • subtype: Same as sender (signals standard app-level registration)
    • gmsVersion: The installed GMS version code
    • scope: "GCM" (the registration scope)
  4. Receive the token: GMS responds asynchronously via the REGISTRATION broadcast with a registration_id extra containing the FCM token, or an error extra if registration failed

Once you have the FCM token from fetchNotificationToken, send messages from your server using the FCM HTTP v1 API:

POST https://fcm.googleapis.com/v1/projects/YOUR_PROJECT_ID/messages:send
Authorization: Bearer <OAuth2-token>
Content-Type: application/json
{
"message": {
"token": "<the-token-from-fetchNotificationToken>",
"notification": {
"title": "Hello",
"body": "World"
}
}
}

For iOS, use the APNs HTTP/2 API with the hex device token returned by fetchNotificationToken.

utilize a tool like gorush to simplify the configuration and authentication.

Follow the steps described in the Registering your app with APNs documentation:

No additional Gradle dependencies or google-services.json file is required. GMS (Google Play Services) must be installed on the device.

To receive push messages (not just register for tokens), your app needs a BroadcastReceiver for the com.google.android.c2dm.intent.RECEIVE action in AndroidManifest.xml:

<receiver android:name=".PushMessageReceiver"
android:permission="com.google.android.c2dm.permission.SEND"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</receiver>

The android:permission attribute is critical — it restricts delivery to broadcasts sent by GMS (which holds the com.google.android.c2dm.permission.SEND permission), preventing other apps from injecting fake push messages.

Message payloads arrive as intent extras:

Extra keyDescription
gcm.notification.titleNotification title
gcm.notification.bodyNotification body
google.message_idUnique message ID
collapse_keyCollapse key (if set)
(your custom keys)Data payload fields

GMS may invalidate registration tokens after Play Services updates or device resets. Re-register at app startup and compare the returned token against the one stored on your server. If it differs, update the server.

You can also listen for the com.google.android.c2dm.intent.REGISTRATION broadcast in your manifest to detect token refreshes:

<receiver android:name=".TokenRefreshReceiver"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
</intent-filter>
</receiver>

The new token arrives in the registration_id extra of the broadcast intent.