Skip to content

iOS and Android apps

Hive Push sends native notifications to your own iOS and Android apps, alongside browsers and the in-app inbox, from the same API call. Apple delivers to iPhones and iPads through the Apple Push Notification service (APNs), and Firebase Cloud Messaging (FCM) delivers to Android phones. You add your own Apple key and Firebase service account once per app, so notifications come from your app, under your name.

You need a Hive Push app first: see the Quickstart steps 1 and 5 (the app and user tokens). Native apps use the same public key as your website and don’t need an allowed origin.

Hive Push signs in to Apple with an APNs key (.p8). Certificates (.p12) are not needed, and one key works for all apps in your Apple team.

  1. In your Apple Developer account, open Certificates, IDs & Profiles > Keys and click +. Name the key, tick Apple Push Notifications service (APNs), continue and register it.

  2. Download the AuthKey_XXXXXXXXXX.p8 file. Apple lets you download it only once, so keep it somewhere safe.

  3. Note the key’s Key ID (10 characters, on the key’s page) and your Team ID (10 characters, under Membership details).

  4. In Xcode, select your app target, open Signing & Capabilities and add the Push Notifications capability. If you will send data-only pushes, also add Background Modes and tick Remote notifications.

  5. In the DockHive console, open your push app, go to iOS (APNs) and fill in the Key ID, Team ID and your app’s bundle ID (for example com.acme.shop), upload the .p8 file and pick the default environment:

    • production for TestFlight and App Store builds,
    • sandbox for development builds installed from Xcode.
  6. Click Verify. Hive Push checks the key with Apple straight away and shows Apple’s reason if the key is refused. The card then shows Configured with the key ID, team ID, bundle ID and environment.

To change the bundle ID or default environment later, edit them without uploading the key again. Remove deletes the key: iOS devices stay registered, but sends to them fail until a key is added again.

  1. In the Firebase console, open your project (or create one) and add your Android app with its package name (Project settings > General > Your apps).

  2. Download google-services.json for the Android app and put it in your app module (app/google-services.json). Your app uses it to get its push token.

  3. Check that the Firebase Cloud Messaging API (V1) is enabled under Project settings > Cloud Messaging.

  4. Open Project settings > Service accounts and click Generate new private key. This downloads a JSON key file.

  5. In the DockHive console, open your push app, go to Android (FCM) and upload the JSON file. Hive Push checks it with Google before saving and shows Google’s own message if it’s refused. The card then shows Configured with the Firebase project ID and the service account’s email.

The key file is stored encrypted and is never shown again, in the console or the API. Upload a new one to replace it.

Each install of your app registers its push token with Hive Push and becomes a device (dev_...) with platform ios or android. Register on every app start: tokens can change, and registering the same token again just refreshes it. Until the app has Apple or Firebase credentials, registration answers 409 platform_not_configured.

Add the Swift package https://github.com/DockHive/hive-push-sdks in Xcode (File > Add Package Dependencies) and link the HivePush library to your app. It supports iOS 14 and later and has no other dependencies.

import HivePush
import UIKit
class AppDelegate: NSObject, UIApplicationDelegate {
let pushDelegate = HivePushNotificationDelegate { url, _ in /* open url */ }
func application(_ app: UIApplication, didFinishLaunchingWithOptions o: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
HivePush.configure(key: "pk_your_public_key", region: "global-1")
UNUserNotificationCenter.current().delegate = pushDelegate
Task { try? await HivePush.requestAuthorization() } // asks permission, then gets a token
return true
}
func application(_ app: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken token: Data) {
Task { try? await HivePush.shared.register(deviceToken: token) }
}
}

register(deviceToken:environment:) takes .auto (the default: sandbox in Debug builds, production otherwise), .sandbox or .production. The SDK keeps the device id across launches, and replaces the old device when iOS hands out a new token.

Flutter and React Native: the SDKs are in github.com/DockHive/hive-push-sdks (flutter/ and react-native/); package-registry releases are coming soon.

To send to your users rather than to single devices, attach the signed-in user to the install with a user token from your server. Never put the secret key in an app. The user can be set before or after the device registers.

try await HivePush.shared.setUser(id: "user-42", token: tokenFromYourServer)
try await HivePush.shared.setTags(["plan": "pro"])
// on sign-out
try await HivePush.shared.clearUser()

The SDKs also give your app the user’s inbox: inbox.list, inbox.markRead and a live stream (inbox.onMessage in Swift, inbox.stream() as a Flow in Kotlin) that reconnects on its own.

Use the Send API as usual: a notification goes to every targeted device, browsers and app installs alike. Three optional fields narrow and tune it for apps:

Field Notes
platforms Send only to devices on these platforms: any of "web", "ios", "android". Default: all
ios Options used for iOS devices only (below)
android Options used for Android devices only (below)

iOS options ("ios": {...}):

