-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Comments
fix: batch Apple Reminders sync into single silent push#4920
fix: batch Apple Reminders sync into single silent push #4920
Conversation
Previously one silent push was sent per action item, causing iOS to throttle and drop most pushes when a conversation had multiple tasks. Now all items are JSON-encoded into a single push payload. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Refactored auto_sync_action_items_batch to check the integration once and send all Apple Reminders items in one push, eliminating redundant Firestore lookups and iOS silent push throttling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Parse JSON array of action items from single push and create all reminders in one background execution pass. Use markExportedBatch callback to notify Flutter of all exported items at once. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the app is in the foreground, FCM messages are handled by Flutter, not the native silent push handler. This method allows Flutter to forward the batch payload to native for processing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Handle markExportedBatch callback from native and add triggerSyncFromFCM to forward foreground FCM messages to native for Apple Reminders creation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously this message type was unhandled in the Flutter foreground FCM listener, causing sync to silently fail when the app was open. Now forwards the payload to native via AppleRemindersService. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request effectively addresses the iOS silent push throttling issue by batching Apple Reminders sync actions into a single push. The changes are well-structured across the backend, native iOS, and Flutter layers. I've identified a significant area of code duplication in the native iOS code between AppDelegate.swift and AppleRemindersService.swift that should be refactored for better maintainability. Additionally, there are opportunities in the Flutter code to improve performance by parallelizing API calls. Overall, this is a solid fix with a few areas for refinement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is significant code duplication between this syncFromFCM function and the handleAppleRemindersSync function in AppDelegate.swift. Both functions implement the same logic for parsing a batch of action items and creating EKReminder objects.
This duplication makes the code harder to maintain, as any future changes to the sync logic will need to be applied in two places.
To improve this, you should refactor the common logic into a single, shared method. Since AppDelegate already has an instance of AppleRemindersService, you could create a new private method within AppleRemindersService that encapsulates the core batch processing logic.
For example, you could create a function like this in AppleRemindersService:
private func syncReminderBatch(items: [[String: Any]]) -> (createdCount: Int, exportedIds: [String]) { // ... core logic from syncFromFCM ... return (createdCount, exportedIds) }
Then, syncFromFCM and handleAppleRemindersSync can call this shared method and handle the results accordingly. This will make the code more modular and easier to maintain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are awaiting _markActionItemExported for each ID in a loop. This executes the API calls sequentially, which can be inefficient if there are many IDs to process.
To improve performance, you can run these asynchronous operations in parallel using Future.wait. This will send all the requests concurrently and wait for all of them to complete.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to the markExportedBatch handler, you are awaiting _markActionItemExported for each ID in a loop. This results in sequential API calls.
For better performance, especially with multiple action items, you should execute these calls in parallel using Future.wait.
Move the core batch-sync logic (parse JSON, dedup, create EKReminders, persist dedup set) into a single public method so both the background silent-push path (AppDelegate) and the foreground MethodChannel path share the same implementation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove duplicated batch-sync logic from AppDelegate and call the shared method on AppleRemindersService. Remove unused EventKit import and iso8601DateFormatter. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Run all _markActionItemExported HTTP calls concurrently instead of sequentially, in both the markExportedBatch handler and the triggerSyncFromFCM method. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
apple_reminders_sync— previously this message type was unhandled in Flutter's foreground listener, causing sync to silently fail when the app was opensyncFromFCMnative method — allows Flutter to forward the batch payload to native iOS for EKReminder creation when the app is in the foregroundRoot Cause
Two issues were causing tasks to not sync to Apple Reminders:
iOS silent push throttling: The backend was sending N individual silent pushes for N action items from a single conversation, all within milliseconds. iOS aggressively throttles
content-availablepushes, delivering only the first and dropping the rest.Foreground handling gap: When the app was in the foreground, Flutter's
FirebaseMessaging.onMessageintercepted the FCM message but had no handler forapple_reminders_sync, so it was silently discarded. The nativedidReceiveRemoteNotificationis not called when FlutterFire handles the message.Changes
Backend
notifications.py:send_apple_reminders_sync_pushnow accepts a list of action items and JSON-encodes them into a single push payloadtask_sync.py:auto_sync_action_items_batchchecks the integration once and sends all Apple Reminders items in one push (also eliminates N redundant Firestore lookups)iOS Native
AppDelegate.swift:handleAppleRemindersSyncparses a JSON array batch and creates all reminders in one background execution pass, callsmarkExportedBatchto notify FlutterAppleRemindersService.swift: NewsyncFromFCMmethod handles the same batch logic when triggered from the foreground via MethodChannelFlutter
notification_service_fcm.dart: Addedapple_reminders_synccase in the foreground FCM listener, forwarding to native viaAppleRemindersService.triggerSyncFromFCMapple_reminders_service.dart: AddedmarkExportedBatchhandler andtriggerSyncFromFCMmethod that invokes native and marks exported items in the backendTest plan
🤖 Generated with Claude Code