Skip to main content
Complete guide to implementing push notifications with Zixflow using Apple Push Notifications (APNs) or Firebase Cloud Messaging (FCM).
💡 Track your campaigns: With auto-tracking enabled, the SDK tracks delivery and opens. For metrics details, see Push Notification Tracking.

Prerequisites

  1. Add push credentials (APNs or FCM) in the Zixflow dashboard
  2. Enable Push Notifications capability in Xcode
  3. Install the push module:
    • ZixflowMessagingPushAPN for APNs
    • ZixflowMessagingPushFCM for FCM
Test on a physical device — the simulator cannot receive remote push.

Apple Push Notifications (APNs)

Step 1: Enable Push Notifications in Xcode

  1. Select your project in Xcode
  2. Go to TargetSigning & Capabilities
  3. Click + CapabilityPush Notifications
  4. Click + CapabilityBackground Modes → enable Remote notifications

Step 2: Initialize core SDK and MessagingPushAPN

import UIKit
import ZixflowDataPipelines
import ZixflowMessagingPushAPN

@main
class AppDelegateWithZixflowIntegration: ZixflowAppDelegateWrapper<AppDelegate> {}

class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        let config = SDKConfigBuilder(apiKey: "YOUR_API_KEY")
            .autoTrackDeviceAttributes(true)
            .trackApplicationLifecycleEvents(true)
            .logLevel(.debug)
            .deepLinkCallback { url in
                // Handle Zixflow push deep links (ZIXFLOW.push.link)
                print("Deep link: \(url)")
                return true
            }
            .build()

        Zixflow.initialize(withConfig: config)

        MessagingPushAPN.initialize(
            withConfig: MessagingPushConfigBuilder()
                .autoFetchDeviceToken(true)
                .autoTrackPushEvents(true)
                .showPushAppInForeground(true)
                .build()
        )

        return true
    }
}
ZixflowAppDelegateWrapper is recommended: it registers device tokens, handles clicks, and tracks opens for Zixflow pushes.
MessagingPushConfigBuilder options
OptionTypeDefaultDescription
autoFetchDeviceTokenBooltrueAutomatically register the device token
autoTrackPushEventsBooltrueTrack delivered and opened metrics
showPushAppInForegroundBooltrueShow pushes while the app is active
appGroupIdString?nilApp Group for NSE delivery tracking (auto-infers group.{bundleId}.zixflow if omitted)
logLevelZixflowLogLevel.errorLogging for the push module

Step 3: Identify users

Device tokens are associated with a profile when you call identify():
Zixflow.shared.identify(
    userId: "user@example.com",
    traits: ["email": "user@example.com"]
)

Step 4: Rich push with Notification Service Extension

  1. FileNewTargetNotification Service Extension
  2. Add ZixflowMessagingPushAPN to the extension target
  3. Share an App Group between the app and extension (group.{bundleId}.zixflow)
CocoaPods extension target:
target 'NotificationServiceExtension' do
  use_frameworks!
  pod 'ZixflowMessagingPushAPN', '~> 1.1'
end
NotificationService.swift:
import UserNotifications
import ZixflowMessagingPushAPN

class NotificationService: UNNotificationServiceExtension {
    override func didReceive(
        _ request: UNNotificationRequest,
        withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
    ) {
        MessagingPushAPN.initializeForExtension(
            withConfig: MessagingPushConfigBuilder(apiKey: "YOUR_API_KEY")
                .logLevel(.debug)
                .appGroupId("group.com.yourcompany.app.zixflow")
                .build()
        )

        MessagingPush.shared.didReceive(request, withContentHandler: contentHandler)
    }

    override func serviceExtensionTimeWillExpire() {
        MessagingPush.shared.serviceExtensionTimeWillExpire()
    }
}
Set the same appGroupId when initializing push in the main app:
MessagingPushAPN.initialize(
    withConfig: MessagingPushConfigBuilder()
        .appGroupId("group.com.yourcompany.app.zixflow")
        .build()
)

Step 5: Manual device token registration (optional)

If you disable autoFetchDeviceToken, forward the APNs token via MessagingPush.shared:
func application(
    _ application: UIApplication,
    didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
    MessagingPush.shared.registerDeviceToken(apnDeviceToken: deviceToken)
}

