# Push Message Integration

{% hint style="info" %}
**Can be used alongside other push solutions**

To use alongside another push solution, you must disable the Swizzling option of that solution.

After disabling Swizzling, refer to that solution's guide to manually configure push notification handling.
{% endhint %}

{% hint style="danger" %}
FCM-based iOS Push Messages are not supported.

To use iOS Push Messages, please integrate with APNs.
{% endhint %}

{% stepper %}
{% step %}

#### Configure APNs

To use Push Messages in an iOS app, you need to configure the integration between the Hackle Workspace and APNs.

For more details, see [Apple Push Notification Service Configuration](/en/external-link/crm-channels/apple-push-notification-service-integration.md).
{% endstep %}

{% step %}

#### Add PushNotification Capability to the App

In the Xcode project settings, click `+ Capability` in the `Signing & Capabilities` tab as shown below.

![](/files/7dS2ujMRVzUXJ3xs1Ngq)

Add `Push Notifications` and `Background Modes`.

![](/files/Zk4zETlTHnCGFSyEZ2K8)

![](/files/bXaDSc8S0qPKd0DGyic4)

Then enable `Remote notifications` in `Background Modes`.

![](/files/KUDu0CGgEijMdpROdkjk)
{% endstep %}

{% step %}

#### Configure AppDelegate

{% hint style="info" %}
AppDelegate configuration is required to collect push tokens, display Push Messages, and handle push clicks.
{% endhint %}

AppDelegate is required for Push Message integration.

Complete the following configuration so Hackle can deliver Push Messages to devices with the iOS app installed.

{% tabs %}
{% tab title="Swift" %}

```swift
class AppDelegate: NSObject, UIApplicationDelegate {
  func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
  ) -> Bool {
    return true
  }
}
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@end
```

```objectivec
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  return YES;
}
```

{% endtab %}
{% endtabs %}

If using `SwiftUI`, register the `AppDelegate` with `SwiftUI` as follows.

```swift
import SwiftUI

@main
struct sampleApp: App {
  ...
  @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
  ...
}
```

{% endstep %}

{% step %}

#### Collect Push Token

{% hint style="warning" %}
Push tokens are not automatically collected.

You must collect push tokens using the code below to receive pushes.
{% endhint %}

Add the `setPushToken` method to `AppDelegate` as shown below.

{% tabs %}
{% tab title="Swift" %}

```swift
import Hackle

class AppDelegate: NSObject, UIApplicationDelegate {
  ...
  func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {

    // iOS 앱에서 푸시 권한 요청
    let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]

    UNUserNotificationCenter.current().requestAuthorization(
      options: authOptions,
      completionHandler: { _, _ in }
    )

    UNUserNotificationCenter.current().delegate = self
    application.registerForRemoteNotifications()

    // 핵클 SDK 초기화
    Hackle.initialize(sdkKey: YOUR_APP_SDK_KEY)
    return true
  }

  func application(
    _ application: UIApplication,
    didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
  ) {
    // 핵클 서버로 APNs 푸시 토큰 전달
    Hackle.app()?.setPushToken(deviceToken)
  }
  ...
}
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
#import <UIKit/UIKit.h>
#import <UserNotifications/UserNotifications.h>
@import Hackle;

@interface AppDelegate : UIResponder <UIApplicationDelegate, UNUserNotificationCenterDelegate>

@end
```

```objectivec
#import "AppDelegate.h"

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // iOS 앱에서 푸시 권한 요청
    UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
    [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound)
                          completionHandler:^(BOOL granted, NSError * _Nullable error) {

    }];
    center.delegate = self;
    [[UIApplication sharedApplication] registerForRemoteNotifications];

    // 핵클 SDK 초기화
    [Hackle initializeWithSdkKey:@"YOUR_APP_SDK_KEY"
                          config:[HackleConfig DEFAULT]];

    return YES;
}

- (void)application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    // 핵클 서버로 APNs 푸시 토큰 전달
    [[Hackle app] setPushToken:deviceToken];
}

```

