푸시 메시지 연동

📘

타 푸시 솔루션과 함께 사용할 수 있습니다

타 푸시 솔루션과 함께 사용하려면 타 푸시 솔루션의 Swizzling 옵션을 비활성화해야 합니다. Swizzling 비활성화 후, 해당 솔루션의 가이드를 참고하여 푸시 알림 처리를 수동으로 설정해 주세요.

APNs 설정하기

iOS 앱에서 푸시 메시지를 사용하기 위해서는 핵클 워크스페이스와 APNs 연동 설정이 필요합니다. 자세한 내용은 Apple Push Notification Service 설정을 참고하세요.

핵클 SDK와 연동하기

APNs 토큰 전달

핵클에서 iOS 앱이 설치된 기기에 푸시 메시지를 전달할수 있도록 아래의 설정을 완료합니다.

class AppDelegate: NSObject, UIApplicationDelegate {
  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
    return true
  }
}
#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate>

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

만약 SwiftUI의 경우 아래와 같이 AppDelegateSwiftUI에 등록해 주세요.

import SwiftUI

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

AppDelegate에 아래과 같이 함수를 추가합니다.

import Hackle

class AppDelegate: NSObject, UIApplicationDelegate {
  ...
  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
    UNUserNotificationCenter.current().requestAuthorization(
      options: authOptions,
      completionHandler: { _, _ in }
    )
    UNUserNotificationCenter.current().delegate = self
    application.registerForRemoteNotifications()
    
    Hackle.initialize(sdkKey: YOUR_APP_SDK_KEY)
    return true
  }
  
  func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    Hackle.app()?.setPushToken(deviceToken)
  }
  ...
}
#import <UIKit/UIKit.h>
#import <UserNotifications/UserNotifications.h>
@import Hackle;

@interface AppDelegate : UIResponder <UIApplicationDelegate, UNUserNotificationCenterDelegate>

@end
#import "AppDelegate.h"
  
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
  [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound) completionHandler:^(BOOL granted, NSError * _Nullable error) {}];
  center.delegate = self;
  [[UIApplication sharedApplication] registerForRemoteNotifications];
  
  [Hackle initializeWithSdkKey:@"YOUR_APP_SDK_KEY" config:[HackleConfig DEFAULT]];
  return YES;
}

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
  [[Hackle app] setPushToken:deviceToken];
}

푸시 메시지 표시

Xcode 프로젝트 설정의 Signing & Capabilities 탭에서 + Capability를 아래와 같이 클릭해주세요.

Push Notifications 를 추가해주세요.

핵클에서 보낸 푸시 메시지를 표시하기 위해서는 아래와 같이 설정해 주세요.

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])
      }
    }
  }
  
  // Converted push message
  public func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    if Hackle.userNotificationCenter(center: center, didReceive: response, withCompletionHandler: completionHandler) {
      // Automatically consumed completion handler
      return
    } else {
      // Received non hackle notification or error
      print("Do something")
      completionHandler()
    }
  }
}
// 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);
  } 
}

// Converted push message
-(void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler {
  if ([Hackle userNotificationCenterWithCenter:center didReceive:response withCompletionHandler:completionHandler]) {
    // Automatically consumed completion handler
    return;
  } else {
    // Received non hackle notification or error
    NSLog(@"Do something");
    completionHandler();
  }
}

푸시 메시지 테스트

토큰 확인

사용자 식별자 확인하기 가이드 를 통해 iOS 기기에 설정된 토큰을 확인합니다.

테스트

푸시 메시지 테스트 발송 가이드 를 참고하여 푸시 메시지를 iOS 기기에서 확인합니다.

(Advanced) 딥링크 이동

핵클 푸시 메시지는 클릭 시 딥링크 이동을 지원합니다. 푸시 메시지를 통해 해당 앱이 열리는 경우 아래의 설정을 통해 열린 딥링크 정보를 확인할수 있습니다.

iOS 딥링크에 대한 자세한 사항은 iOS 딥링크 가이드 에서 확인 가능합니다.

import SwiftUI

@main
struct sampleApp: App {
  ...
  var body: some Scene {
    WindowGroup {
      ContentView()
        .onOpenURL(perform: { url in
          // Handle opened url
          print("\(url.absoluteString) opened.")
        })
    }
  }
  ...
}
class AppDelegate: NSObject, UIApplicationDelegate {
  ...
  func application(_ application: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:] ) -> Bool {
    // Handle opened url
    print("\(url.absoluteString) opened.")
  }
  ...
}

