How to Set Up iOS App Push Notifications with PushEngage

You can now implement push notifications for your iOS application using our iOS App SDK. You would need the help of your developer as it would be a one-time integration before you can send them push notifications using the PushEngage dashboard.

Before You Start

  • You would need Xcode installed on your system.
  • A valid developer account and App ID are configured on the Apple Developer Portal.

We will follow these steps to enable push notifications for your iOS Application.

Configuring Your App

Enable remote Notifications

1. Open your Xcode project and select the root project in the Project Navigator. Choose your main app target.

2. Navigate to Signing & Capabilities. Ensure that the Background Modes capability is added. If not, add it by clicking the “+ Capability” button.

3. Similarly, ensure that the Push Notifications capability is added. If not, add it using the “+ Capability” button.

If the Push Notifications capability is not visible in Xcode. You need to follow below steps :

1. Go to your Apple Developer account.

2. Navigate to Certificates, Identifiers & Profiles.

3. Select your App identifier. Edit the configuration of your App ID and ensure that Push Notifications is enabled.

4. Return to Xcode and attempt to add the “Push Notifications” capability again.

Enable Background Modes

1. In your Xcode project, navigate to Signing & Capabilities.

2. Inside Background Modes, enable both Remote notifications and Background Fetch.

This step ensures your app can efficiently handle remote notifications and background fetches.

Here is the guide to creating your APNs certificate.

Integrating PushEngage iOS SDK

The PushEngage iOS SDK is available as a Swift package and a CocoaPods pod. We request you to go through both methods but only use one of them while completing your setup.

Integrating PushEngage iOS SDK with SPM

To integrate PushEngage iOS SDK using Swift Package Manager, follow these steps:

  1. Open XCode and navigate to your project. Select the package dependencies tab. Click on the + button.
  1. Paste the URL https://github.com/awesomemotive/pushengage-ios-sdk in the search bar. Click on “Add Package”.
  1. Select your main app target under “Add to Target” and click “Add Package”.

Integrating PushEngage iOS SDK with CocoaPods

To integrate PushEngage iOS SDK using CocoaPods, you need to follow these steps :

If CocoaPods is not installed, close your current Xcode project and run the following command in your project’s root directory

sudo gem install cocoapods

Run the following command to initialize a Podfile in your project

pod init

Open the newly created Podfile using a text editor or by following the command in your terminal in the root project directory.

open Podfile

Add the PushEngage dependency under your project’s target. Ensure that your Podfile looks similar to the example below

# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'YourProjectName' do
  # Add PushEngage SDK dependency
  pod 'PushEngage'
  post_install do |installer|
    installer.pods_project.targets.each do |target|
      target.build_configurations.each do |config|
        config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'NO'
      end
    end
  end
end

Save the Podfile and run the following commands in the terminal.

pod repo update
pod install

Open the newly created <project-name>.xcworkspace file in Xcode.

Initializing PushEngage iOS SDK

Once you have integrated the SDK, we will have to initialize the iOS SDK in your AppDelegate.

Using Swift language, here is how you can do it

import PushEngage
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    override init() {
        super.init()
        PushEngage.swizzleInjection(isEnabled: true)
    }

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
       // Set your PushEngage App ID (replace       "APP_ID_FROM_PUSH_ENGAGE_DASHBOARD" with your actual App ID)
        PushEngage.setAppId(key: "APP_ID_FROM_PUSH_ENGAGE_DASHBOARD")
        // Start PushEngage initial info
        PushEngage.setInitialInfo(for: application, with: launchOptions)
        // Enable logs for debugging (optional)
        PushEngage.enableLogs = true
        return true
    }
}

Using Objective-C language, here is how you can do it

#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 {
// Set your PushEngage App ID (replace "APP_ID_FROM_PUSH_ENGAGE_DASHBOARD" with your actual App ID)
    [PushEngage setAppIdWithKey:@"APP_ID_FROM_PUSH_ENGAGE_DASHBOARD"];
    // Set initial info
    [PushEngage setInitialInfoFor:application with:launchOptions];
    // Enable logs for debugging (optional)
    [PushEngage setEnableLogs:YES];
    return YES;
}