{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

#### Display Push Messages

{% hint style="info" %}
In the background, pushes are displayed automatically without any code implementation.
{% endhint %}

{% hint style="warning" %}
You must implement the following code to display pushes in the foreground.
{% endhint %}

Add the `userNotificationCenter` method to display foreground Push Messages.

{% tabs %}
{% tab title="Swift" %}

```swift
import Hackle

extension AppDelegate: UNUserNotificationCenterDelegate {
  // Foreground push message
  func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    willPresent notification: UNNotification,
    withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions
  ) -> Void) {

    if Hackle.userNotificationCenter(
      center: center, willPresent: notification, withCompletionHandler: completionHandler
    ) {
      // Succefully processed notification
      // Automatically consumed completion handler
      return
    } else {
      // Received not hackle notification or error
      print("Do something")

      if #available(iOS 14.0, *) {
        completionHandler([.list, .banner])
      } else {
        completionHandler([.alert])
      }
    }
  }
}
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
// Foreground push message
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
       willPresentNotification:(UNNotification *)notification
         withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
    if ([Hackle userNotificationCenterWithCenter:center
                                      willPresent:notification
                             withCompletionHandler:completionHandler]) {
        // Succefully processed notification
        // Automatically consumed completion handler
        return;
    } else {
        // Received not hackle notification or error
        NSLog(@"Do something");
        completionHandler(UNNotificationPresentationOptionList | UNNotificationPresentationOptionBanner);
    }
}

```

{% endtab %}
{% endtabs %}

If the push was not sent by Hackle, false is returned.
{% endstep %}

{% step %}

#### Handle Push Click

{% hint style="danger" %}
If you do not call the push click handling function provided by Hackle, the push will not be processed correctly.

Also, the push click event will not be collected and you will not be able to use the push click rate metric.
{% endhint %}

Add the `handleNotification` method to handle push clicks.

{% tabs %}
{% tab title="Swift" %}

```swift
import Hackle

extension AppDelegate: UNUserNotificationCenterDelegate {
  // push click
  public func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    didReceive response: UNNotificationResponse,
    withCompletionHandler completionHandler: @escaping () -> Void
  ) {

    if let _ = Hackle.handleNotification(response: response) {
      // process hackle notification
    } else {
      // not hackle notification or error
      print("do something")
    }

    // handleNotification 에서 completionHandler를 호출하지 않으니
    // 핵클 푸시 여부에 관계없이 반드시 completionHandler를 호출해야 합니다.
    completionHandler()
  }
}
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
// push click
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void (^)(void))completionHandler
{
    if ([Hackle handleNotificationWithResponse:response] != nil) {
        // process hackle notification
    } else {
        // not hackle notification or error
        NSLog(@"do something");
    }
    // handleNotification 에서 completionHandler를 호출하지 않으니
    // 핵클 푸시 여부에 관계없이 반드시 completionHandler를 호출해야 합니다.
    completionHandler();
}

```

{% endtab %}
{% endtabs %}

The push click function processes in the following order:

1. Check if the push was sent by Hackle
2. Send the push click event to the Hackle server
3. *(If it is a deep link push)* Handle the deep link

If the push was not sent by Hackle, nil is returned.

**Custom Deep Link Handling for Push Click**

If you need to reprocess the link received from Hackle within the app, declare the **`handleAction` parameter as false** when calling `handleNotification`.

When the `handleAction` parameter is false:

* The Hackle SDK sends the push click event to the Hackle server.
* Deep link handling is not performed.

{% tabs %}
{% tab title="Swift" %}

```swift
import Hackle

extension AppDelegate: UNUserNotificationCenterDelegate {
  // push click
  public func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    didReceive response: UNNotificationResponse,
    withCompletionHandler completionHandler: @escaping () -> Void
  ) {

    if let notification = Hackle.handleNotification(response: response, handleAction: false) {
      // 푸시 메시지 Action Type
      print("\(notifiaction.actionType)")

      // 푸시 메시지에 등록된 link
      print("\(notifiaction.link)")
    } else {
      // not hackle notification or error
      print("do something")
    }

    // handleNotification 에서 completionHandler를 호출하지 않으니
    // 핵클 푸시 여부에 관계없이 반드시 completionHandler를 호출해야 합니다.
    completionHandler()
  }
}
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
// push click
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void (^)(void))completionHandler
{
    id notification = [Hackle handleNotificationWithResponse:response handleAction:NO];
    if (notification != nil) {
        // process hackle notification
        NSLog(@"%@", [notification valueForKey:@"actionType"]);
        NSLog(@"%@", [notification valueForKey:@"link"]);
    } else {
        // not hackle notification or error
        NSLog(@"do something");
    }
    // handleNotification 에서 completionHandler를 호출하지 않으니
    // 핵클 푸시 여부에 관계없이 반드시 completionHandler를 호출해야 합니다.
    completionHandler();
}

```

{% endtab %}
{% endtabs %}

The `actionType` for a Push Message is as follows.

<table><thead><tr><th width="150">actionType</th><th>Description</th></tr></thead><tbody><tr><td>appOpen</td><td>Open the app</td></tr><tr><td>link</td><td>Open the app and navigate to a link</td></tr></tbody></table>

When `actionType` is `appOpen`, `link` is nil.
{% endstep %}

{% step %}

#### Test Push Messages

**Check Token**

* Use the [User Identifier guide](/en/development-guide/ios/ios-user-explorer.md) to check the token configured on the iOS device.
* Use the [User Profile guide](/en/user-view/user-profile.md) to check the iOS push token assigned to a specific user.

**Test**

* Follow the [Push Message test send guide](/en/crm-marketing/push-message-guide/create-campaign.md#id-3-1) to verify Push Messages on an iOS device.
  {% endstep %}

{% step %}

#### Push Message Reception

On iOS, whether a push is received depends on the build environment.

{% hint style="info" %}
Even when the APNs Key Environment is set to `Sandbox & Production`, the range of Push Messages that can be received differs by Hackle environment and app build environment as follows.
{% endhint %}

<table><thead><tr><th width="175.78125">Hackle Environment</th><th width="175.40625">APNs Environment</th><th>Build Environment</th></tr></thead><tbody><tr><td>Development Environment,<br>Development/Production Test Push</td><td>Sandbox</td><td>Direct run from Xcode,<br>Development provisioning</td></tr><tr><td>Production Environment</td><td>Production</td><td>TestFlight, Ad Hoc,<br>App Store distribution</td></tr></tbody></table>
{% endstep %}
{% endstepper %}

## Deep Link Navigation

Hackle Push Messages support deep link navigation on click.

When the app is opened via a Push Message, you can retrieve the opened deep link information using the following configuration.

{% hint style="info" %}
For more details on iOS deep links, see the [iOS Deep Link Guide](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app).

If you have used [Custom Deep Link Handling for Push Click](#custom-deep-link-handling-for-push-click), the deep link information is not passed as shown below.
{% endhint %}

{% tabs %}
{% tab title="SwiftUI" %}

```swift
import SwiftUI

@main
struct sampleApp: App {
  ...
  var body: some Scene {
    WindowGroup {
      ContentView()
        .onOpenURL(perform: { url in
          // Handle opened url
          print("\(url.absoluteString) opened.")
        })
    }
  }
  ...
}
```

{% endtab %}

{% tab title="Storyboard" %}

```swift
class AppDelegate: NSObject, UIApplicationDelegate {
  ...
  func application(_ application: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:] ) -> Bool {
    // Handle opened url
    print("\(url.absoluteString) opened.")
  }
  ...
}
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
Deep link handling in `SwiftUI` and `Storyboard` is independent, so configure it according to your iOS app project.
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.hackle.io/en/development-guide/ios/push-message.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