🚧

SwiftUIStoryboard에서의 딥링크 처리는 각각 독립적이기 때문 해당 iOS 앱 프로젝트에 맞도록 설정해 주세요

(Advanced) 이미지를 포함한 푸시 메시지 표시 (Rich Push Notification)

iOS 앱에서 이미지를 포함한 푸시 메시지를 보여주기 위해서는 Notification Service Extension을 추가하여 아래의 설정을 완료합니다.

iOS Rich Push Notification 에 대한 자세한 사항은 Rich Push Notification 에서 확인 가능합니다.

Xcode 설정

Xcode 프로젝트 상단 File > New > Target... 탭을 선택하여 아래와 같이 Notification Service Extension을 선택합니다.

알맞은 이름을 입력 후 Finish를 눌러주세요.

CocoaPods 설정

Podfile에 앞서 추가한 Extension을 다음과 같이 구성합니다.

use_frameworks!

target 'sampleapp' do
  pod 'Hackle', '~> 2.28.0'
end

target 'NotificationServiceExtension' do
  pod 'Hackle', '~> 2.28.0'
end

Swift Package Manager 설정

추가한 ExtensionHackle 프레임워크를 추가합니다.

푸시 메시지 표시

import UserNotifications
import Hackle

class NotificationService: UNNotificationServiceExtension {
  var contentHandler: ((UNNotificationContent) -> Void)?
  var bestAttemptContent: UNMutableNotificationContent?
  
  override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
    self.contentHandler = contentHandler
    self.bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

    if (Hackle.populateNotificationContent(request: request, withContentHandler: contentHandler)) {
      // Succefully processed notification
      // Automatically consumed content handler
      return
    } else {
      // Received non hackle notification or error
      if let bestAttemptContent = bestAttemptContent {
        // Modify the notification content here...
        bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"
        contentHandler(bestAttemptContent)
      }
    }
  }
  
  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 = self.contentHandler, let bestAttemptContent = self.bestAttemptContent {
      contentHandler(bestAttemptContent)
    }
  }
}
#import <UserNotifications/UserNotifications.h>
@import Hackle;

@interface NotificationService : UNNotificationServiceExtension

@end
#import "NotificationService.h"

@interface NotificationService ()

@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@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];
    
  if ([Hackle populateNotificationContentWithRequest:request withContentHandler:contentHandler]) {
    // Succefully processed notification
    // Automatically consumed content handler
    return;
  } else {
    // Received non hackle notification or error
    self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [modified]", self.bestAttemptContent.title];
    self.contentHandler(self.bestAttemptContent);
  }
}

- (void)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.
  self.contentHandler(self.bestAttemptContent);
}

@end

(Advanced) 푸시 수신 동의 설정

푸시 수신 동의 여부(HacklePushSubscriptionStatus)를 사용자로부터 받아 서버에 등록합니다.

이전에 푸시 수신 동의 여부를 설정하지 않은 경우(unknown) 또는 updatePushSubscriptionStatus API를 호출하여 푸시 수신 동의(subscribed)로 설정한 경우에는 푸시가 발송됩니다.

명시적으로 updatePushSubscriptionStatus API를 호출하여 푸시 수신 안함(unsubscribed)으로 설정한 경우에만 푸시가 발송되지 않습니다.

푸시 수신 동의 설정

Hackle.app()?.updatePushSubscriptionStatus(status: .subscribed)
[[Hackle app] updatePushSubscriptionStatus:HacklePushSubscriptionStatusSubscribed];

HacklePushSubscriptionStatus

Type설명
subscribed푸시 수신 동의
unsubscribed푸시 수신 안함
unknown알 수 없음