Push Notifications for iOS apps
Add Apptics framework to your Apple project
By now, you would have already integrated Apptics with your Apple project. If not, refer to the integration guide.
- Open the .xcworkspace file and make sure you have enabled push notification and app group identifier in your application.
- Go to your root project in Xcode and select the main app target. Navigate to the Signing & Capabilities tab.
- If push notifications are not enabled, click + Capability and add push notifications.

- Click + Capability again and add Background modes.
- Also, enable the remote notifications.

Add notification service extension
The AppticsNotificationServiceExtension enables your iOS app to receive the rich notifications with images, buttons, and badges. It is also essential for Apptics' confirmed devliery analytics stats.
- In Xcode, navigate to File > New > Target.
- Select the Notification Service Extension and click Next.

- Give a name to the NotificationServiceExtension and click Finish.

- When prompted to activate the scheme after selecting Finish, click Cancel to avoid activating it.

- In Xcode, select the NotificationServiceExtension target.
- Go to general settings. Set the minimum deployment to match your main application target. This should be iOS 14.5 or higher.

Add app groups
App groups enable your app and the NotificationServiceExtension to share data when a notification is received, even if the app is not running. This is essential for implementing the badges and confirmed deliveries.
- Select your main app target in Xcode.
- Go to Signing & Capabilities and click on + Capability.
- Choose App groups.

- Click on + to add a new group.
- Set the app groups container name to 'group.MAIN_BUNDLE_IDENTIFIER.apptics', where the MAIN_BUNDLE_IDENTIFIER should match the bundle identifier of your main application.

- Click OK to save the app group for your main app target. Repeat the steps for the NotificationServiceExtension Target.
- Select the NotificationServiceExtension Target > Signing & Capabilities > + Capability > App groups.

- In app groups, click + button to add a new group.
- Set the app group container to 'group.MAIN_BUNDLE_IDENTIFIER.apptics', making sure NOT to include 'NotificationServiceExtension' in the name.
- Replace the MAIN_BUNDLE_IDENTIFIER with the bundle identifier of your main application.

Add notification content extension (Carousel UI)
- Create the extension target: File > New > Target.
- Choose Notification Content Extension.

- Create the extension target:

Install Apptics pods (CocoaPods)
- Install the Apptics iOS SDK with Cocoapod.
- Specify pod 'AppticsMessaging and AppticsNotificationServiceExtension' in your podfile.
- The podfile will look something similar to the one shown below.
Copiedsource 'https://github.com/CocoaPods/Specs.git'
target 'MAIN TARGET' do
pod 'Apptics-SDK'
pod 'AppticsMessaging'
# Pre build script will register the app version, upload dSYM file to the server and add apptics specific information to the main info.plist which will be used by the SDK.
script_phase :name => 'Apptics pre build', :script => 'sh "./Pods/Apptics-SDK/scripts/run" --upload-symbols-for-configurations="Release, Appstore" --app-group-identifier="group.MAIN_BUNDLE_IDENTIFIER.apptics"', :execution_position => :before_compile
end
target 'NOTIFICATION EXTENSION TARGET' do
pod 'AppticsNotificationServiceExtension'
end
target 'NOTIFICATION CONTENT TARGET' do
pod 'AppticsNotificationContentExtension'
end- Add the AppticsMessaging, AppticsNotificationServiceExtension, and AppticsNotificationContentExtension frameworks.
- From your terminal, navigate to the project root folder and run the pod install --repo-update.
Import and initialize in main application
Copied#import <Apptics/Apptics.h>
#import <AppticsMessaging/APMessaging.h>
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey,id> *)launchOptions {
[Apptics initializeWithVerbose:true];
[APMessaging startService];
return YES;
}Copiedimport Apptics
import AppticsMessaging
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Apptics.initialize(withVerbose: true)
APMessaging.startService()
return true
}Integrate the notification service extension
- In the Xcode project navigator, select the NotificationServiceExtension folder.
- Locate and open the NotificationService.m (for Obj-C) or NotificationService.swift (for Swift) file.
- Replace the whole content of the file with the following code.
Copied#import "NotificationService.h"
#import <AppticsNotificationServiceExtension/AppticsNotificationServiceExtension.h>
@interface NotificationService ()
@property (nonatomic, copy) void (^contentHandler)(UNNotificationContent * _Nonnull);
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
@end
@implementation NotificationService
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request
withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
APPushNotificationExtension *apext = [APPushNotificationExtension new];
apext.appGroup = @"group.MAIN_BUNDLE_IDENTIFIER.apptics";
if ([apext isNotificationFromApptics:request]) {
[apext didReceiveNotificationExtensionWithContent:self.bestAttemptContent
contentHandler:contentHandler];
} else {
contentHandler(self.bestAttemptContent);
}
}
- (void)serviceExtensionTimeWillExpire {
self.contentHandler(self.bestAttemptContent);
}
@endCopiedimport UserNotifications
import AppticsNotificationServiceExtension
final class NotificationService: UNNotificationServiceExtension {
private var contentHandler: ((UNNotificationContent) -> Void)?
private var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
self.bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
guard let bestAttemptContent else { return }
let apext = APPushNotificationExtension()
apext.appGroup = "group.MAIN_BUNDLE_IDENTIFIER.apptics"
if apext.isNotificationFromApptics(request) {
apext.didReceiveNotificationExtension(withContent: bestAttemptContent,
contentHandler: contentHandler)
} else {
contentHandler(bestAttemptContent)
}
}
override func serviceExtensionTimeWillExpire() {
if let bestAttemptContent {
contentHandler?(bestAttemptContent)
}
}
}Integrate notification content extension (Carousel UI)
Copied#import "NotificationViewController.h"
#import <UserNotifications/UserNotifications.h>
#import <UserNotificationsUI/UserNotificationsUI.h>
#import <AppticsNotificationContentExtension/AppticsNotificationContentExtension.h>
static NSString *const kAppticsCarouselCategory = @"CUSTOM_CATEGORY_APPTICS_CAROUSEL";
@interface NotificationViewController () <UNNotificationContentExtension>
@property IBOutlet UILabel *label;
@property (nonatomic, strong) APNotificationContentExtension *apext;
@property (nonatomic, strong) UNNotification *bestAttemptContent;
@end
@implementation NotificationViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.label.hidden = YES;
}
- (void)dealloc {
if ([self.bestAttemptContent.request.content.categoryIdentifier isEqualToString:kAppticsCarouselCategory] && self.apext) {
[self.apext stopAutoScrollIfNeeded];
[self.apext stopMessageCountdownIfNeeded];
}
}
- (void)didReceiveNotification:(UNNotification *)notification {
self.bestAttemptContent = notification;
if ([self.bestAttemptContent.request.content.categoryIdentifier isEqualToString:kAppticsCarouselCategory]) {
if (!self.apext) {
self.apext = [[APNotificationContentExtension alloc] initWithViewController:self];
[self.apext setupUI];
}
[self.apext didReceiveNotificationExtensionWithContent:self.bestAttemptContent contentHandler:nil];
return;
}
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
if ([self.bestAttemptContent.request.content.categoryIdentifier isEqualToString:kAppticsCarouselCategory] && self.apext) {
[self.apext viewDidLayoutSubviews];
}
}
@endCopiedimport UIKit
import UserNotifications
import UserNotificationsUI
import AppticsNotificationContentExtension
final class NotificationViewController: UIViewController, UNNotificationContentExtension {
private let kAppticsCarouselCategory = "CUSTOM_CATEGORY_APPTICS_CAROUSEL"
@IBOutlet weak var label: UILabel!
private var apext: APNotificationContentExtension?
private var bestAttemptContent: UNNotification?
override func viewDidLoad() {
super.viewDidLoad()
label.isHidden = true
}
deinit {
if bestAttemptContent?.request.content.categoryIdentifier == kAppticsCarouselCategory, let apext {
apext.stopAutoScrollIfNeeded()
apext.stopMessageCountdownIfNeeded()
}
}
func didReceive(_ notification: UNNotification) {
bestAttemptContent = notification
guard notification.request.content.categoryIdentifier == kAppticsCarouselCategory else { return }
if apext == nil {
apext = APNotificationContentExtension(viewController: self)
apext?.setupUI()
}
apext?.didReceiveNotificationExtension(withContent: notification, contentHandler: nil)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if bestAttemptContent?.request.content.categoryIdentifier == kAppticsCarouselCategory {
apext?.viewDidLayoutSubviews()
}
}
}Carousel category requirement (important)
- Add the following key-value pairs to the NotificationContentExtension’s Info.plist.
| UNNotificationExtensionCategory | String | CUSTOM_CATEGORY_APPTICS_CAROUSEL |
| UNNotificationExtensionDefaultContentHidden | Bool | YES |
| UNNotificationExtensionUserInteractionEnabled | Bool | YES |
| UNNotificationExtensionInitialContentSizeRatio | Number | 1 |

