Skip to content

Commit bc2455c

Browse files
docs: Update integration guide with auth handler and push permission fixes
- Auth handler must read email fresh from source of truth on every onAuthTokenRequested() call. Caching at startup causes AUTH_TOKEN_NULL. - Added full runtime POST_NOTIFICATIONS permission code for Android 13+. Without it, push silently fails. - Updated traps list from 10 to 12 with both new issues. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent da5ed66 commit bc2455c

1 file changed

Lines changed: 47 additions & 11 deletions

File tree

docs/ai/integration-guide.md

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ class YourApplication : Application() {
9797
.setCustomActionHandler(AppCustomActionHandler())
9898
.setUrlHandler(AppUrlHandler(this))
9999
.setAllowedProtocols(arrayOf("yourscheme")) // REQUIRED for custom deep link schemes
100-
.setAuthHandler(AppAuthHandler()) // REQUIRED if JWT key
100+
.setAuthHandler(AppAuthHandler { getFreshEmail() }) // REQUIRED if JWT key — see note below
101101
.setEnableEmbeddedMessaging(true) // Only if using embedded messages
102102
.build()
103103

@@ -117,6 +117,9 @@ class YourApplication : Application() {
117117
### Gotcha: Do NOT call setEmail in the init callback
118118
The SDK's `IterableAuthManager` has internal retry limits. Calling `setEmail` too early consumes the retry budget, causing `AUTH_TOKEN_MISSING` for all subsequent calls in the same process.
119119

120+
### Gotcha: Auth handler must read the email FRESH every time
121+
The SDK calls `onAuthTokenRequested()` at unpredictable times (token refresh, retries, etc.). The lambda or function that provides the email to the auth handler must **read from the source of truth every time** (e.g. DataStore, SharedPreferences, database). Do NOT cache the email in a variable at startup and pass that — the cached value will be empty or stale if the user logs in after app launch, causing `AUTH_TOKEN_NULL`.
122+
120123
---
121124

122125
## Step 3: User Identification (Login/Email Screen)
@@ -253,7 +256,38 @@ class AppFirebaseMessagingService : IterableFirebaseMessagingService() {
253256
```
254257

255258
### Runtime permission (Android 13+)
256-
Request `POST_NOTIFICATIONS` in the app's main activity.
259+
260+
> **Agent note:** This is REQUIRED for push notifications to work on Android 13+ (API 33+). Without it, the system silently blocks all notifications. Add this to the app's main activity (or wherever the app first loads). Adapt it to the app's existing permission-handling pattern if one exists.
261+
262+
```kotlin
263+
// In your main activity:
264+
265+
private val notificationPermissionLauncher = registerForActivityResult(
266+
ActivityResultContracts.RequestPermission()
267+
) { granted ->
268+
Log.d("Iterable", "Notification permission granted: $granted")
269+
}
270+
271+
// Call this in onCreate():
272+
private fun requestNotificationPermission() {
273+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
274+
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
275+
!= PackageManager.PERMISSION_GRANTED
276+
) {
277+
notificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
278+
}
279+
}
280+
}
281+
```
282+
283+
Required imports:
284+
```kotlin
285+
import android.Manifest
286+
import android.content.pm.PackageManager
287+
import android.os.Build
288+
import androidx.activity.result.contract.ActivityResultContracts
289+
import androidx.core.content.ContextCompat
290+
```
257291

258292
### Dashboard setup (user must do this)
259293
- Upload Firebase service account JSON (NOT the deprecated Server Key)
@@ -402,15 +436,17 @@ Before testing ANY channel, the user must configure these in Iterable:
402436

403437
---
404438

405-
## Top 10 Traps (ordered by severity)
439+
## Top 12 Traps (ordered by severity)
406440

407441
1. **JWT-required key with no auth handler** → ALL SDK calls silently fail, zero errors
408442
2. **JWT secret encoding** → use raw UTF-8, not hex-decode, not base64-decode
409-
3. **Calling setEmail in Application.onCreate init callback** → poisons SDK auth retry state
410-
4. **Custom URL schemes silently blocked** → must use `setAllowedProtocols`
411-
5. **`onTokenRegistrationSuccessful` is local, not server** → don't trust it
412-
6. **Firing setEmail + updateUser + registerForPush simultaneously** → auth race condition. Use `setEmail(email, onSuccess, onFailure)` and chain `updateUser` in `onSuccess`
413-
7. **Accessing embeddedManager before SDK init** → crash
414-
8. **Placement IDs are auto-generated** → never hardcode assumed values
415-
9. **Message Types must exist before templates** → dashboard prerequisite for every channel
416-
10. **Don't hallucinate Iterable dashboard paths** → defer to user or official docs
443+
3. **Caching email at startup for the auth handler**`AUTH_TOKEN_NULL`. The auth handler lambda must read the email fresh from the source of truth (DataStore, SharedPreferences, etc.) every time `onAuthTokenRequested()` is called
444+
4. **Calling setEmail in Application.onCreate init callback** → poisons SDK auth retry state
445+
5. **Missing runtime POST_NOTIFICATIONS permission** → push silently fails on Android 13+. Must use `ActivityResultContracts.RequestPermission()` in the main activity
446+
6. **Custom URL schemes silently blocked** → must use `setAllowedProtocols`
447+
7. **`onTokenRegistrationSuccessful` is local, not server** → don't trust it
448+
8. **Firing setEmail + updateUser + registerForPush simultaneously** → auth race condition. Use `setEmail(email, onSuccess, onFailure)` and chain `updateUser` in `onSuccess`
449+
9. **Accessing embeddedManager before SDK init** → crash
450+
10. **Placement IDs are auto-generated** → never hardcode assumed values
451+
11. **Message Types must exist before templates** → dashboard prerequisite for every channel
452+
12. **Don't hallucinate Iterable dashboard paths** → defer to user or official docs

0 commit comments

Comments
 (0)