feat: iOS push notifications (APNs via push gateway) (#4)#44
Merged
Conversation
Registers the device's APNs token with the backend so new-episode pushes
can be delivered, and routes a tapped notification to the player. Pure
platform-channel integration (no Firebase), modeled on the proven
Cantinarr pattern.
Native (AppDelegate): sets the UNUserNotificationCenter delegate, exposes
a `nullfeed/push` method channel over the implicit engine's messenger,
hex-encodes the device token and forwards it as `onApnsToken`, presents
foreground notifications, and forwards taps as `onNotificationTap`
(caching a cold-start tap until Dart pulls it). Adds Runner.entitlements
(aps-environment) and wires CODE_SIGN_ENTITLEMENTS into all three Runner
build configs.
Dart: PushService owns the channel; registerForPush() (interactive sign-in)
requests permission, registerIfAuthorized() (silent restore) refreshes the
token without prompting at cold launch, and taps route to /player/<id>.
A stable device id is generated once and persisted in Hive. ApiService
gains registerPushToken/unregisterPushToken against POST/DELETE
/api/push/register, tolerating {"enabled": false}. Wired into the auth
lifecycle: register on sign-in/restore, unregister on sign-out/expiry.
Tests cover token -> backend registration (incl. device-id generation and
dedup), tap -> route mapping (mocked channel), the API methods, and the
auth wiring.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RXMKM1rDWn8wNh93MMUtxY
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
`UNNotificationPresentationOptions.banner`/`.list` are iOS 14+, but the app targets iOS 13, so the no-codesign release build failed to compile. Fall back to the deprecated-but-functional `.alert` below iOS 14. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RXMKM1rDWn8wNh93MMUtxY
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
New-episode push on iOS (roadmap LATER tier, #4), modeled on Cantinarr (native APNs via a platform channel — no Firebase). Consumes the backend's
/api/push/register+ gateway integration.What
ios/Runner/AppDelegate.swift:UNUserNotificationCenterauth +registerForRemoteNotifications; anullfeed/pushmethod channel exposingrequestPermissionAndToken/registerIfAuthorized/getInitialNotification; device-token →onApnsToken; foreground presentation + tap →onNotificationTap.ios/Runner/Runner.entitlements(new, wired viaCODE_SIGN_ENTITLEMENTSin all configs):aps-environment = production(matches the gateway'sAPNS_ENV=production).lib/services/push_service.dart: requests permission after sign-in; on token →apiService.registerPushToken(token, deviceId, platform:"ios"); on tap (incl. cold-start viagetInitialNotification) →ref.read(routerProvider).push('/player/$videoId')fromdata.video_id. Stabledevice_idpersisted in Hive.api_service.dart:registerPushToken(POST) /unregisterPushToken(DELETE); tolerates{"enabled":false}. Wired into auth: register on sign-in, unregister on logout.Verification
dart format --set-exit-if-changedexit 0 ·flutter analyze— No issues ·flutter test— 151 passed (+ push_service/api_service/auth tests).Deploy note (handled separately with your Apple access): the App ID
codes.julian.nullfeedneeds the Push Notifications capability, and delivery only reaches production/TestFlight builds (the gateway is production APNs). CI's--no-codesignbuild is unaffected.🤖 Generated with Claude Code
https://claude.ai/code/session_01RXMKM1rDWn8wNh93MMUtxY