@end

Sometimes, when using SwiftUI, the AppDelegate might not be available by default. In such cases, you can initialize the PushEngage SDK in your main App File. Here is an example in SwiftUI

import SwiftUI
import PushEngage

@main
struct PEDemoApp: 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(key: "APP_ID_FROM_PUSH_ENGAGE_DASHBOARD")
        PushEngage.setIntialInfo(for: application,
                                             with: launchOptions)
        
        PushEngage.enableLogs = true
        return true
    }
}

Note : While developing your application, you might find it useful to enable logs from the PushEngage SDK for debugging purposes. However, it’s essential to disable logs in the production build to avoid exposing sensitive information.

Creating Notification Service Extension

The Notification Service Extension enhances your iOS app’s capability to receive notifications. This is used to modify the notification’s content or fetch/process any data on receiving the notification. You can follow the below steps to create a Notification Service Extension:

1. Open Xcode and navigate to your project. Choose File » New » Target from the menu.

2. In the template selection window, choose Notification Service Extension and click Next.

3. Provide a name for your extension, for example, PushEngageNotificationServiceExtension, and click Finish.

4. When you finish creating the Notification Service Extension, you might be prompted to activate it. Do not activate it immediately.

Activating the extension would shift Xcode’s debugging focus from your app to the extension. If you activate it by accident, don’t worry; you can switch back to debugging your app within Xcode.

Initializing PushEngage SDK for Notification Service Extension

To ensure the proper functioning of PushEngage SDK in your iOS Notification Service Extension, you need follow these steps:

1. Open the Podfile associated with your project.

2. Then you need to add Dependency. Insert the following code snippet into your Podfile:

target 'Your_Main_Application_Target' do
      pod 'PushEngage'
      post_install do |installer|
          installer.pods_project.targets.each do |target|
            target.build_configurations.each do |config|
              config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'NO'
          end
        end
      end
     
      target 'Your_Notification_Service_Extension_Target' do
              pod 'PushEngage'
      end
end

3. Run the following commands in your terminal within your root project directory:

pod repo update
pod install

4. In your Notification Service Extension target, import the PushEngage framework and add the necessary initialization code. Here’s how you can do it.

Using Swift

import UserNotifications
import PushEngage

@available(iOSApplicationExtension 10.0, *)
class PushEngageNotificationServiceExtension: 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 {
            PushEngage.didReceiveNotificationExtensionRequest(request, bestContentHandler: bestContent)
            contentHandler(bestContent)
        }
    }
    
    override func serviceExtensionTimeWillExpire() {
        // Called just before the extension will be terminated by the system.
        // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
        if let contentHandler = contentHandler, let request = request ,let bestAttemptContent =  bestAttemptContent {
            guard let content = PushEngage.serviceExtensionTimeWillExpire(request, content: bestAttemptContent) else {
                contentHandler(bestAttemptContent)
                return
            }
            contentHandler(content)
        }
    }

}

Using Objective-C

#import "NotificationService.h"
@import PushEngage;

@interface NotificationService ()

@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
@property (nonatomic, strong) UNNotificationRequest *request;

@end

@implementation NotificationService

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
    self.request = request;
    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];
    
    if (self.bestAttemptContent) {
        [PushEngage didReceiveNotificationExtensionRequest:request bestContentHandler:self.bestAttemptContent];
        contentHandler(self.bestAttemptContent);
    }
}

- (void)serviceExtensionTimeWillExpire {

    if (self.contentHandler && self.request && self.bestAttemptContent) {
        UNNotificationContent *content = [PushEngage serviceExtensionTimeWillExpire:self.request content:self.bestAttemptContent];
        if (content) {
            self.contentHandler(self.bestAttemptContent);
            return;
        }
    }
    self.contentHandler(self.bestAttemptContent);
}

@end

Creating Notification Content Extension

To improve the way by adding custom UI, you’ll need to create a Notification Content Extension. Follow the steps below to set up the extension:

1. In Xcode, go to File » New » Target.

2. Select Notification Content Extension and click Next.