Your carousel notifications must arrive with:
CopiedUNNotificationContent.categoryIdentifier == "CUSTOM_CATEGORY_APPTICS_CAROUSEL"
In practice, that means your push payload must include aps.category = CUSTOM_CATEGORY_APPTICS_CAROUSEL, so iOS sets the notification category and the content extension runs the carousel UI logic.
Handling notification payload in app
APNotificationHandler is a central helper to handle push tap actions such as:
- Notification body tap (default action)
- Action button taps (actionButton[].action)
- clickAction resolution from addInfo (JSON string or dictionary)
- Deep link/URL open
Copied#import <AppticsMessaging/AppticsMessaging.h>Copiedimport AppticsMessaging SwiftSet notification delegate on launch
Copied[UNUserNotificationCenter currentNotificationCenter].delegate = self;Handle push tap in AppDelegate
Copiedfunc isAppticsPayload(_ userInfo: [AnyHashable: Any]) -> Bool {
var parsed: [String: Any]?
if let addInfo = userInfo["addInfo"] as? [String: Any] {
parsed = addInfo
} else if let addInfoString = userInfo["addInfo"] as? String,
let data = addInfoString.data(using: .utf8),
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
parsed = json
}
return parsed?["appticsPnID"] != nil
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
if ([self isNotificationFromApptics:request]) {
[[APNotificationHandler shared] handleNotificationResponse:response
completionHandler:completionHandler];
}else{
completionHandler();
}
}
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
if self.isNotificationFromApptics(request) {
APNotificationHandler.shared().handleNotificationResponse(
response,
completionHandler: completionHandler
)
}else{
completionHandler()
}
}Build and test checklist
- Confirm main app can register for remote notifications.
- Confirm both extensions have the correct app group.
Copiedgroup.<MAIN_BUNDLE_IDENTIFIER>.apptics- Send a test carousel push with aps.category = CUSTOM_CATEGORY_APPTICS_CAROUSEL.
Copiedaps.category = CUSTOM_CATEGORY_APPTICS_CAROUSEL
Verify:
- Notification Service Extension runs (SDK modifies notification content / sets category-related behavior)
- Notification Content Extension displays the carousel UI when category matches
Configure notification service
Upload p12 certificate
Refer to this link for detailed steps on how to upload your p12 certificate to Apptics.
On this page
- Add Apptics framework to your Apple project
- Add notification service extension
- Add app groups
- Add notification content extension (Carousel UI)
- Add AppticsMessaging and AppticsNotificationServiceExtension
- Import and initialize in main application
- Integrate the notification service and content extension
- Handle notification payload in app
- Build and test checklist
- Upload p12 certificate