Files
crowd-funder-for-time-pwa/doc/consuming-app-handoff-ios-native-fetcher-chained-dual.md
Jose Olarte III 7d87a746f9 feat(ios): register Swift TimeSafariNativeFetcher for New Activity notifications
Add TimeSafariNativeFetcher (plansLastUpdatedBetween parity with Android) and
call DailyNotificationPlugin.registerNativeFetcher from AppDelegate before JS
configureNativeFetcher; broaden DailyNotificationDelivered scheduled_time types
in willPresent. Wire the new file into the App target; normalize PBX object IDs
to 24-char hex.

Document plugin ≥3 handoff (consuming-app-handoff-ios-native-fetcher-chained-dual),
refresh iOS/Android parity and notification-from-api-call file tables.
2026-04-02 19:02:48 +08:00

4.5 KiB
Raw Blame History

Consuming app handoff: iOS native fetcher + chained dual (mirror)

Canonical source: daily-notification-plugin repo, doc/CONSUMING_APP_HANDOFF_IOS_NATIVE_FETCHER_AND_CHAINED_DUAL.md (same content as below for offline use).


Implemented in this app

  • ios/App/App/TimeSafariNativeFetcher.swift — Swift NativeNotificationContentFetcher mirroring TimeSafariNativeFetcher.java (POST …/plansLastUpdatedBetween, starred IDs from daily_notification_timesafari.starredPlanIds, JWT pool selection, pagination key daily_notification_timesafari.last_acked_jwt_id, aggregated copy).
  • AppDelegate.swiftDailyNotificationPlugin.registerNativeFetcher(TimeSafariNativeFetcher.shared) at launch before any JS configureNativeFetcher; foreground handler reads scheduled_time as Int64, NSNumber, or Int for DailyNotificationDelivered.

Dependency

  • @timesafari/daily-notification-plugin must be ≥ 3.0.0 (register native fetcher, chained dual, iOS updateStarredPlans). Declare it in package.json from the official remote (git+https://gitea.anomalistdesign.com/trent_larson/daily-notification-plugin.git, branch or tag as needed), then npm install so package-lock.json resolves the published tree.

Bump / sync (after plugin version is resolved)

  1. npm install
  2. npx cap sync ios && npx cap sync android
  3. cd ios/App && pod install
  4. Clean build in Xcode / Android Studio

QA focus

  • iOS: Fetcher registered before configureNativeFetcher; updateStarredPlans not UNIMPLEMENTED.
  • Both: New Activity fires after prefetch for that cycle where the plugin implements chaining.
  • Android: Existing MainActivity.setNativeFetcher unchanged; regression-test cancelDualSchedule vs Daily Reminder.

Original handoff text (from plugin)

This document is for the host app repository (e.g. crowd-funder-for-time-pwa) after bumping @timesafari/daily-notification-plugin to a version that includes:

  • iOS NativeNotificationContentFetcherstyle registration (DailyNotificationPlugin.registerNativeFetcher)
  • iOS updateStarredPlans / getStarredPlans (parity with Android daily_notification_timesafari / starredPlanIds semantics)
  • iOS chained dual flow: user notification is armed only after prefetch completes (delay if fetch is late; max slip 15 minutes before fallback copy)
  • Android chained dual flow: exact notify alarm is scheduled after dual prefetch completes (no longer scheduled at initial scheduleDualNotification before fetch)

Material from doc/new-activity-notifications-ios-android-parity.md still applies; the plugin doc adds app-side steps not spelled out there.

1. iOS — register native fetcher before configureNativeFetcher

The plugin rejects configureNativeFetcher if no fetcher is registered (aligned with Android).

In AppDelegate (or earliest app startup before Capacitor calls into the plugin):

import TimesafariDailyNotificationPlugin

DailyNotificationPlugin.registerNativeFetcher(TimeSafariNativeFetcher.shared)

Implement TimeSafariNativeFetcher as a Swift type that:

  • Conforms to NativeNotificationContentFetcher
  • Implements fetchContent(context: FetchContext) async throws -> [NotificationContent] with the same Endorser behavior as TimeSafariNativeFetcher.java
  • Implements configure(apiBaseUrl:activeDid:jwtToken:jwtTokenPool:) if the fetcher needs credentials pushed from TypeScript

Starred plan IDs for the fetcher: Read JSON array string from UserDefaults key daily_notification_timesafari.starredPlanIds (written by updateStarredPlans from JS).

2. iOS — UNUserNotificationCenterDelegate / rollover

Chained dual notifications set:

  • notification_id = org.timesafari.dailynotification.dual
  • scheduled_time = NSNumber (fire time in ms)

Ensure DailyNotificationDelivered forwards notification_id and scheduled_time from notification content userInfo.

3. Android — no API change for setNativeFetcher

Host apps that already call DailyNotificationPlugin.setNativeFetcher(TimeSafariNativeFetcher(...)) keep that flow.

Behavior change: the dual notify alarm is scheduled when dual prefetch work finishes, not at the initial scheduleDualNotification only.

4. Assumptions

  • Swift host implements TimeSafariNativeFetcher; the plugin does not embed plansLastUpdatedBetween on iOS when a host fetcher is registered (mirrors Android).
  • Module import: TimesafariDailyNotificationPlugin (Pod TimesafariDailyNotificationPlugin).