Manual Integration in Swift

Prev Next

Goal

Manually integrate the Mapp Engage SDK into your iOS app using Swift.

Note

  • This guide covers manual integration for advanced users who need greater customization.

  • For basic integration, see the Basic Automated Setup. For the Objective-C version of this guide, see Manual Integration in Objective-C.

  • 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 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.swift file and add:

import AppoxeeSDK

Step 4: Initialize the SDK

Add the following to the application(_:didFinishLaunchingWithOptions:) method:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    Appoxee.shared().engage(withLaunchOptions: launchOptions, andDelegate: self, andSDKID: "xxx.xxx", with: .L3)

    // Insert other initialization code or other Frameworks code here.

    return true
}
  • 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

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

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    Appoxee.shared().didRegister(forRemoteNotificationsWithDeviceToken: deviceToken)
}

Receive Remote Notifications

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    Appoxee.shared().receivedRemoteNotification(userInfo)
    completionHandler(.newData)
}

Forward Notification Taps

When a user taps a notification or selects a notification action, iOS calls userNotificationCenter(_:didReceive: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.

func userNotificationCenter(_ center: UNUserNotificationCenter,
                            didReceive response: UNNotificationResponse,
                            withCompletionHandler completionHandler: @escaping () -> Void) {
    guard let sdk = Appoxee.shared() else {
        completionHandler()
        return
    }
    sdk.userNotificationCenter(center, didReceive: 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

Import UserNotifications and make your AppDelegate conform to UNUserNotificationCenterDelegate. iOS only calls this method if your AppDelegate is the delegate of UNUserNotificationCenter.current(). 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.

Step 6: Custom Delegate (Optional)

Handle custom push actions by implementing:

// MARK: - AppoxeeNotificationDelegate
func appoxee(_ appoxee: Appoxee, handledRemoteNotification pushNotification: APXPushNotification, andIdentifer actionIdentifier: String) {
    // A push notification was received and processed.
}

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 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(_:willPresent: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.

userNotificationCenter(_:didReceive:withCompletionHandler:)

The user taps a displayed notification or selects a notification action.

Navigation, deep links and action handling. Forward it to the SDK.

Only didReceive represents the user's tap. Implementing willPresent or didReceiveRemoteNotification does not replace forwarding didReceive to the SDK.

What Happens Without Forwarding

If your app implements didReceive 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.

{
  "aps": {
    "alert": {
      "title": "Example",
      "body": "Message"
    }
  },
  "push_provider": "mapp_engage"
}

Check the field with a helper method:

private func isMappNotification(_ userInfo: [AnyHashable: Any]) -> Bool {
    userInfo["push_provider"] as? String == "mapp_engage"
}

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

func application(_ application: UIApplication,
                 didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                 fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    guard isMappNotification(userInfo) else {
        // Not a Mapp notification: forward it to the other provider
        // or handle it in the app, then call the completion handler once.
        completionHandler(.noData)
        return
    }

    // Mapp notification: the SDK calls the completion handler.
    Appoxee.shared()?.didReceiveRemoteNotification(userInfo,
                                                   fetchCompletionHandler: completionHandler,
                                                   andNotifyCompletionWith: nil)
}

Filter Notification Taps

func userNotificationCenter(_ center: UNUserNotificationCenter,
                            didReceive response: UNNotificationResponse,
                            withCompletionHandler completionHandler: @escaping () -> Void) {
    let userInfo = response.notification.request.content.userInfo

    guard isMappNotification(userInfo) else {
        // Not a Mapp notification: forward it to the other provider
        // or handle it in the app, then call the completion handler once.
        completionHandler()
        return
    }

    guard let sdk = Appoxee.shared() else {
        completionHandler()
        return
    }

    // Mapp notification: the SDK calls the completion handler.
    sdk.userNotificationCenter(center, didReceive: 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

If your app routes notifications to providers itself, check whether you should use this manual integration instead of the Basic Automated Setup. The automatic integration uses method swizzling and may handle notification callbacks before your own filter runs.