iOS Quickstart
This guide walks you through integrating PushEngage push notifications into a native iOS application. Estimated time: 15 minutes.
After completing this guide, your app will receive push notifications with rich media support. See the iOS SDK Reference for the full API.
The SDK is now split into two modules so notification extension targets no longer link app-only code (this fixes a CocoaPods regression that broke push delivery on production builds).
PushEngage— link to your app target only. App-target code is unchanged.PushEngageExtension— link to your Notification Service / Content Extension target(s).
For existing 0.1.0 integrations, three things change in each extension target:
Dependency
- CocoaPods:
pod 'PushEngage'→pod 'PushEngageExtension' - SPM: link the
PushEngageExtensionproduct instead ofPushEngage
- CocoaPods:
Import:
import PushEngage→import PushEngageExtension(Objective-C:@import PushEngage;→@import PushEngageExtension;)Call sites: replace the
PushEngageclass prefix withPushEngageExtension— method names are unchanged.// Before
PushEngage.didReceiveNotificationExtensionRequest(request, bestContentHandler: bestContent)
PushEngage.serviceExtensionTimeWillExpire(request, content: bestAttemptContent)
PushEngage.getCustomUIPayLoad(for: notification.request)
// After
PushEngageExtension.didReceiveNotificationExtensionRequest(request, bestContentHandler: bestContent)
PushEngageExtension.serviceExtensionTimeWillExpire(request, content: bestAttemptContent)
PushEngageExtension.getCustomUIPayLoad(for: notification.request)
You can also remove the APPLICATION_EXTENSION_API_ONLY = 'NO' Podfile post_install build-setting override from earlier versions — it is no longer needed.
Objective-C with modules disabled: if an app target builds with CLANG_ENABLE_MODULES = NO (or uses Objective-C++), also add #import <PushEngageExtension/PushEngageExtension-Swift.h> where you use the notification model types (PENotification, PENotificationOpenResult, SubscriberDetailsData). Targets with modules enabled (the default) need nothing extra.
Prerequisites
- Xcode installed
- Apple Developer account with a valid App ID configured on the Apple Developer Portal
- APNs certificate (.p12) (create one)
- PushEngage account and App ID (see Get Your App ID)
Step 1 — Install the SDK
Choose either Swift Package Manager or CocoaPods. Use only one method.
- Swift Package Manager
- CocoaPods
- In Xcode, go to File → Add Package Dependencies.
- Paste
https://github.com/awesomemotive/pushengage-ios-sdkinto the search bar. - Click Add Package.
- Under Add to Target, select your main app target.
- Click Add Package.
If CocoaPods is not installed, run:
sudo gem install cocoapods
Initialize a Podfile in your project root:
pod init
Add the PushEngage dependency to your Podfile:
target 'YourProjectName' do
use_frameworks!
pod 'PushEngage'
end
Install the pods:
pod repo update
pod install
Open the generated .xcworkspace file in Xcode going forward (not .xcodeproj).
Step 2 — Configure Xcode Capabilities
Enable Push Notifications
- In Xcode, select the root project in the Project Navigator and choose your main app target.
- Go to Signing & Capabilities.
- Click + Capability and add Push Notifications.
If Push Notifications is not visible in Xcode: go to your Apple Developer account → Certificates, Identifiers & Profiles → select your App ID → enable Push Notifications → return to Xcode and try again.
Enable Background Modes
- Click + Capability and add Background Modes.
- Check both Remote notifications and Background fetch.
Step 3 — Connect to PushEngage Dashboard
- Log in to your PushEngage Dashboard.
- Navigate to Site Settings → Installation → iOS SDK.
Under "1. Configure your Apple iOS APNs settings":
- Enter your App Push Id (your iOS Bundle Identifier, e.g.
com.example.MyApp). - Upload your APNs Certificate (.p12).
- Enter the Private Key Password you set when exporting the .p12.
- Click Update.
Under "2. Install the SDK":
- Click Copy next to the App ID — you'll pass it to
setAppIDin the next step.
Step 4 — Initialize the SDK
Add PushEngage initialization to your AppDelegate:
- Swift
- Objective-C
- SwiftUI
import UIKit
import PushEngage
class AppDelegate: UIResponder, UIApplicationDelegate {
override init() {
super.init()
PushEngage.swizzleInjection(isEnabled: true)
}
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
PushEngage.setAppID(id: "YOUR_APP_ID")
PushEngage.setInitialInfo(for: application, with: launchOptions)
PushEngage.enableLogging = true
// Prompt for notification permission. On grant, the SDK registers with
// APNs and subscribes the device automatically. Without this call the
// system prompt never appears and no subscriber is created.
PushEngage.requestNotificationPermission { granted, error in
print("Notification permission granted: \(granted)")
}
return true
}
}
#import "AppDelegate.h"
@import PushEngage;
@implementation AppDelegate
- (instancetype)init {
self = [super init];
if (self) {
[PushEngage swizzleInjectionWithIsEnabled:YES];
}
return self;
}
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[PushEngage setAppIDWithId:@"YOUR_APP_ID"];
[PushEngage setInitialInfoFor:application with:launchOptions];
[PushEngage setEnableLogging:YES];
// Prompt for notification permission. On grant, the SDK registers with
// APNs and subscribes the device automatically. Without this call the
// system prompt never appears and no subscriber is created.
[PushEngage requestNotificationPermissionWithCompletion:^(BOOL granted, NSError * _Nullable error) {
NSLog(@"Notification permission granted: %d", granted);
}];
return YES;
}
@end
import SwiftUI
import PushEngage
@main
struct YourApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
override init() {
super.init()
PushEngage.swizzleInjection(isEnabled: true)
}
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
PushEngage.setAppID(id: "YOUR_APP_ID")
PushEngage.setInitialInfo(for: application, with: launchOptions)
PushEngage.enableLogging = true
// Prompt for notification permission. On grant, the SDK registers with
// APNs and subscribes the device automatically. Without this call the
// system prompt never appears and no subscriber is created.
PushEngage.requestNotificationPermission { granted, error in
print("Notification permission granted: \(granted)")
}
return true
}
}
Replace YOUR_APP_ID with the App ID copied in Step 3.
Remove or set PushEngage.enableLogging = false before releasing to production. Leaving it enabled exposes internal SDK data in production app logs.
Step 5 — Send a Test Notification
- Build and run your app on a physical iOS device — push notifications do not work in the simulator.
- Accept the notification permission prompt when it appears.
- In the PushEngage Dashboard, go to Campaign → Push Broadcasts → Create New Push Broadcast.
- Send a test notification to yourself.
You should receive the notification within a few seconds. Your integration is complete. To unlock rich notifications and images, continue with the steps below.
Enable Rich Notifications
The following steps enable rich notifications (images, action buttons) and app badge sync. They require adding iOS extension targets to your Xcode project.
Step 6 — Add Notification Service Extension
The Notification Service Extension enables rich notifications (images, action buttons). It is required for full PushEngage functionality.
Create the Extension
- In Xcode, go to File → New → Target.
- Select Notification Service Extension and click Next.
- Name it
PushEngageNotificationServiceExtensionand click Finish. - When prompted to activate the scheme, click Cancel — this keeps Xcode's debugging focus on your main app.
- Select the new extension target in the Project Navigator and set its Deployment Target to iOS 12 or above.
Add SDK Dependency to the Extension
Notification extensions link the companion PushEngageExtension module, not the main PushEngage module. PushEngageExtension is extension-safe and ships the three notification-extension APIs (didReceiveNotificationExtensionRequest, serviceExtensionTimeWillExpire, getCustomUIPayLoad). Linking the wrong module will break push delivery on production builds.
- Swift Package Manager
- CocoaPods
- Select your project in the navigator.
- Select the
PushEngageNotificationServiceExtensiontarget. - Go to General → Frameworks, Libraries, and Embedded Content.
- Click +, find
PushEngageExtension, and add it.
Add a sibling target for the extension at the top level of your Podfile:
target 'YourProjectName' do
use_frameworks!
pod 'PushEngage'
end
target 'PushEngageNotificationServiceExtension' do
use_frameworks!
pod 'PushEngageExtension'
end
Run:
pod repo update
pod install
Implement the Extension
Replace the auto-generated NotificationService.swift with:
import UserNotifications
import PushEngageExtension
@available(iOSApplicationExtension 12.0, *)
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
var request: UNNotificationRequest?
override func didReceive(
_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
) {
self.request = request
self.contentHandler = contentHandler
self.bestAttemptContent = request.content.mutableCopy() as? UNMutableNotificationContent
if let bestContent = bestAttemptContent {
PushEngageExtension.didReceiveNotificationExtensionRequest(request, bestContentHandler: bestContent)
contentHandler(bestContent)
}
}
override func serviceExtensionTimeWillExpire() {
if let contentHandler = contentHandler,
let request = request,
let bestAttemptContent = bestAttemptContent {
guard let content = PushEngageExtension.serviceExtensionTimeWillExpire(
request, content: bestAttemptContent
) else {
contentHandler(bestAttemptContent)
return
}
contentHandler(content)
}
}
}
Step 7 — Add Notification Content Extension (Optional)
The Notification Content Extension renders the custom expanded UI for PushEngage notifications sent with a matching category. Without it, notifications still deliver normally, but any custom expanded layout falls back to iOS's default view.
Create the Extension
- In Xcode, go to File → New → Target.
- Select Notification Content Extension and click Next.
- Name it
PushEngageNotificationContentExtensionand click Finish. - When prompted to activate the scheme, click Cancel.
- Set the Deployment Target to iOS 12 or above.
- Open the extension's
Info.plist. Under NSExtension → NSExtensionAttributes → UNNotificationExtensionCategory, set the array to contain the category identifier(s) you'll send with custom-UI notifications (must match thecategoryIdentifiercheck in your extension code).
Add SDK Dependency
- Swift Package Manager
- CocoaPods
- Select your project in the navigator.
- Select the
PushEngageNotificationContentExtensiontarget. - Go to General → Frameworks, Libraries, and Embedded Content.
- Click +, find
PushEngageExtension, and add it.
Add to your Podfile:
target 'PushEngageNotificationContentExtension' do
use_frameworks!
pod 'PushEngageExtension'
end
Run pod install.
Implement the Extension
import UIKit
import UserNotifications
import UserNotificationsUI
import PushEngageExtension
@available(iOSApplicationExtension 12.0, *)
class NotificationViewController: UIViewController, UNNotificationContentExtension {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .white
}
func didReceive(_ notification: UNNotification) {
if notification.request.content.categoryIdentifier == "your_category_identifier" {
let payload = PushEngageExtension.getCustomUIPayLoad(for: notification.request)
// Pass payload to your custom SwiftUI or UIKit view
}
}
}
Step 8 — Add App Groups
App Groups enable communication between the main app and notification extensions. This step is required.
- Select your main app target in Xcode → Signing & Capabilities → + Capability → App Groups.
- Click + to add a new App Group with a unique name, e.g.,
group.com.yourcompany.yourapp. - In your main app target's
Info.plist, add:- Key:
PushEngage_App_Group_Key - Value:
group.com.yourcompany.yourapp
- Key:
- In
PushEngageNotificationServiceExtension'sInfo.plist, add the same key and value. - Select the extension target → Signing & Capabilities → enable the same App Group.
Troubleshooting
Build error: User Script Sandboxing
In Xcode → Build Settings → search for User Script Sandboxing → set to No.
Conflict between Firebase SDK and PushEngage swizzling
Disable PushEngage swizzling and wire up all four delegate methods below — omitting any one will silently break notifications, click tracking, or foreground display:
// In AppDelegate.init()
PushEngage.swizzleInjection(isEnabled: false)
// In AppDelegate
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
PushEngage.registerDeviceToServer(with: deviceToken)
}
func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
PushEngage.receivedRemoteNotification(application: application,
userInfo: userInfo,
completionHandler: completionHandler)
}
// Set yourself as UNUserNotificationCenter delegate, then:
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler:
@escaping (UNNotificationPresentationOptions) -> Void) {
PushEngage.willPresentNotification(center: center,
notification: notification,
completionHandler: completionHandler)
}
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
PushEngage.didReceiveRemoteNotification(with: response)
completionHandler()
}