> ## Documentation Index
> Fetch the complete documentation index at: https://docs.evomarketing.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Push notifications

> iOS push notification handling and routing

The iOS app handles push notifications via APNs with rich notification support and deep link routing.

## Architecture

```
┌─────────────────────────────────────────────┐
│                   APNs                       │
└─────────────────┬───────────────────────────┘
                  │
    ┌─────────────┴─────────────┐
    │                           │
    ▼                           ▼
┌─────────────────┐   ┌─────────────────────────┐
│ Notification    │   │ Main App                │
│ Service Ext     │   │ (foreground delivery)   │
│ (background)    │   │                         │
└────────┬────────┘   └────────────┬────────────┘
         │                         │
         ▼                         ▼
┌─────────────────┐   ┌─────────────────────────┐
│ Rich content    │   │ PushManager             │
│ (avatar, image) │   │ (routing, handling)     │
└─────────────────┘   └─────────────────────────┘
```

## Key files

| File                                                  | Purpose                        |
| ----------------------------------------------------- | ------------------------------ |
| `Utils/Push/PushManager.swift`                        | Main push handling             |
| `DialedNotificationService/NotificationService.swift` | Service extension              |
| `dialedShared/`                                       | Shared notification processing |

## PushManager

**File:** `Utils/Push/PushManager.swift`

```swift theme={null}
@MainActor
final class PushManager: NSObject, ObservableObject {
    static let shared = PushManager()
    @Published var pendingRoute: PushRoute?
}
```

Responsibilities:

* Device token registration
* Notification permission handling
* Push payload parsing
* Route extraction
* Pending route management

## Push routes

Routes map notification targets to in-app destinations:

```swift theme={null}
enum PushRoute: Equatable {
    case dashboard
    case communityLeaderboards
    case campaigns
    case community
    case communityChat(slug: String?)
    case brandHub(publicId: String, tab: PushRouteBrandHubTab)
    case support(ticketId: String?, compose: Bool)
    case creativeApproval(id: String)
    case brandDealDetail(id: String)
    // ... more routes
}
```

### Route parsing

```swift theme={null}
// From target_url
PushRoute.parse("/brands/abc123/hub")
// → .brandHub(publicId: "abc123", tab: .overview)

// From universal link
PushRoute.fromUniversalLink(URL(string: "https://dialed.evo.co/brands/123")!)

// From push payload
PushRoute.fromPushData(notification.userInfo)
```

### Kind overrides

Some notification kinds have special routing:

| Kind                 | Route              |
| -------------------- | ------------------ |
| `posting_behind`     | Brand hub overview |
| `weekly_report_sent` | Brand hub reports  |
| `early_viral_alert`  | Brand hub overview |
| `creative_approved`  | Creative detail    |

## Notification service extension

**Target:** `DialedNotificationService`

Enriches notifications before display:

### Communication style

For chat DMs, uses `INSendMessageIntent`:

```swift theme={null}
let intent = INSendMessageIntent(
    recipients: nil,
    outgoingMessageType: .outgoingMessageText,
    content: body,
    speakableGroupName: nil,
    conversationIdentifier: channelId,
    serviceName: nil,
    sender: sender
)
```

This enables:

* Sender avatar in notification
* Communication notification style
* Notification grouping

### Attachment style

For media notifications:

* Downloads image/avatar JPEGs
* Attaches to notification
* Shows rich preview

## Token registration

```swift theme={null}
func registerDeviceToken(_ token: Data) async {
    let tokenString = token.map { String(format: "%02x", $0) }.joined()
    try await api.post("/api/creator/device_tokens", body: [
        "token": tokenString,
        "platform": "ios"
    ])
}
```

## Handling flow

1. **Notification received** (background or foreground)
2. **Service extension** enriches if applicable
3. **User taps notification**
4. **PushManager** extracts route from payload
5. **Sets `pendingRoute`** published property
6. **Shell view** observes and navigates

```swift theme={null}
// In shell view
.onChange(of: PushManager.shared.pendingRoute) { route in
    if let route = route {
        consumePushRoute(route)
        PushManager.shared.pendingRoute = nil
    }
}
```

## Notification kinds

Common notification kinds and their handling:

| Kind                      | Description          | Route           |
| ------------------------- | -------------------- | --------------- |
| `early_viral_alert`       | Post going viral     | Brand hub       |
| `creative_approved`       | Creative approved    | Creative detail |
| `creative_needs_revision` | Revision requested   | Creative detail |
| `chat_message`            | New DM               | Chat channel    |
| `chat_mention`            | Mentioned in chat    | Chat channel    |
| `pipeline_status_changed` | Brand status changed | Brand hub       |
| `brand_deal_published`    | New brand deal       | Brand deals     |
| `leaderboard_top_3`       | Made top 3           | Leaderboards    |

## Testing push

Use Xcode's push notification simulator:

1. Create `.apns` file with payload
2. Drag onto simulator
3. Or use `xcrun simctl push` command

```json theme={null}
{
  "aps": {
    "alert": {
      "title": "Test",
      "body": "Test notification"
    }
  },
  "target_url": "/brands/123/hub",
  "kind": "early_viral_alert"
}
```

## Troubleshooting

| Issue           | Check                       |
| --------------- | --------------------------- |
| Not receiving   | Device token registered?    |
| Wrong route     | Verify `target_url` format  |
| No rich content | Service extension enabled?  |
| Duplicate       | Check dedup\_key in payload |