func application(
    _ application: UIApplication,
    didFailToRegisterForRemoteNotificationsWithError error: Error
) {
    MessagingPush.shared.deleteDeviceToken()
}

Firebase Cloud Messaging (FCM)

Step 1: Firebase project

  1. Create/select a project in the Firebase console
  2. Add your iOS app and download GoogleService-Info.plist into the Xcode project

Step 2: Install dependencies

platform :ios, '13.0'
use_frameworks!

target 'YourApp' do
  pod 'ZixflowDataPipelines', '~> 1.1'
  pod 'ZixflowMessagingPushFCM', '~> 1.1'
  pod 'FirebaseMessaging', '~> 10.0'
end

Step 3: Initialize Firebase, Zixflow, and push

Configure Firebase first, then initialize Zixflow and the push module. Forward FCM registration tokens to Zixflow:
import UIKit
import FirebaseCore
import FirebaseMessaging
import ZixflowDataPipelines
import ZixflowMessagingPushFCM

@main
class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate {
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        FirebaseApp.configure()

        let config = SDKConfigBuilder(apiKey: "YOUR_API_KEY")
            .logLevel(.debug)
            .deepLinkCallback { url in
                print("Deep link: \(url)")
                return true
            }
            .build()
        Zixflow.initialize(withConfig: config)

        MessagingPush.initialize(
            withConfig: MessagingPushConfigBuilder()
                .autoFetchDeviceToken(true)
                .autoTrackPushEvents(true)
                .showPushAppInForeground(true)
                .build()
        )

        Messaging.messaging().delegate = self
        return true
    }

    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
        MessagingPush.shared.registerDeviceToken(fcmToken: fcmToken)
    }
}
Advanced: MessagingPushFCM.internalSetup(withConfig:firebaseService:) accepts a FirebaseService adapter for automatic APNs↔FCM token bridging. Most apps can use MessagingPush.initialize plus registerDeviceToken(fcmToken:) as shown above.

Step 4: FCM Notification Service Extension

import UserNotifications
import ZixflowMessagingPushFCM

class NotificationService: UNNotificationServiceExtension {
    override func didReceive(
        _ request: UNNotificationRequest,
        withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
    ) {
        MessagingPushFCM.initializeForExtension(
            withConfig: MessagingPushConfigBuilder(apiKey: "YOUR_API_KEY")
                .logLevel(.debug)
                .build()
        )

        MessagingPush.shared.didReceive(request, withContentHandler: contentHandler)
    }

    override func serviceExtensionTimeWillExpire() {
        MessagingPush.shared.serviceExtensionTimeWillExpire()
    }
}

Zixflow push payloads may include a deep link at ZIXFLOW.push.link. Prefer SDKConfigBuilder.deepLinkCallback so links work consistently (including with FCM swizzling):
.deepLinkCallback { url in
    // Navigate in your app
    return true  // true = handled; false may open in browser
}
The SDK also exposes the parsed payload when you handle notification responses manually (overload without a completion handler):
if let payload = MessagingPush.shared.userNotificationCenter(center, didReceive: response) {
    if let link = payload.deepLink {
        // Navigate using link (URL)
    }
}

Manual metrics (optional)

If you are not using the AppDelegate wrapper / auto-tracking, call:
MessagingPush.shared.trackMetric(
    deliveryID: deliveryId,
    event: .opened,  // .delivered, .opened, .converted
    deviceToken: deliveryToken
)
Read delivery fields from the payload keys ZIXFLOW-Delivery-ID and ZIXFLOW-Delivery-Token. See Push Notification Tracking.

Delete device token

MessagingPush.shared.deleteDeviceToken()
// or
Zixflow.shared.deleteDeviceToken()

Troubleshooting

IssueCheck
No pushesPhysical device; Push capability; dashboard credentials; user identify() called
No rich media / delivery metricsNSE installed; App Groups match; mutable-content: 1 in payload
Deep links not openingSet deepLinkCallback; confirm ZIXFLOW.push.link in payload
Enable .logLevel(.debug) while integrating.