# iOS 푸시 메시지 이미지 첨부

{% hint style="info" %}
Flutter SDK 2.23.0 버전 이상에서 지원하는 기능입니다.
{% endhint %}

iOS 앱에서 이미지를 포함한 푸시 메시지를 보여주기 위해서는 [Notification Service Extension](https://developer.apple.com/documentation/usernotifications/unnotificationserviceextension)을 추가하여 아래의 설정을 완료합니다.

iOS Rich Push Notification 에 대한 자세한 사항은 [Rich Push Notification](https://developer.apple.com/documentation/usernotificationsui/customizing-the-appearance-of-notifications) 에서 확인 가능합니다.

{% stepper %}
{% step %}
**앱에 Notification Service Extension 추가**

Xcode로 `/ios/Runner.xcworkspace` 파일을 열어 프로젝트를 연 후 프로젝트 상단 `File > New > Target...` 탭을 선택하여 아래와 같이 `Notification Service Extension`을 선택합니다.

![](/files/Zrae7mBVGIdvbe9aLbCw)

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

![](/files/OkrfEN5gsIdfMvpT6y7I)

{% tabs %}
{% tab title="Swift Package Manager 설정" %}
Flutter에서 Swift Package Manager를 이용한 경우 `Extension`에 `Hackle` 프레임워크를 추가합니다.

![](/files/zcIt6Tl3w0K0Xrq2vrfJ)

![](/files/Ph1I5Ez2QXsV4rj5EXxo)

![](/files/5vYIWC7r6LP7OEAOTY3A)
{% endtab %}

{% tab title="CocoaPods 설정" %}
`Podfile`에 앞서 추가한 `Extension` 에 핵클 종속성을 추가해야 합니다.

아래와 같이 `Hackle` 종속성을 추가합니다.

```ruby
target 'NotificationServiceExtension' do
  pod 'Hackle'
end
```

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

{% step %}
**Minimum Deployment 설정**

{% hint style="danger" %}
최소 지원 버전이 앱과 extension이 다를 경우, 앱 버전에 따라 이미지가 표시되지 않을 수 있습니다.

ex) App 최소 지원 버전이 iOS 15, Extension 최소 지원 버전이 iOS 18인 경우

* iOS 15 이상, iOS 18 미만 버전은 이미지가 표시되지 않습니다.
* iOS 18 이상 버전은 이미지가 표시됩니다.
  {% endhint %}

Notification Service Extension은 앱과 별도로 최소 지원버전을 명시해야 합니다.

**`Minimum Deployment`를 앱과 동일하게 설정하는 것을 추천합니다**.

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

{% step %}
**핵클 SDK와 연동하기**

**푸시 메시지에 이미지 추가**

푸시 이미지 처리를 위해 `didReceive` 함수에서 `handleRichNotification` 함수를 추가합니다.

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

```swift
import UserNotifications
import Hackle

class NotificationService: UNNotificationServiceExtension {

    var defaultNotificationContent: UNNotificationContent?
    var contentHandler: ((UNNotificationContent) -> Void)?

    override func didReceive(
      _ request: UNNotificationRequest,
      withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
    ) {
        self.defaultNotificationContent = request.content
        self.contentHandler = contentHandler

        if Hackle.handleRichNotification(request: request, contentHandler: contentHandler) {
            return
        }

        contentHandler(request.content)
    }

    override func serviceExtensionTimeWillExpire() {
      if let contentHandler = contentHandler,
         let defaultNotificationContent = defaultNotificationContent {
            contentHandler(defaultNotificationContent)
        }
    }
}
```

{% endtab %}

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

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

@interface NotificationService : UNNotificationServiceExtension

@end
```

```objectivec
#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 *))contentHandler {
    self.defaultNotificationContent = request.content;
    self.contentHandler = contentHandler;

    if ([Hackle handleRichNotificationWithRequest:request contentHandler:contentHandler]) {
        return;
    }
    contentHandler(request.content);
}

- (void)serviceExtensionTimeWillExpire {
    self.contentHandler(self.bestAttemptContent);
}

@end
```

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


---

# 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/development-guide/flutter/push-message/flutter-ios-rich-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.
