nexo/docs/macos-notification-test-guide.md
toki 0faa687954 feat: macos notification support and documentation updates
- Add macos notification smoke evidence template and test guide
- Add Fixtures for open_channel and open_thread json
- Update roadmaps for messaging runtime phase and macos notification milestone
- Update flutter-test and messaging_flutter READMEs
2026-06-02 04:32:38 +09:00

6.9 KiB

macOS Notification Test Guide

Last verified: 2026-06-02

This guide fixes the preflight contract for macOS notification work before Apple signing, a macOS test device, or real OS notification UI evidence is available.

Do not commit real Apple, APNs, Firebase, service-account, device-token, auth token, private endpoint, Team ID, signing identity, provisioning, notarization, or private host values into tracked docs. Use placeholders in examples and keep credential files in approved local or secret-store locations.

Current Repository State

  • The macOS plugin is still a scaffold. packages/messaging_flutter/macos/Classes/NexoMessagingPlugin.swift currently registers only the legacy nexo_messaging method channel and implements getPlatformVersion.
  • Dart expects native notification and action channels: com.tokilabs.nexo.messaging/notifications and com.tokilabs.nexo.messaging/notification_actions.
  • The minimum macOS bridge is therefore not implemented yet. Until it exists, local routing can only be tested through Dart unit tests or a future debug injection bridge.
  • apps/flutter-test/macos is a host scaffold. It carries sandbox entitlements, but no notification entitlement, signing team, provisioning profile, hardened runtime, or notarization value is tracked in this repo.
  • Local routing fixtures live under apps/flutter-test/macos/Fixtures/*.json.

The first useful macOS smoke target is:

  1. Register the expected EventChannel and MethodChannel in the macOS plugin.
  2. Implement a debug-only local injection path compatible with debugSendNativeEvent.
  3. Map notification userInfo fields into the Dart NotificationOpenedEvent contract.
  4. Build and run the test host with local placeholder signing.
  5. Collect permission, display, click/open routing, lifecycle, and known ACK, dismiss, and inline reply gap evidence.

Minimum Bridge Contract

The Dart SDK expects the macOS native side to preserve these channel names:

Channel Name Direction Minimum macOS behavior
EventChannel com.tokilabs.nexo.messaging/notifications native to Dart Emit codec-safe maps for notification, open, token, clear, session, and future action-failure events.
MethodChannel com.tokilabs.nexo.messaging/notification_actions Dart to native Accept SDK action methods used by Dart without crashing the host app.
Legacy MethodChannel nexo_messaging Dart/test compatibility Keep getPlatformVersion until the generated example compatibility path is intentionally removed.

Minimum methods for the action channel:

Method Minimum preflight result Production expectation
debugSendNativeEvent Validate the argument is a map and emit it on the EventChannel. Debug/test-only injection path.
saveDeviceToken Accept and store or no-op a formatted token string without logging secrets. Store the macOS server-registration token once the platform token source is chosen.
getDeviceToken Return the last stored formatted token or null. Return the current formatted macOS server-registration token.
setAuthToken Validate required string arguments and no-op. Persist token for ACK/reply work only after storage rules are designed.
clearAuthToken Validate serverUrl and no-op. Clear persisted token for the server.
setSigningKey Validate required string arguments and no-op without persistence. Persist the signing key only after secure storage rules are designed.

Fallback boundaries:

  • Missing bridge methods should return FlutterMethodNotImplemented, not a successful placeholder.
  • Token, auth, signing-key, ACK, dismiss, and inline reply persistence are not implied by this preflight milestone.
  • Real notification permission prompts and visible notification display belong to macOS 알림 테스트 unless a small debug bridge task explicitly implements them first.

Host Placeholder Criteria

Tracked macOS host files must stay generic:

  • Keep real signing Team ID, identity, provisioning profile, notarization profile, Apple ID, and private host values out of tracked files.
  • Keep apps/flutter-test/macos/Runner/DebugProfile.entitlements and Release.entitlements as placeholder entitlements until a smoke run requires a specific capability.
  • Add notification capability through Xcode or local build settings for a smoke run, then record sanitized evidence instead of committing private signing state.
  • Do not add real credential files under apps/flutter-test/macos.

Expected local placeholder checks:

codesign -d --entitlements :- build/macos/Build/Products/Debug/flutter_test.app

Look for sandbox values and any locally added notification-related capability. Record private identifiers as placeholders in evidence.

Payload Mapping

macOS notification userInfo maps into Dart using the same stable keys as the Android and iOS contracts:

userInfo key Dart event field Notes
type type Use message for visible message notifications and opened for direct open events.
server_url server_url Required before host navigation callbacks can run.
server_id server_id Diagnostic and future server URL fallback input.
channel_id channel_id Drives onNavigateToChannel when thread routing is not selected.
root_id root_id Drives onNavigateToThread when CRT is enabled.
is_crt_enabled is_crt_enabled Accept boolean true, string "true", or string "1".
ack_id ack_id Preserve for future ACK evidence; do not invent ACK behavior.
post_id post_id Preserve for notification identity and future ACK/display evidence.
userInteraction userInteraction Set to true when a click/open path caused the event.

Routing expectation:

  • Channel fixture: server_url and channel_id are present, root_id is empty and is_crt_enabled is false, so Dart calls onNavigateToChannel.
  • Thread fixture: server_url and root_id are present and is_crt_enabled is true, so Dart calls onNavigateToThread.

Local Fixtures

Use the fixture files as sanitized payloads for a future debug injection method or for manual userInfo construction during smoke testing:

  • apps/flutter-test/macos/Fixtures/open_channel.json
  • apps/flutter-test/macos/Fixtures/open_thread.json

The fixtures intentionally use https://example.invalid and smoke IDs. Replace private runtime values only in local run notes or sanitized evidence.

Evidence

Use macos-notification-smoke-evidence-template.md for macOS runs. Preflight evidence should explicitly separate:

  • permission state
  • visible display state
  • click/open routing result
  • app lifecycle state
  • ACK, dismiss, and inline reply implementation gaps

Until the bridge and real OS smoke exist, a valid preflight result may be "not implemented yet" with a concrete follow-up plan, rather than a failed runtime claim.