# การติดตามการส่งข้อความบน iOS

มี [API method](/th/developer/api-reference/device-api#messagedeliveryevent) ใน Pushwoosh ที่ติดตามการส่ง push notifications แอป iOS ไม่รองรับเมธอดนี้โดยตรง เนื่องจาก push notifications บน iOS ถูกจัดการโดย OS ไม่ใช่โดย Pushwoosh SDK คุณสามารถเพิ่มการติดตามการส่งได้โดยการเพิ่ม Notification Service Extension เข้าไปในโปรเจกต์ของคุณ หน้านี้จะแสดงวิธีนำการติดตามการส่งข้อความไปใช้สำหรับแอป iOS

<Aside>
ต้องใช้ Pushwoosh iOS SDK 7.x ซึ่งรองรับ iOS 13.0 และใหม่กว่า
</Aside>

<Aside type="note">
ตั้งแต่ Pushwoosh iOS SDK 7.1.0 การผสานรวมที่แนะนำคือการใช้ base class `PushwooshNotificationServiceExtension` ที่แสดงด้านล่าง ซึ่งจะส่งเหตุการณ์การส่งข้อความ, ตั้งค่า badge, ดาวน์โหลดไฟล์แนบมีเดีย และจัดการ fallback ที่จำเป็นอย่าง `serviceExtensionTimeWillExpire` ให้คุณโดยอัตโนมัติ API เก่าอย่าง `PWNotificationExtensionManager` ยังคงใช้งานได้แต่ถูกเลิกใช้แล้ว — ดูที่ [การผสานรวมแบบเก่า (Legacy)](#legacy-integration)
</Aside>

## เพิ่ม Notification Service Extension

1. ใน Xcode เลือก **File** > **New** > **Target...**

2. เลือก **Notification Service Extension** และกด **Next**

<img src="/ios-push-notifications-ios-message-delivery-tracking-1.webp" alt="หน้าจอเลือก template target ของ Xcode โดยเลือก Notification Service Extension"/>

3. ป้อนชื่อผลิตภัณฑ์และกด **Finish**

<Aside type="caution">
อย่าเลือก **Activate** ในกล่องโต้ตอบที่แสดงขึ้นมาหลังจากกด **Finish**
</Aside>

4. กด **Cancel** ที่หน้าต่าง **Activate scheme**

<img
  src="/ios-push-notifications-ios-message-delivery-tracking-2.webp"
  alt="หน้าต่าง Activate scheme โดยมีการไฮไลท์ที่ปุ่ม Cancel"
  style={{ display: "block", margin: "0 auto", maxWidth: "40%", height: "auto" }}
  width="400"
/>

การกดยกเลิกจะทำให้ Xcode ยังคงดีบักแอปของคุณแทนที่จะเป็น extension ที่คุณเพิ่งสร้างขึ้น หากคุณเผลอกด activate ไป คุณสามารถสลับกลับไปดีบักแอปของคุณได้ภายใน Xcode

## Dependencies สำหรับ Notification Service Extension (สำหรับ CocoaPods เท่านั้น)

หากคุณใช้ Swift Package Manager ในการจัดการ dependencies คุณสามารถข้ามขั้นตอนนี้ไปได้ เนื่องจาก dependencies จะถูกเพิ่มโดยอัตโนมัติ

เปิดไฟล์ `Podfile` ของคุณและเพิ่ม dependency สำหรับ target:

```ruby title="Podfile"
target 'NotificationServiceExtension' do
  use_frameworks!
  pod 'PushwooshXCFramework'
end
```

รันคำสั่งต่อไปนี้ใน terminal เพื่อติดตั้ง dependencies:

```shell
rm -rf Podfile.lock
pod deintegrate
pod setup
pod repo update
pod install
```

## เพิ่มโค้ดสำหรับติดตามเหตุการณ์การส่งข้อความ

ทำให้ extension ของคุณเป็น subclass ของ `PushwooshNotificationServiceExtension` แค่ subclass ว่างๆ ก็เพียงพอแล้ว: Pushwoosh จะส่งเหตุการณ์การส่งข้อความ, ตั้งค่า badge, ดาวน์โหลดไฟล์แนบมีเดีย และจัดการ timeout fallback โดยอัตโนมัติ

แทนที่เนื้อหาที่สร้างขึ้นในไฟล์ **NotificationService** ของคุณ:

<Tabs>
<TabItem label="Swift">

```swift
import UserNotifications
import PushwooshFramework

class NotificationService: PushwooshNotificationServiceExtension {}
```

</TabItem>

<TabItem label="Objective-C">

```objective-c
#import <PushwooshFramework/PushwooshNotificationServiceExtension.h>

@interface NotificationService : PushwooshNotificationServiceExtension

@end

@implementation NotificationService

@end
```

</TabItem>
</Tabs>

<Aside type="tip">
หากคุณไม่ต้องการโค้ดที่กำหนดเองใดๆ คุณสามารถข้ามไฟล์ source ไปทั้งหมดและชี้ `NSExtensionPrincipalClass` ใน Info.plist ของ extension ไปที่ `PushwooshNotificationServiceExtension` ได้โดยตรง
</Aside>

### App ID

ตั้งแต่เวอร์ชัน 7.1.0 เป็นต้นไป extension จะสืบทอด `Pushwoosh_APPID` (และคีย์ `Pushwoosh_*` อื่นๆ) จาก Info.plist ของแอปโฮสต์ ดังนั้นคุณไม่จำเป็นต้องทำซ้ำใน extension อีกต่อไป เพิ่ม `Pushwoosh_APPID` ใน Info.plist ของ extension เฉพาะเมื่อคุณต้องการแทนที่ค่าของโฮสต์:

```xml title="NotificationService/Info.plist"
<key>Pushwoosh_APPID</key>
<string>XXXXX-XXXXX</string>
```

<Aside type="note">
ใน Pushwoosh iOS SDK เวอร์ชันก่อน 7.1.0 extension จะไม่สืบทอดการกำหนดค่าจากแอปโฮสต์ ในเวอร์ชันเหล่านั้น คุณต้องเพิ่ม `Pushwoosh_APPID` ใน Info.plist ของ extension
</Aside>

### App Group (สำหรับ badge และ reverse proxy)

จำเป็นต้องมี App Group ที่ใช้ร่วมกันระหว่างแอปและ extension เพื่อซิงค์จำนวน badge และเพื่ออ่านการตั้งค่า reverse-proxy ที่แอปโฮสต์จัดเก็บไว้

1. เพิ่มความสามารถ **App Groups** ให้กับ target ของ extension และเปิดใช้งาน group เดียวกันกับในแอปโฮสต์ นี่เป็นสิ่งจำเป็น — หากไม่มี container ที่ใช้ร่วมกัน จำนวน badge และการตั้งค่า reverse-proxy จะไม่สามารถซิงค์ได้

2. ระบุชื่อ App Group เช่นเดียวกับ `Pushwoosh_APPID` extension จะสืบทอด `PW_APP_GROUPS_NAME` จาก Info.plist ของแอปโฮสต์ตั้งแต่เวอร์ชัน 7.1.0 ดังนั้นหากคุณได้ตั้งค่าไว้ที่นั่นสำหรับ badges แล้ว คุณไม่จำเป็นต้องเพิ่มใน extension อีก ตั้งค่าใน Info.plist ของ extension เฉพาะเพื่อแทนที่ค่าของโฮสต์ หรือระบุผ่านโปรแกรมโดยการ override `pushwooshAppGroupsName`

```xml title="App Info.plist"
<key>PW_APP_GROUPS_NAME</key>
<string>group.com.example.app</string>
```

<Aside type="caution">
หากแอปโฮสต์ใช้ reverse proxy (`Pushwoosh_ALLOW_REVERSE_PROXY`) extension จะต้องใช้ App Group นี้เพื่ออ่าน URL ของ proxy ที่แอปจัดเก็บไว้ หากไม่มี App Group นี้ เหตุการณ์การส่งจะถูกระงับไว้แทนที่จะส่งโดยตรง ซึ่งจะข้าม proxy ไป
</Aside>

## ปรับแต่งการแจ้งเตือน (ทางเลือก)

base class มีจุดให้ override อยู่สองสามจุด ตั้งแต่การควบคุมน้อยที่สุดไปจนถึงมากที่สุด Pushwoosh ยังคงรันเหตุการณ์การส่ง, badge, ไฟล์แนบ และ timeout fallback ในทุกกรณี

ตั้งค่า App Group ผ่านโปรแกรมแทนการใช้คีย์ใน Info.plist:

```swift
override func pushwooshAppGroupsName() -> String? {
    "group.com.example.app"
}
```

รันการเตรียมการแบบ asynchronous ก่อนที่ Pushwoosh จะประมวลผล push — ตัวอย่างเช่น การ prefetch มีเดียของ Push Stories — โดยไม่ต้อง override `didReceive` มาตรฐาน เรียก `completion` เพียงครั้งเดียวบน main thread:

```swift
override func pushwooshPrepare(for request: UNNotificationRequest,
                              completion: @escaping () -> Void) {
    // async work here
    completion()
}
```

แก้ไขเนื้อหาก่อนที่จะแสดงโดยการ override `didReceive` เรียก `super` ด้วย content handler ของคุณเอง, เปลี่ยนแปลงเนื้อหาภายในนั้น, แล้วส่งต่อไปยัง handler เดิม:

```swift
override func didReceive(_ request: UNNotificationRequest,
                         withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
    super.didReceive(request) { content in
        let mutable = (content.mutableCopy() as? UNMutableNotificationContent) ?? content
        // customize `mutable` here
        contentHandler(mutable)
    }
}
```

## การผสานรวมแบบเก่า (Legacy)

<Aside type="caution">
`PWNotificationExtensionManager` ถูกเลิกใช้แล้วตั้งแต่เวอร์ชัน 7.1.0 ใช้เฉพาะในกรณีที่คุณไม่สามารถ subclass `PushwooshNotificationServiceExtension` ได้ — ตัวอย่างเช่น extension ที่ขยาย base class ของ SDK อื่นอยู่แล้ว หรือ wrapper ข้ามแพลตฟอร์ม (React Native, Flutter, Unity) การผสานรวมใหม่ควรใช้ base class ที่กล่าวถึงข้างต้น
</Aside>

API ระดับต่ำนี้ขับเคลื่อนการประมวลผลแบบเดียวกัน (เหตุการณ์การส่ง, badge, ไฟล์แนบ) จาก `UNNotificationServiceExtension` ทั่วไป:

<Tabs>
<TabItem label="Swift">

```swift
import UserNotifications
import PushwooshFramework

class NotificationService: UNNotificationServiceExtension {

    override func didReceive(_ request: UNNotificationRequest,
                             withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        PWNotificationExtensionManager.sharedManager()
            .handleNotificationRequest(request, contentHandler: contentHandler)
    }
}
```

</TabItem>

<TabItem label="Objective-C">

```objective-c
#import "PWNotificationExtensionManager.h"

@interface NotificationService : UNNotificationServiceExtension

@end

@implementation NotificationService

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request
                   withContentHandler:(void (^)(UNNotificationContent *))contentHandler {
    [[PWNotificationExtensionManager sharedManager] handleNotificationRequest:request
                                                              contentHandler:contentHandler];
}

@end
```

</TabItem>
</Tabs>

## แบ่งปันความคิดเห็นของคุณกับเรา

ความคิดเห็นของคุณช่วยให้เราสร้างประสบการณ์ที่ดีขึ้น ดังนั้นเรายินดีที่จะรับฟังจากคุณหากคุณมีปัญหาใดๆ ในระหว่างกระบวนการผสานรวม SDK หากคุณประสบปัญหาใดๆ โปรดอย่าลังเลที่จะแบ่งปันความคิดเห็นของคุณกับเรา [ผ่านแบบฟอร์มนี้](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform)