--- title: "Manual Integration in Objective-C" slug: "mapp-engage-ios-sdk-manual-integration-objc" description: "Integrate the Mapp Engage iOS SDK manually in Objective-C: initialize the SDK, handle push notifications, forward notification taps and filter push providers." status: "update" updated: 2026-11-02T16:15:52Z published: 2026-11-02T16:15:52Z canonical: "docs.mapp.com/mapp-engage-ios-sdk-manual-integration-objc" --- > ## Documentation Index > Fetch the complete documentation index at: https://docs.mapp.com/llms.txt > Use this file to discover all available pages before exploring further. # Manual Integration in Objective-C ## Goal Manually integrate the Mapp Engage SDK into your iOS app using Objective-C. > [!NOTE] > Note > > - This guide covers manual integration for advanced users who need greater customization. > - For basic integration, see the [Basic Automated Setup](/v1/docs/mapp-engage-ios-sdk-basic-automated-setup). For the Swift version of this guide, see [Manual Integration in Swift](/v1/docs/mapp-engage-ios-sdk-manual-integration-swift). > - Class and method names in the SDK use `Appoxee`, the legacy name of the Mapp Engage SDK. --- ## Prerequisites Before starting, ensure you have: 1. Xcode with: - A base SDK of **iOS 12 or above** - A deployment target of **iOS 12 or above** 2. A Mapp Engage Account with the mobile channel activated. 3. Required permissions configured in your app's `Info.plist`. --- ## Procedure ### Step 1: Download the SDK [Download](/v1/docs/mapp-engage-ios-sdk-installation) the SDK. ### Step 2: Add the SDK to Your Project Drag `AppoxeeSDK.xcframework` into your project. ### Step 3: Import the SDK in AppDelegate Open your `AppDelegate.m` file and add: ```objectivec #import ``` Declare the delegate in `@interface`: ```objectivec @interface AppDelegate() @end ``` ### Step 4: Initialize the SDK Add the following to the `application:didFinishLaunchingWithOptions:` method: ```objectivec - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    [[Appoxee shared] engageWithLaunchOptions:launchOptions andDelegate:self andSDKID:@"xxx.xxx" with:L3];    // Insert other initialization code or other Frameworks code here.    return YES; } ``` - Replace `xxx.xxx` with the SDK Key configured in your Mapp Engage Channel Configuration. - The `with` parameter is the `SERVER` enum. Customer-facing values are `L3`, `EMC`, `EMC_US`, and `CROC`. Your account manager will tell you which one to use. > [!NOTE] > Note > > The `SERVER` enum also defines `TEST`, `TEST55`, and `TEST61` values. These are internal test environments and are not meant for production use. ### Step 5: Handle Notifications Implement the following methods in your `AppDelegate` to handle push notifications. All three are required. #### Register the Device Token ```objectivec - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {    [[Appoxee shared] didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; } ``` #### Receive Remote Notifications ```objectivec - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {    [[Appoxee shared] receivedRemoteNotification:userInfo];    completionHandler(UIBackgroundFetchResultNewData); } ``` #### Forward Notification Taps When a user taps a notification or selects a notification action, iOS calls `userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:` on your `UNUserNotificationCenterDelegate`. Forward this call to the SDK. Only then can the SDK open the landing page or deep link and record the push click. ```objectivec - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response         withCompletionHandler:(void (^)(void))completionHandler {    Appoxee *sdk = [Appoxee shared];    if (sdk == nil) {        completionHandler();        return;    }    [sdk userNotificationCenter:center didReceiveNotificationResponse:response   withAppoxeeCompletionHandler:^{        // The SDK has finished processing the notification response.        completionHandler();    }]; } ``` Call the system completion handler only from the SDK completion block. Do not call it again after forwarding. > [!NOTE] > Note > > Import `UserNotifications` and add `UNUserNotificationCenterDelegate` to the protocols of your `AppDelegate`. iOS only calls this method if your `AppDelegate` is the delegate of `[UNUserNotificationCenter currentNotificationCenter]`. Assign the delegate before `application:didFinishLaunchingWithOptions:` returns. Otherwise, the tap that launches the app is not delivered. If you skip this step, see [What Happens Without Forwarding](/docs/mapp-engage-ios-sdk-manual-integration-objc#what-happens-without-forwarding). ### Step 6: Custom Delegate (Optional) Handle custom push actions by implementing: ```objectivec #pragma mark - AppoxeeNotificationDelegate - (void)appoxee:(Appoxee *)appoxee handledRemoteNotification:(APXPushNotification *)pushNotification andIdentifer:(NSString *)actionIdentifier {    // A push notification was received and processed. } ``` > [!WARNING] > Warning > > The selector `appoxee:handledRemoteNotification:andIdentifer:` contains a typo (`Identifer` without the second `i`) in the public header. Use the selector exactly as shown. It matches the symbol exported by `AppoxeeSDK.xcframework`. `AppoxeeNotificationDelegate` receives callbacks from the SDK. It does not replace [forwarding notification taps](/docs/mapp-engage-ios-sdk-manual-integration-objc#forward-notification-taps) from your `UNUserNotificationCenterDelegate`. --- ## Notification Callbacks at a Glance iOS uses three different callbacks for push notifications. Each one covers a different moment in a notification's lifecycle. | Callback | When iOS calls it | Use it for | | --- | --- | --- | | `application:didReceiveRemoteNotification:fetchCompletionHandler:` | The app receives a remote notification payload, especially a background or silent push with `"content-available": 1`. | Fetching or updating data. This callback does **not** mean that the user tapped the notification. | | `userNotificationCenter:willPresentNotification:withCompletionHandler:` | A notification arrives while the app is in the foreground. | Deciding whether iOS shows a banner, plays a sound or updates the badge. See [Foreground Notifications](/v1/docs/mapp-engage-ios-sdk-foreground-notifications). | | `userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:` | The user taps a displayed notification or selects a notification action. | Navigation, deep links and action handling. [Forward it to the SDK.](/docs/mapp-engage-ios-sdk-manual-integration-objc#forward-notification-taps) | Only `didReceiveNotificationResponse` represents the user's tap. Implementing `willPresentNotification` or `didReceiveRemoteNotification` does not replace forwarding `didReceiveNotificationResponse` to the SDK. ### What Happens Without Forwarding If your app implements `didReceiveNotificationResponse` but does not forward it to the SDK, the SDK never learns that the user tapped the notification. As a result: - The landing page or deep link may not open. The app opens on its home screen instead. - Custom notification actions may not be processed. - Push clicks are not tracked. - Campaign statistics undercount engagement. --- ## Apps With Multiple Push Providers If your app receives push notifications from Mapp Engage and from other providers, forward only Mapp notifications to the SDK. Route all other notifications to the provider they belong to. ### Identify Mapp Notifications Add a dedicated custom field to the push payload, for example `push_provider` with the value `mapp_engage`. Set this field in every Mapp Engage campaign and template that sends push notifications. `push_provider` is an example name. The SDK does not add this field automatically. ```json {  "aps": {    "alert": {      "title": "Example",      "body": "Message"    }  },  "push_provider": "mapp_engage" } ``` Check the field with a helper function: ```objectivec static BOOL IsMappNotification(NSDictionary *userInfo) {    return [@"mapp_engage" isEqual:userInfo[@"push_provider"]]; } ``` Do not use the following fields to identify Mapp notifications: - `aps`: present in every APNs notification. - `mutable-content`: used by many providers. - `ios_apx_media`: Mapp-specific, but only present in rich media messages. - Category identifiers: set per campaign and not guaranteed. ### Filter Background Pushes ```objectivec - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {    if (!IsMappNotification(userInfo)) {        // Not a Mapp notification: forward it to the other provider        // or handle it in the app, then call the completion handler once.        completionHandler(UIBackgroundFetchResultNoData);        return;    }    // Mapp notification: the SDK calls the completion handler.    [[Appoxee shared] didReceiveRemoteNotification:userInfo                            fetchCompletionHandler:completionHandler                      andNotifyCompletionWithBlock:nil]; } ``` ### Filter Notification Taps ```objectivec - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response         withCompletionHandler:(void (^)(void))completionHandler {    NSDictionary *userInfo = response.notification.request.content.userInfo;    if (!IsMappNotification(userInfo)) {        // Not a Mapp notification: forward it to the other provider        // or handle it in the app, then call the completion handler once.        completionHandler();        return;    }    Appoxee *sdk = [Appoxee shared];    if (sdk == nil) {        completionHandler();        return;    }    // Mapp notification: the SDK calls the completion handler.    [sdk userNotificationCenter:center didReceiveNotificationResponse:response   withAppoxeeCompletionHandler:completionHandler]; } ``` ### Call the Completion Handler Exactly Once For each notification, exactly one party calls the completion handler: - For a Mapp notification, pass the original completion handler to the SDK. The SDK calls it. - For any other notification, the other provider or your app calls it. - Never call the completion handler again after you passed it to the SDK. - If several non-Mapp providers handle notifications, use one coordinator that combines their results and calls the system completion handler once. Do not pass the same system completion handler to several providers. > [!WARNING] > Warning > > If your app routes notifications to providers itself, check whether you should use this manual integration instead of the [Basic Automated Setup](/v1/docs/mapp-engage-ios-sdk-basic-automated-setup). The automatic integration uses method swizzling and may handle notification callbacks before your own filter runs. --- ## Troubleshooting 1. **Framework Not Found**: Ensure the framework is linked correctly in the "Build Phases" tab of your Xcode project. 2. **Push Notifications Not Working**: Double-check your APNs certificate and Mapp Engage Dashboard settings. 3. **Compile Errors**: Confirm import paths and SDK version compatibility. 4. **App Opens on Its Home Screen Instead of the Landing Page**: Check that your app forwards notification taps to the SDK, see [Forward Notification Taps](/docs/mapp-engage-ios-sdk-manual-integration-objc#forward-notification-taps). If you use several push providers, also check that your filter does not discard Mapp notifications. Without forwarding, push clicks are not tracked either. ## Related - [Installation](/mapp-engage-ios-sdk-installation.md)