if WarplySDK.shared.checkForLoyaltySDKNotification(userInfo) {
print("Warply notification handled")
} else {
// Handle other notifications
### Handle Pending Notifications
When the app is launched from a push notification (cold start), call this in your root view controller:
```swift
class MainViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Handle any pending push notification from cold start
WarplySDK.shared.handlePendingNotification()
}
}
```
### Handle Notifications
### Optional — Clear Badge on App Open
The **badge** is the small red number circle on your app icon (e.g., showing "3" for 3 unread notifications). Call `resetBadge()` to clear it when the user opens the app:
By default, the SDK handles all Warply push notifications automatically (shows a campaign WebView). However, if the Warply backend sends pushes with a **custom action code** (`action != 0`), you can implement a handler to process them:
> **Note**: You only need this if the Warply/DEI backend team has told you they send custom action pushes. For standard offer notifications, the SDK handles everything automatically.
- ✅ Accept device tokens from the host app and send them to the Warply server
- ✅ Detect Warply push notifications from payload
- ✅ Process and route Warply notifications
- ✅ **Present rich push content via `CampaignViewController` (WebView)** — consistent with old `WLPushManager.showItem:`
- ✅ Provide convenience wrappers for `UNUserNotificationCenterDelegate` methods (host app just forwards)
- ✅ Provide a delegate/protocol for custom push handling
- ✅ Send push analytics events
- ✅ Send comprehensive device info to server
- ✅ Handle pending notifications
- ✅ Provide badge management utilities
This is the modern iOS best practice: the framework is a **helper**, not the **owner** of push notification registration.
### Why Option A (Host App Forwards) Over Option B (SDK is Delegate)
The old ObjC `WLPushManager` set itself as `UNUserNotificationCenter.delegate`. This created conflicts when the host app also had its own delegate (Firebase, OneSignal, etc.) — only one delegate can exist at a time.
**Option A approach**: The host app remains the delegate and forwards calls to the SDK:
```swift
// Host app's AppDelegate — just forward these 2 calls to the SDK:
-[] Rich push: Campaign URL correctly built as `{baseURL}/api/session/{session_uuid}`
-[] Rich push: Alert shown when push received in active (foreground) state
-[] Rich push: WebView shown immediately when push received in background state
-[] Rich push: Pending notification stored and shown via `handlePendingNotification()` for closed state
-[]`willPresentNotification()` correctly handles Warply vs non-Warply pushes
-[]`didReceiveNotificationResponse()` routes notification and logs `NB_PushAck`
-[] No compilation errors introduced
-[] All existing public API remains backward compatible
---
## ⚠️ Important Notes
1.**`"_a"` is the Warply Push Identifier** — The old ObjC code uses `[userInfo valueForKey:@"_a"]` to detect Warply pushes. The current Swift stub incorrectly checks for `"loyalty_sdk"` and `"warply"` keys.
2.**The framework should NOT be UNUserNotificationCenterDelegate** — In the old ObjC code, WLPushManager set itself as `UNUserNotificationCenter.delegate`. In modern iOS, the host app owns this. The framework should only provide helper methods.
3.**Device info sending uses the event system** — In old ObjC, device info was sent via `WLEventSimple` (type: `"device_info"`) through `addEvent:priority:`. In Swift, we use the existing `Endpoint.sendDeviceInfo` which POSTs to `/api/async/info/{appUUID}/`.
4.**Rich push WebView handled by framework** — The old `showItem:` presented a `WLInboxItemViewController` WebView for rich pushes. The modern framework maintains this behavior by reusing the existing `CampaignViewController` — for `action == 0` pushes, the framework builds a campaign URL from `session_uuid` (`{baseURL}/api/session/{session_uuid}`) and presents `CampaignViewController` modally. This ensures full consistency with the old implementation.
5.**Keychain vs UserDefaults for device token** — Old code used `WLKeychain`. New code uses `UserDefaults`. Device tokens are not sensitive (they're sent to servers in plain text), so `UserDefaults` is acceptable. However, we could optionally use `KeychainManager` for consistency.
---
## 📊 Estimated Effort
| Phase | Estimated Time | Complexity |
|-------|---------------|------------|
| Phase 1: Core Infrastructure | 30 min | Medium |
| Phase 2: Device Token Management | 45 min | Medium |
The framework does **NOT** set itself as `UNUserNotificationCenterDelegate`. Instead, the host app owns the delegate and forwards calls to the SDK. This avoids conflicts with Firebase, OneSignal, etc.