Field Notes
sound "default" or the name of a sound file in your app bundle
badge The number to show on the app icon (0 removes it)
thread_id Groups notifications in Notification Center
category Your notification category (action buttons defined in the app)
interruption_level active (default), passive or time-sensitive
mutable_content true lets your notification service extension change the notification, for example to attach the image
content_available true wakes your app in the background
subtitle A line under the title, up to 120 characters

Android options ("android": {...}):

Field Notes
channel_id The notification channel your app created (Android 8 and later)
priority high or normal. Default: high for urgency high and normal, otherwise normal
color Accent color, #RRGGBB
sound "default" or a sound resource in your app
tag A newer notification with the same tag replaces the one on screen
click_action The intent action opened on tap

To one user’s phones, with platform options:

Terminal window
curl https://push-global-1.dockhive.io/v1/apps/app_0123456789abcdef/notifications \
-H "Authorization: Bearer $HIVE_PUSH_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": {"users": ["user-42"]},
"platforms": ["ios", "android"],
"title": "New message from Ada",
"body": "Are we still on for 6?",
"data": {"chat_id": "c_19"},
"ios": {"sound": "default", "badge": 3, "thread_id": "chat-c_19"},
"android": {"channel_id": "messages", "priority": "high"}
}'

ttl and topic apply on every platform, and urgency very-low and low also lower the iOS priority (delivered when the device is awake). The notification’s status shows counts for each platform under counts.by_platform (see Status and counts).

Size. For iOS, the notification as Apple receives it must fit 4096 bytes; for Android, the notification and its data must fit 4096 bytes. A larger send is refused with 413 payload_too_large, which names the platform; leave that platform out with platforms or shorten the content.

Reserved data keys. When the app has Android set up, data can’t use the keys Firebase reserves: from, message_type, notification, collapse_key, or keys starting with google or gcm. Such sends are refused with 400 invalid_data.

iOS, in the notification’s userInfo:

{"aps": {"alert": {"title": "Your order shipped", "subtitle": "Shop", "body": "It arrives on Thursday."},
"sound": "default", "badge": 1, "thread-id": "orders"},
"hp": {"id": "ntf_5b0c9d2e7f1a3c4b", "url": "https://shop.example/orders/77",
"data": {"order_id": 77}, "image": "https://shop.example/box.png"}}

hp.image is there for a notification service extension that downloads and attaches it; send with "ios": {"mutable_content": true}. In Swift, HivePush.url(from:) and HivePush.data(from:) read hp.url and hp.data.

Android, in the RemoteMessage: the title, body and image in its notification part (shown by the system while your app is in the background), and in its data: hp_id (the notification id), hp_url, and each key of your data object as a string (strings as they are, other values as JSON). When the user taps the notification, the same data arrives in the extras of the intent that opens your app.

Leave out title (and body) to deliver a notification to your app without showing anything, so the app handles data itself, for example to sync in the background. A data-only push needs platforms without "web"; for iOS it also needs "ios": {"content_available": true}, which makes it a background push that iOS may delay or limit. Data-only pushes make no inbox items.

{"to": {"users": ["user-42"]}, "platforms": ["ios", "android"],
"ios": {"content_available": true}, "data": {"sync": "orders"}}

On iOS, your app gets it in application(_:didReceiveRemoteNotification:fetchCompletionHandler:) (with the Remote notifications background mode on). On Android, it arrives in onMessageReceived of your messaging service.

When the user taps a notification, report it so it counts in your clicked stats (clicked counts once per device).

HivePushNotificationDelegate (in the setup above) reports opens for you and calls your closure with the notification’s URL. With your own UNUserNotificationCenterDelegate, call:

try await HivePush.shared.reportOpened(userInfo: response.notification.request.content.userInfo)

Registration answers 409 platform_not_configured. The app has no APNs key (iOS) or Firebase service account (Android) yet. Add it in the console under iOS (APNs) or Android (FCM).

Sends fail and the iOS or Android card shows a last error. Apple or Google refused your credentials while sending, for example because the key was revoked in your Apple account or the service account was deleted. Those deliveries fail, but devices stay registered. Upload working credentials again; the error clears with the next successful send.

iOS devices are disabled right after a send. Usually the token is from the other APNs environment. Builds installed from Xcode get sandbox tokens; TestFlight and App Store builds get production tokens. Register with the right environment (the Swift SDK’s .auto picks sandbox for Debug builds and production otherwise; pass it explicitly if you ship Debug-configured builds through TestFlight), or set the app’s default environment to match. Registering the token again re-enables the device.

iOS devices are disabled with a token for another app. The bundle ID in the console doesn’t match the app that registered the token. Fix the bundle ID on the iOS (APNs) card and register again.

Android devices are disabled after a send. The token is no longer valid (the app was uninstalled or its data cleared) or it belongs to another Firebase project. Check that google-services.json in the app and the service account in the console come from the same Firebase project.

Nothing shows on Android 13 or later. The app needs the POST_NOTIFICATIONS permission, granted by the user at runtime.

401 invalid_user_token from setUser or the inbox. The user token is expired or was signed with an old secret key. Get a fresh one from your server and call setUser again.

See Limits and regions for every error code.