# iOS 消息送达跟踪

Pushwoosh 中有一个 [API 方法](/zh/developer/api-reference/device-api#messagedeliveryevent)可以跟踪推送通知的送达情况。iOS 应用本身不支持此方法，因为 iOS 上的推送通知由操作系统而非 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 起，推荐的集成方式是使用下文所示的 `PushwooshNotificationServiceExtension` 基类。它会自动发送消息送达事件、设置角标、下载媒体附件，并为您处理强制性的 `serviceExtensionTimeWillExpire` 回退。旧的 `PWNotificationExtensionManager` API 仍然可用但已被弃用 — 请参阅[旧版集成](#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="Xcode 目标模板选择器，已选中 Notification Service Extension"/>

3. 输入产品名称并点击 **Finish**。

<Aside type="caution">
在点击 **Finish** 后显示的对话框中，请勿选择 **Activate**。
</Aside>

4. 在 **Activate scheme** 提示中点击 **Cancel**。

<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 继续调试您的应用，而不是刚刚创建的扩展。如果您不小心激活了它，可以在 Xcode 中切换回调试您的应用。

## 为 Notification Service Extension 添加依赖项 (仅限 CocoaPods)

如果您使用 Swift Package Manager 管理依赖项，可以跳过此步骤，因为依赖项会自动添加。

打开您的 `Podfile` 并为目标添加依赖项：

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

在终端中运行以下命令来安装依赖项：

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

## 添加代码以跟踪消息送达事件

将您的扩展设为 `PushwooshNotificationServiceExtension` 的子类。一个空的子类就足够了：Pushwoosh 会自动发送消息送达事件、设置角标、下载媒体附件并处理超时回退。

替换您的 **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">
如果您不需要任何自定义代码，可以完全跳过源文件，并将扩展的 Info.plist 中的 `NSExtensionPrincipalClass` 直接指向 `PushwooshNotificationServiceExtension`。
</Aside>

### App ID

自 7.1.0 版本起，扩展会从主应用的 Info.plist 中继承 `Pushwoosh_APPID`（以及其他 `Pushwoosh_*` 键），因此您不再需要在扩展中重复设置。仅当您想覆盖主应用的值时，才将 `Pushwoosh_APPID` 添加到扩展的 Info.plist 中：

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

<Aside type="note">
在 7.1.0 之前的 Pushwoosh iOS SDK 版本中，扩展不会从主应用继承配置。在这些版本上，您必须将 `Pushwoosh_APPID` 添加到扩展的 Info.plist 中。
</Aside>

### App Group (角标和反向代理)

应用和扩展之间需要共享一个 App Group (应用组)，以便同步角标数量和读取主应用存储的反向代理设置。

1. 将 **App Groups** 功能添加到扩展目标，并启用与主应用中相同的组。这是必需的 — 如果没有共享容器，角标数量和反向代理设置将无法同步。

2. 提供 App Group 名称。与 `Pushwoosh_APPID` 类似，自 7.1.0 版本起，扩展会从主应用的 Info.plist 中继承 `PW_APP_GROUPS_NAME`，因此如果您已在主应用中为角标设置了该值，则无需再将其添加到扩展中。仅当需要覆盖主应用的值时，才在扩展的 Info.plist 中设置它，或者通过重写 `pushwooshAppGroupsName` 以编程方式提供它。

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

<Aside type="caution">
如果主应用使用反向代理 (`Pushwoosh_ALLOW_REVERSE_PROXY`)，扩展需要此 App Group 来读取应用存储在其中的代理 URL。如果没有它，送达事件将被延迟而不是直接发送，从而绕过了代理。
</Aside>

## 自定义通知 (可选)

基类提供了一些可供重写的点，控制程度由低到高。在任何情况下，Pushwoosh 仍然会执行送达事件、角标、附件和超时回退。

以编程方式设置 App Group，而不是使用 Info.plist 键：

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

在 Pushwoosh 处理推送之前运行异步准备 — 例如，预取 Push Stories 媒体 — 而无需重写标准的 `didReceive`。在主线程上调用 `completion`，且仅调用一次：

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

通过重写 `didReceive` 在显示内容前对其进行修改。使用您自己的内容处理程序调用 `super`，在其中修改内容，然后将其转发给原始处理程序：

```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)
    }
}
```

## 旧版集成

<Aside type="caution">
`PWNotificationExtensionManager` 自 7.1.0 版本起已被弃用。仅当您无法子类化 `PushwooshNotificationServiceExtension` 时才使用它 — 例如，当一个扩展已经继承了另一个 SDK 的基类，或者是一个跨平台包装器 (React Native, Flutter, Unity)。新的集成应使用上述的基类。
</Aside>

这个低级 API 从一个普通的 `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)与我们分享您的想法。