3. Do not select “Activate” on the dialog that appears after clicking Finish. Canceling keeps Xcode debugging your app instead of the extension. If you accidentally activate it, switch back to debugging your app within Xcode (next to the run button).

4. In the project navigator, select the top-level project directory and select the NotificationContentExtension target in the project from the targets list created in step no. 2.

5. Set the Deployment Target to iOS 10 or above, which is the version of iOS that Apple released the support for this extension. 

Initializing PushEngage SDK for Notification Content Extension

To ensure the proper functioning of PushEngage SDK in your iOS Notification Content Extension, you need to follow these steps:

1. Open the Podfile associated with your project.

2. Add Dependency, insert the following code snippet into your Podfile:

target 'Your_Main_Application_Target' do
  pod 'PushEngage'
  target 'Your_Notification_Content_Extension' do
    pod 'PushEngage'
  end
end

3. Run the following commands in your terminal within your root project directory:

pod repo update

pod install

4. In your Notification Content Extension target, import the PushEngage framework and add the necessary initialization code. Here’s how you can do it with some examples of UI elements:

Using Swift

import UIKit
import UserNotifications
import UserNotificationsUI
import PushEngage

@available(iOSApplicationExtension 10.0, *)
class NotificationViewController: UIViewController, UNNotificationContentExtension {
    
    fileprivate var hostingView: UIHostingController<ContentView>?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .white
    }
    
    func didReceive(_ notification: UNNotification) {
        if(notification.request.content.categoryIdentifier == "your_identifier"){
            let payLoad = PushEngage.getCustomUIPayLoad(for: notification.request)
  //pass the payload to your custom View
            let view = CustomView(payLoadInfo: payLoad)
            hostingView = UIHostingController(rootView: view)
If let customView = self.hostingView {
            addChild(hostingView!)
}
        }
    }
    
}


Objective-C:

#import "NotificationViewController.h"
#import <UserNotifications/UserNotifications.h>
#import <UserNotificationsUI/UserNotificationsUI.h>

@import PushEngage;
@import UIKit;

@interface NotificationViewController () <UNNotificationContentExtension>

@property IBOutlet UILabel *label;
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (weak, nonatomic) IBOutlet UIButton *firstButton;
@property (weak, nonatomic) IBOutlet UIButton *secondButton;

@end

@implementation NotificationViewController

- (IBAction)firstbuttonAction:(id)sender {
    // do what action you want to perform.
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any required interface initialization here.
}

- (void)didReceiveNotification:(UNNotification *)notification {
    
    NotificationViewController * __block blockSelf = self;
    CustomUIModel *object = [PushEngage getCustomUIPayLoadFor:notification.request];
    dispatch_async(dispatch_get_main_queue(), ^{
        blockSelf.label.text = object.title;
        blockSelf.imageView.image = object.image;
        [blockSelf.firstButton setTitle:object.buttons.firstObject.text forState:UIControlStateNormal];
        [blockSelf.secondButton setTitle:object.buttons.lastObject.text forState:UIControlStateNormal];
        blockSelf = NULL;
    });
}

@end

Add App Groups

App Groups are essential for communication between the main app, notification service extension, and content extension. You can follow these steps to add App Groups to your iOS project :

If you have an existing app group and want to use that only, skip to step no. 5.

1. In your Xcode project, in the project navigator, select the top-level project directory and select the main target of the app.

2. Navigate to the Signing & Capabilities tab.

3. Click the “+ Capability” button and select App Groups from the list.

4. Click on the + button to add an App Group. Add a unique name to your App Group and click on OK.

5. In the main editor area, select the main target of your app and create an app group. Please provide the name of the group in your application Info.plist with the key PushEngage_App_Group_Key.

6. Add the same key and value in the Notification Service Extension’s Info.plist file.

7. Select the same app group in the Main Application Target and the YourNotificationServiceExtension.

Do ensure that you are choosing your notification service extension in the above step.

Deep Linking

Deep linking enables your subscribers to navigate directly to a specific screen within the application or a designated webpage when interacting with push notifications. By default, if you provide a valid URL, the subscriber will be redirected to that webpage.

Handling Web URLs:

  • Provide the PushEngageInAppEnabled to YES in Info.plist, then the URL will load within the application using WKWebview.
  • Provide the PushEngageInAppEnabled to NO in Info.plist, if you want your subscriber to redirect to safari to load the URL. 
  • Provide the PushEngageAutoHandleDeeplinkURL to YES in Info.plist, then the SDK will handle the deep link according to PushEngageInAppEnabled configuration.
  • Provide the PushEngageAutoHandleDeeplinkURL to NO in Info.plist, then the control of handling the deep link will be given to the client app from the SDK.
    • If the deeplink is not a valid URL, you need to configure navigation by using setNotificationOpenHandler in the AppDelegate within the didFinishLaunchingWithOptions method. This allows you to navigate to a specific screen based on the provided string, as shown below. If not configured, the SDK will simply open the application.
    ios-deeplink

    Using Swift:

    class AppDelegate: UIResponder, UIApplicationDelegate {
        
        var window: UIWindow?
        
        
        override init() {
            super.init()
            // method Swizzling enabled for the application.
            PushEngage.swizzleInjection(isEnabled: true)
        }
    
       func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    
         PushEngage.setAppId(key: "APP_ID_FROM_PUSH_ENGAGE_DASHBOARD")
         PushEngage.setInitialInfo(for: application,with: launchOptions)
            
            
         // Notification open handler.
         // deep linking screen
         // here ShoesScreen and pepay are example deep link texts
         PushEngage.setNotificationOpenHandler { (result) in
                //replace this block with your own handling
                let additionData = result.notification.additionalData
                if result.notificationAction.actionID == "ShoesScreen" {
                    print(additionData ?? [])
                    let storyBoard = UIStoryboard(name: "Main", bundle: .main)
                    let viewController = storyBoard.instantiateViewController(withIdentifier: "SportViewController")
                    let navcontroller = application.windows.first?.rootViewController as? UINavigationController
                    navcontroller?.popToRootViewController(animated: true)
                    navcontroller?.pushViewController(viewController, animated: true)
                } else if result.notificationAction.actionID == "SalesScreen" {
                    let storyBoard = UIStoryboard(name: "Main", bundle: .main)
                    let viewController = storyBoard.instantiateViewController(withIdentifier: "NotificationApiTestViewconttoller")
                    let navcontroller = application.windows.first?.rootViewController as? UINavigationController
                    navcontroller?.popToRootViewController(animated: true)
                    navcontroller?.pushViewController(viewController, animated: true)
                } else if result.notificationAction.actionID == "pepay" {
                    let storyBoard = UIStoryboard(name: "Main", bundle: .main)
                    let viewController = storyBoard.instantiateViewController(withIdentifier: "PEPay")
                    let navcontroller = application.windows.first?.rootViewController as? UINavigationController
                    navcontroller?.popToRootViewController(animated: true)
                    navcontroller?.pushViewController(viewController, animated: true)
                }
            }
            
            PushEngage.enableLogs = true
            
            return true
        }
    }
    

    Using Objective-C :

    @implementation AppDelegate
    
    
    - (instancetype)init
    {
        self = [super init];
        if (self) {
           [PushEngage swizzleInjectionWithIsEnabled: YES];
        }
        return self;
    }
    
    typedef void (^PEnotificationOpenHandler)(PENotificationOpenResult * nonnull);
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        UNUserNotificationCenter.currentNotificationCenter.delegate = self;
        PEnotificationOpenHandler actionHandler = ^void(PENotificationOpenResult *result) {
           //replace this block with your own handling
            if ([result.notificationAction.actionID isEqualToString: @"ShoesScreen"]) {
                AddToCart *controller = [AddToCart new];
                UINavigationController *navigationController = (UINavigationController *) application.windows.firstObject.rootViewController;
                [navigationController popToRootViewControllerAnimated:YES];
                [navigationController pushViewController:controller animated:YES];
            } else if ([result.notificationAction.actionID isEqualToString: @"SalesScreen"]) {
                SportsViewcontroller *controller = [SportsViewcontroller new];
                UINavigationController *navigationController = (UINavigationController *) application.windows.firstObject.rootViewController;
                [navigationController popToRootViewControllerAnimated:YES];
                [navigationController pushViewController:controller animated:YES];
            }
        };
        application.applicationIconBadgeNumber = 0;
        [PushEngage setAppIdWithKey:@"APP_ID_FROM_PUSH_ENGAGE_DASHBOARD"];
        [PushEngage setInitialInfoFor:application with:launchOptions];
        [PushEngage setNotificationOpenHandlerWithBlock:actionHandler];
        [PushEngage setEnableLogs:true];
        return YES;
    }
    @end
    

    Handling Notifications in the Foreground

    When notifications come in the foreground you have to make a decision whether or not to show the notification alert to the device.To handle notifications in foreground use the setNotificationWillShowInForgroundHandler

    If the completion block is not called by you at all SDK will call the completion block after 29 seconds. In any case, silent or alert notification when the application is in foreground mode.

    Using Swift

    class AppDelegate: UIResponder, UIApplicationDelegate {
        
        var window: UIWindow?
        
        
        override init() {
            super.init()
            // method Swizzling enabled for the application.
           PushEngage.swizzleInjection(isEnabled: true)
        }
    
        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    
            PushEngage.setAppId(key: "APP_ID_FROM_PUSH_ENGAGE_DASHBOARD")
            PushEngage.setInitialInfo(for: application, with: launchOptions)
            
            // Notification handler when notification deliver's and app is in foreground.
            
            PushEngage.setNotificationWillShowInForgroundHandler { notification, completion in
                if notification.contentAvailable == 1 {
                    // in case the developer failed to set the completion handler. After 29 sec the handler will call from the SDK after 29 sec.
                    completion(nil)
                } else {
                    completion(notification)
                }
            }
            
            PushEngage.enableLogs = true
            
            return true
        }
    }
    
    
    

    Using Objective-C

    @implementation AppDelegate
    
    
    - (instancetype)init
    {
        self = [super init];
        if (self) {
           [PushEngage swizzleInjectionWithIsEnabled: YES];
        }
        return self;
    }
    // please create this handlers 
    typedef void (^ _Nonnull PENotificationDisplayHandler)(PENotification * _Nullable);
    
    -(BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        UNUserNotificationCenter.currentNotificationCenter.delegate = self;
        
        [PushEngage setNotificationWillShowInForgroundHandlerWithBlock:^(PENotification * _Nonnull notification, PENotificationDisplayHandler completion) {
            if (notification.contentAvailable == 1) {
                completion(nil);
            } else {
                completion(notification);
            }
        }];
        [PushEngage setAppIdWithKey:@"APP_ID_FROM_PUSH_ENGAGE_DASHBOARD"];
        [PushEngage setInitialInfoFor:application with:launchOptions];
        [PushEngage setEnableLogs:true];
        return YES;
    }
    @end
    

    Troubleshooting

    Resolving Build Issues Related to Sandboxing

    Problem:

    You’re experiencing build issues related to sandboxing.

    Solution:

    • Open your project in Xcode.
    • Navigate to Build Settings.
    • Locate the User Script Sandboxing option.
    • Set it to No.
    Issues with Method Swizzling in Firebase and PushEngage SDKs

    Problem:

    You’re using both the Firebase SDK and PushEngage SDK with method swizzling enabled for both, and it’s causing issues.

    Solution:

    Disable method swizzling for the PushEngage SDK and follow the manual steps for handling PushEngage methods. You can find detailed instructions at:
    PushEngage iOS SDK Documentation

    If you want to explore more of the capabilities of iOS SDK, you can go through our detailed API documentation.

    If you run into any issues, please contact us by clicking here. Our support team will be able to help you.

    Still stuck? How can we help?
    Last updated on November 20th, 2024

    Engage and Retain Visitors AfterThey’ve Left Your Website

    Increase the value of every web visit with Push Notifications that are hard to miss.

    • Forever Free Plan
    • Easy Setup
    • 5 Star Support