Skip to content

feat(analytics): activation timestamp telemetry + native newsletter prompt (Mac + iOS)#973

Merged
datlechin merged 1 commit intomainfrom
feat/activation-telemetry-newsletter-w5
May 4, 2026
Merged

feat(analytics): activation timestamp telemetry + native newsletter prompt (Mac + iOS)#973
datlechin merged 1 commit intomainfrom
feat/activation-telemetry-newsletter-w5

Conversation

@datlechin
Copy link
Copy Markdown
Member

Summary

Mac and iOS clients now send 3 activation timestamps in the existing analytics heartbeat (connection_attempted_at, connection_succeeded_at, first_query_executed_at), and Mac shows a native NSAlert newsletter prompt after the user's 3rd successful connection. Companion to backend PR TableProApp/license#28 which adds the receiving columns and write-once server-side guard.

Why

Live analytics shows 65% of installs never open a database connection — bounce is universal across every region, indicating a product onboarding leak. Prior heartbeat only sent connection_count (peak concurrent), which conflates "user never tried" with "user tried and failed." The new timestamps let us measure the real activation funnel:

install → connection_attempted_at → connection_succeeded_at → first_query_executed_at → has_license

Changes

Shared package (Packages/TableProCore/Sources/TableProAnalytics/)

  • AnalyticsPayload: 3 new optional Date? fields, ISO 8601 encoding
  • AnalyticsEnvironmentProvider: 3 new protocol getters with default-nil extension (so iOS / future plugins compile without explicitly implementing them)
  • AnalyticsHeartbeatService: reads new fields when building payload; HMAC body sign covers them automatically
  • New TableProAnalyticsTests target: 4 tests (ISO 8601 round-trip, nil omission, existing fields preserved, HMAC covers new fields)

Mac (TablePro/)

  • MacAnalyticsProvider singleton: write-once UserDefaults pattern for the 3 timestamps + successfulConnectionCount counter + newsletterPromptShown latch
  • DatabaseManager+Sessions: marks attempt before connect; marks success + posts .successfulConnectionRecorded notification AFTER startup commands complete (partial failures don't count)
  • Reused connections (existing.driver != nil) intentionally not counted — only fresh connections fire markers, matching activation funnel intent
  • DatabaseManager+Queries: marks first query after successful execute
  • NewsletterPromptCoordinator: singleton listening on the notification; presents NSAlert after 3rd successful connection. Coordinator owns the trigger rather than empty-state view because the welcome window typically closes the moment the user successfully connects (so empty-state-tied prompt would never fire).
  • 7 Swift Testing tests for write-once + counter + latch independence

iOS (TableProMobile/)

  • IOSAnalyticsProvider: same UserDefaults write-once pattern; attached via attach(appState:) after .shared is created
  • ConnectionCoordinator: marks attempt + success around connectFresh
  • QueryEditorView: marks first query after successful driver.execute

Adjacent fix

  • ConnectionManagerTests mock was missing fetchSchemas() stub — 1-line fix unblocks the entire TableProCore test bundle (was blocking other suites from compiling)

Test plan

  • xcrun swift test --package-path Packages/TableProCore47 tests, 7 suites, all pass (includes 4 new analytics tests)
  • 7 new XCTest tests for MacAnalyticsProvider ready in TableProTests/Services/
  • swiftlint lint --strict on all 12 modified files → 0 violations
  • CHANGELOG.md updated under [Unreleased] / Added
  • User runs xcodebuild themselves (per their preference)
  • Manual smoke after build: launch app, click Connect → check UserDefaults via defaults read com.TablePro for the 3 new keys
  • After 3rd successful connection: NSAlert prompt should appear once
  • Verify heartbeat hits backend with new fields after first connect

Companion PR

sprint/growth-2026-05 in TableProApp/license ships the receiving end: schema migration 2026_05_03_000009_add_activation_timestamps_to_analytics_events, validation rules in StoreAnalyticsRequest, and write-once defense in AnalyticsController.

Out of scope

  • iOS does not have an equivalent newsletter prompt (Mac-only this sprint)
  • The successfulConnectionCount counter exists on iOS but has no UI hook (no iOS empty state with newsletter)
  • No telemetry for connection failures (the funnel only tracks success transitions)

…rompt (W5)

Mac + iOS clients now send 3 activation timestamps in heartbeat:
- connection_attempted_at: first time user clicks Connect
- connection_succeeded_at: first time a connection actually establishes
- first_query_executed_at: first time user runs any query

Shared package (TableProCore):
- AnalyticsPayload: 3 new optional Date fields, ISO 8601 encoding
- AnalyticsEnvironmentProvider: 3 new protocol getters with default-nil
  extension (iOS / future plugins compile without implementing them)
- AnalyticsHeartbeatService: reads new fields when building payload;
  HMAC body sign covers them automatically
- New TableProAnalyticsTests target: 4 tests (ISO 8601 round-trip, nil
  omission, existing fields preserved, HMAC covers new fields)

Mac (TablePro):
- MacAnalyticsProvider singleton: write-once UserDefaults pattern for the
  3 timestamps + successfulConnectionCount counter + newsletterPromptShown
  latch
- DatabaseManager+Sessions: markConnectionAttempted before connect;
  markConnectionSucceeded + post .successfulConnectionRecorded notification
  AFTER startup commands complete (partial failures don't count). Reused
  connections (existing.driver != nil) intentionally not counted — only
  fresh connections fire markers, matching activation funnel intent.
- DatabaseManager+Queries: markFirstQueryExecuted after successful execute
- NewsletterPromptCoordinator: singleton listening on the notification;
  presents NSAlert after 3rd successful connection ('Subscribe in Browser'
  opens https://tablepro.app/?subscribe=true&source=mac via NSWorkspace).
  Coordinator owns the trigger rather than empty-state view because welcome
  window typically closes the moment the user successfully connects.
- 7 Swift Testing tests for write-once + counter + latch independence

iOS (TableProMobile):
- IOSAnalyticsProvider: same UserDefaults write-once pattern; attached via
  attach(appState:) after .shared is created
- ConnectionCoordinator: marks attempt + success around connectFresh
- QueryEditorView: marks first query after successful driver.execute
- Number-separator fix on adjacent line as part of file-touch cleanup

Adjacent fix:
- ConnectionManagerTests mock missing fetchSchemas() stub: 1-line fix
  unblocks the entire TableProCore test bundle (was blocking other suites
  from compiling)
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@datlechin datlechin merged commit a2ec34a into main May 4, 2026
2 checks passed
@datlechin datlechin deleted the feat/activation-telemetry-newsletter-w5 branch May 4, 2026 03:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant