Skip to content

chore: rebuild iOS sample apps to match Android demo refresh#57

Merged
bobbyg603 merged 15 commits into
mainfrom
feat/samples-revamp-v2
May 18, 2026
Merged

chore: rebuild iOS sample apps to match Android demo refresh#57
bobbyg603 merged 15 commits into
mainfrom
feat/samples-revamp-v2

Conversation

@bobbyg603

@bobbyg603 bobbyg603 commented May 18, 2026

Copy link
Copy Markdown
Member

Rebased onto main now that the hang-detection PR (#56) has merged.

Summary

Rebuilds the SwiftUI, UIKit-Swift, UIKit-ObjC, macOS-UIKit-ObjC, and macOS C++ CLI demo apps to match the visual language of the Android sample refresh. Each sample now has:

  • A consistent card-based layout (Crash / Non-Crash Error / User Feedback / Hang).
  • Working buttons for crash, fatal hang, and user feedback.
  • Splat overlay tuning and gesture polish on macOS.
  • Shake-to-feedback wired up across all three iOS samples.

Test plan

  • Build each sample app target locally and confirm crash, hang, and feedback cards work.
  • On iOS, confirm shake gesture opens the feedback prompt.
  • On macOS, confirm splat overlay tuning and Edit menu work.

Related

🤖 Generated with Claude Code

bobbyg603 and others added 13 commits May 18, 2026 11:59
Add opt-in detection and reporting of fatal main-thread hangs on iOS,
tvOS, and macOS. A new BugSplatHangTracker class runs a low-QoS
watchdog thread that dispatches a "ping" block to the main queue every
threshold/5 seconds (clamped to at least 100ms). The block, when
serviced, resets an atomic counter; if the counter accumulates enough
unanswered pings to cover `hangDetectionThreshold` seconds, the main
thread is considered hung. When the main thread later services a ping,
a recovery callback fires so the persisted report can be discarded -
only fatal hangs survive to the next launch.

BugSplat persists the report as a synthetic PLCrashReport-style live
report (marking the main thread as crashed via its captured Mach port)
in the crashes directory with bugsplat-hang-* attributes and a per-
launch UUID for correlation with any crash from the same launch. Reuses
the existing next-launch scanner and upload pipeline.

Public API on BugSplat:

* `enableHangDetection` (BOOL, default NO) - opt in before -start
* `hangDetectionThreshold` (NSTimeInterval, default 2.0) - tune the
  unresponsive duration that counts as a hang; clamped to >= 0.1s

Detection is suppressed when a debugger is attached or the app is
inactive (iOS/tvOS background). No-op inside app extensions. -start
must be invoked on the main thread when hang detection is enabled so
the main thread's Mach port can be captured.

Tests cover the watchdog state machine (detection, throttle, recovery,
suspension guard) deterministically via injectable clock and dispatch
blocks, and the BugSplat-side persistence (file layout, metadata,
recovery cleanup).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wire enableHangDetection into each example app and add a "Simulate Hang"
button that blocks the main thread forever (sleep loop with observable
side effects so the C++ forward-progress rule can't elide it). On the
next launch, a fatal-hang report uploads automatically.

Apps updated: SwiftUI, UIKit-Swift, UIKit-ObjC, macOS-UIKit-ObjC, and
the macOS C++ command-line tool. The CLI tool's loop alternates between
mainObjCRunLoop pumping and getline; the recovery callback discards any
hang report queued during idle prompt waits.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rewrites the SwiftUI sample to mirror bugsplat-android's
feat/demo-app-ui-refresh design: top bar with wordmark + version + status
pill, large title with database badge, four event cards (Crash, Non-Crash
Error, Feedback, Hang) with splat icons, recent-activity card backed by
UserDefaults-persisted entries, and View dashboard deeplink.

New files:
- DemoTheme.swift - palette
- DemoComponents.swift - reusable views (event card, activity row, pill)
- ActivityLog.swift - up-to-10 entry log, synchronous flush on crash entries

App icon regenerated from bug.png composited on white at all required sizes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ports the SwiftUI sample's demo-screen design to both UIKit apps using
programmatic Auto Layout. Mirrors the Android refresh: wordmark + version
+ status pill top bar, large title with database badge, four splat-icon
event cards, recent-activity card with UserDefaults persistence
(synchronous flush on crash entries), and View dashboard deeplink.

UIKit-Swift adds DemoTheme.swift, DemoViews.swift, ActivityLog.swift; the
ObjC counterpart adds BSPDemoTheme, BSPDemoViews, BSPActivityLog. Both
asset catalogs gain the five splat/wordmark imagesets and regenerated
AppIcon variants. Stock Main.storyboard "Crash!" buttons removed so they
don't render over the new programmatic UI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The UIImageView for the top-bar wordmark was stretching to fill
horizontal space, and scaleAspectFit then centered the image inside
that wide frame - making the logo appear ~25pt right of the leading
edge instead of flush left with the title below.

Pin width = height × intrinsic aspect ratio and set horizontal content
hugging to required, so the view sits at its natural size.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rebuilds the macOS-AppKit-ObjC sample to match the iOS demo refresh,
adapted for desktop:

- Custom window title \"BugSplat • Sample App\" with native traffic lights
- 2x2 tile grid of event cards (vs mobile's stacked column) for the wider
  canvas, each with a hover state and a keyboard-shortcut chip (1-4)
- Cmd+1..4 shortcuts wired via NSEvent local monitor route to crash /
  non-crash / feedback / hang
- \"Splat your keyboard\" gesture - press any 8 keys within 1.2s opens
  the feedback sheet, the desktop analog to shake-to-feedback
- Shared Recent Activity component backed by the same BSPActivityLog
  (UserDefaults JSON, max 10, synchronous flush on crash)
- Footer line uses a system handwriting font (Bradley Hand / Noteworthy)
  for the \"splat your keyboard.\" call-out

Asset catalog gains the splat/wordmark imagesets and macOS-sized AppIcon
variants regenerated from bug.png on white.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
NSStackView with a bottom constraint was stretching the recent-activity
card to absorb leftover window space, pushing the wordmark away from the
title bar. Pin the topBar to an explicit height, force the stack to hug
its content vertically, and drop the bottom constraint - the stack now
sits at the top with screenBg filling the area below.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sending an ObjC message to nil silently returns 0, so the prior
[nil performSelector:] / [(NSNumber*)nil longValue] approaches did
nothing - the cards looked broken. Replace with a plain C null
pointer dereference that reliably produces SIGSEGV on both iOS and
macOS.

Verified on macOS via diagnostic report: byte-write translation fault
at the new line in triggerCrash:.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The 8-second recoverable freeze never produced a hang report - the
fatal-only hang detector deletes the persisted report on recovery.
Switch all four samples (SwiftUI / UIKit-Swift / UIKit-ObjC /
macOS-AppKit-ObjC) to an indefinite freeze that requires force-quit,
which is what produces the upload on next launch.

Implementation: single Thread.sleep(until: .distantFuture) /
sleepUntilDate: distantFuture - no spin loop, quiet CPU while frozen.
Card subtitle updated to "Freeze main thread - force-quit to upload"
and confirmation alerts explain the force-quit requirement per
platform (simctl terminate, swipe-up, killall, etc).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The keyUp monitor was removing each keyCode from the tracking set as
soon as the key released, so unless the user literally held 8+ keys
simultaneously (often blocked by laptop keyboard ghosting) the set
never accumulated and the feedback sheet never opened.

Drop the keyUp monitor and treat the set as \"distinct keys pressed
within the 1.2s window\" regardless of release. Now a normal mash of
8 keys across a fraction of a second reliably opens feedback.
Verified via System Events sending 8 sequential keystrokes - the
feedback sheet opens.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…issions

- macOS sample: gate the keyDown monitor on a feedbackInProgress flag so
  typing in the feedback sheet doesn't re-trigger another splat. Clear
  the pressed-keys set on enter and exit so the gesture starts fresh.
- All four samples: treat an empty (whitespace-only) title the same as
  Cancel. No postFeedback call, no Recent Activity entry. Previously
  hitting Send with blank fields would log \"(no title)\" or
  \"Feedback submitted\" - now only real input counts as a submission.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The footer copy says \"Shake the device to send feedback anytime.\" but
the gesture wasn't actually implemented. Wire it up:

- UIKit-ObjC: override motionEnded:withEvent: on the view controller
  (already becomes first responder); on .motionShake open the
  existing feedback dialog.
- UIKit-Swift: same pattern - override canBecomeFirstResponder + call
  becomeFirstResponder in viewDidAppear + override motionEnded(_:with:).
- SwiftUI: add ShakeDetector (UIViewControllerRepresentable) that
  hosts a tiny first-responder view controller whose
  motionEnded(_:with:) calls back into the SwiftUI view. Use as a
  zero-size .background overlay in ContentView.

Verified in iOS Simulator via Device > Shake on all three apps - the
Send Feedback alert opens.

(Earlier attempt swizzled UIWindow.motionEnded but Swift extensions
can't override, and method_exchangeImplementations against an
inherited method silently rewires the base class for every UIResponder
subclass - the SwiftUI app crashed at launch as a result. The
representable approach is constrained to one well-defined first
responder and avoids the swizzle entirely.)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three small fixes after end-to-end testing:

- Splat gesture: drop threshold from 8 distinct keys / 1.2s to 6 distinct
  keys / 0.5s. MacBook keyboards have a 6-key rollover ceiling so 8
  simultaneous keys physically can't all register; 6 fits and the tighter
  window keeps real typing from triggering it. Footer copy updated to
  match.
- AppDelegate now installs a minimal Edit menu (Cut/Copy/Paste/Select
  All). The storyboard's MainMenu only had App + Window menus, so
  NSTextField didn't get standard Cmd+A/C/V/X - which mattered when stray
  splat keys leaked into the feedback fields.
- Recent Activity list caps visible rows at 5. The log still persists 10,
  but rendering more was growing the card off the bottom of the window.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Base automatically changed from feat/hang-detection-v2 to main May 18, 2026 16:48
@bobbyg603 bobbyg603 changed the title feat: rebuild iOS sample apps to match Android demo refresh chore: rebuild iOS sample apps to match Android demo refresh May 18, 2026
Resolve conflicts in favor of main for SDK files (BugSplat.m,
BugSplatHangTracker.m, Package.swift) since the hang-detection PR is
already merged. Keep our revamped sample apps for the three iOS sample
files. The macOS sample auto-merged a stale Simulate Hang button block
into a layout helper; reset that file to our branch's version which
already wires hang via -triggerHang: against the new event card.
Copilot AI review requested due to automatic review settings May 18, 2026 18:45

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Rebuilds the sample app UIs around a shared card-based demo experience, adding reusable theme/view components, local recent-activity persistence, feedback flows, hang/crash triggers, and updated assets across SwiftUI, UIKit Swift/ObjC, and macOS AppKit samples.

Changes:

  • Adds shared demo UI components, palettes, activity logs, and refreshed card layouts.
  • Wires crash, hang, feedback, dashboard, shake/splat gesture, and recent activity behavior.
  • Updates asset catalogs, app icons, storyboards, and Xcode project source membership.

Reviewed changes

Copilot reviewed 55 out of 143 changed files in this pull request and generated 15 comments.

Show a summary per file
File Description
Example_Apps/BugSplatTest-UIKit-Swift/BugSplatTest-UIKit-Swift/ViewController.swift Replaces simple UIKit screen with full card-based demo UI and actions.
Example_Apps/BugSplatTest-UIKit-Swift/BugSplatTest-UIKit-Swift/DemoViews.swift Adds reusable UIKit demo controls and activity rows.
Example_Apps/BugSplatTest-UIKit-Swift/BugSplatTest-UIKit-Swift/DemoTheme.swift Adds shared UIKit Swift color palette.
Example_Apps/BugSplatTest-UIKit-Swift/BugSplatTest-UIKit-Swift/Base.lproj/Main.storyboard Removes old storyboard button now replaced by programmatic UI.
Example_Apps/BugSplatTest-UIKit-Swift/BugSplatTest-UIKit-Swift/Assets.xcassets/splat_hang.imageset/Contents.json Adds hang card image metadata.
Example_Apps/BugSplatTest-UIKit-Swift/BugSplatTest-UIKit-Swift/Assets.xcassets/splat_feedback.imageset/Contents.json Adds feedback card image metadata.
Example_Apps/BugSplatTest-UIKit-Swift/BugSplatTest-UIKit-Swift/Assets.xcassets/splat_error.imageset/Contents.json Adds error card image metadata.
Example_Apps/BugSplatTest-UIKit-Swift/BugSplatTest-UIKit-Swift/Assets.xcassets/splat_crash.imageset/Contents.json Adds crash card image metadata.
Example_Apps/BugSplatTest-UIKit-Swift/BugSplatTest-UIKit-Swift/Assets.xcassets/bugsplat_wordmark.imageset/Contents.json Adds wordmark image metadata.
Example_Apps/BugSplatTest-UIKit-Swift/BugSplatTest-UIKit-Swift/Assets.xcassets/AppIcon.appiconset/Contents.json Updates iPad app icon filename mappings.
Example_Apps/BugSplatTest-UIKit-Swift/BugSplatTest-UIKit-Swift/ActivityLog.swift Adds local recent-activity persistence and relative time formatting.
Example_Apps/BugSplatTest-UIKit-Swift/BugSplatTest-UIKit-Swift.xcodeproj/project.pbxproj Adds new Swift files to the target.
Example_Apps/BugSplatTest-UIKit-ObjC/BugSplatTest-UIKit-ObjC/ViewController.m Rebuilds UIKit ObjC demo screen and actions.
Example_Apps/BugSplatTest-UIKit-ObjC/BugSplatTest-UIKit-ObjC/ViewController.h Removes old crash-demo array property.
Example_Apps/BugSplatTest-UIKit-ObjC/BugSplatTest-UIKit-ObjC/BSPDemoViews.m Adds reusable ObjC UIKit demo views.
Example_Apps/BugSplatTest-UIKit-ObjC/BugSplatTest-UIKit-ObjC/BSPDemoViews.h Declares reusable ObjC UIKit demo views.
Example_Apps/BugSplatTest-UIKit-ObjC/BugSplatTest-UIKit-ObjC/BSPDemoTheme.m Adds ObjC UIKit color palette implementation.
Example_Apps/BugSplatTest-UIKit-ObjC/BugSplatTest-UIKit-ObjC/BSPDemoTheme.h Declares ObjC UIKit theme colors.
Example_Apps/BugSplatTest-UIKit-ObjC/BugSplatTest-UIKit-ObjC/BSPActivityLog.m Adds ObjC recent-activity persistence.
Example_Apps/BugSplatTest-UIKit-ObjC/BugSplatTest-UIKit-ObjC/BSPActivityLog.h Declares ObjC activity log API/constants.
Example_Apps/BugSplatTest-UIKit-ObjC/BugSplatTest-UIKit-ObjC/Base.lproj/Main.storyboard Removes old storyboard button now replaced by programmatic UI.
Example_Apps/BugSplatTest-UIKit-ObjC/BugSplatTest-UIKit-ObjC/Assets.xcassets/splat_hang.imageset/Contents.json Adds hang card image metadata.
Example_Apps/BugSplatTest-UIKit-ObjC/BugSplatTest-UIKit-ObjC/Assets.xcassets/splat_feedback.imageset/Contents.json Adds feedback card image metadata.
Example_Apps/BugSplatTest-UIKit-ObjC/BugSplatTest-UIKit-ObjC/Assets.xcassets/splat_error.imageset/Contents.json Adds error card image metadata.
Example_Apps/BugSplatTest-UIKit-ObjC/BugSplatTest-UIKit-ObjC/Assets.xcassets/splat_crash.imageset/Contents.json Adds crash card image metadata.
Example_Apps/BugSplatTest-UIKit-ObjC/BugSplatTest-UIKit-ObjC/Assets.xcassets/bugsplat_wordmark.imageset/Contents.json Adds wordmark image metadata.
Example_Apps/BugSplatTest-UIKit-ObjC/BugSplatTest-UIKit-ObjC/Assets.xcassets/AppIcon.appiconset/Contents.json Updates iPad app icon filename mappings.
Example_Apps/BugSplatTest-UIKit-ObjC/BugSplatTest-UIKit-ObjC.xcodeproj/project.pbxproj Adds new ObjC source files to the target.
Example_Apps/BugSplatTest-SwiftUI/BugSplatTest-SwiftUI/ShakeNotification.swift Adds SwiftUI shake detection bridge.
Example_Apps/BugSplatTest-SwiftUI/BugSplatTest-SwiftUI/DemoTheme.swift Adds SwiftUI color palette.
Example_Apps/BugSplatTest-SwiftUI/BugSplatTest-SwiftUI/DemoComponents.swift Adds reusable SwiftUI demo components.
Example_Apps/BugSplatTest-SwiftUI/BugSplatTest-SwiftUI/ContentView.swift Rebuilds SwiftUI demo screen and actions.
Example_Apps/BugSplatTest-SwiftUI/BugSplatTest-SwiftUI/BugSplatTest-SwiftUIApp.swift Cleans whitespace in app initializer.
Example_Apps/BugSplatTest-SwiftUI/BugSplatTest-SwiftUI/Assets.xcassets/splat_hang.imageset/Contents.json Adds hang card image metadata.
Example_Apps/BugSplatTest-SwiftUI/BugSplatTest-SwiftUI/Assets.xcassets/splat_feedback.imageset/Contents.json Adds feedback card image metadata.
Example_Apps/BugSplatTest-SwiftUI/BugSplatTest-SwiftUI/Assets.xcassets/splat_error.imageset/Contents.json Adds error card image metadata.
Example_Apps/BugSplatTest-SwiftUI/BugSplatTest-SwiftUI/Assets.xcassets/splat_crash.imageset/Contents.json Adds crash card image metadata.
Example_Apps/BugSplatTest-SwiftUI/BugSplatTest-SwiftUI/Assets.xcassets/bugsplat_wordmark.imageset/Contents.json Adds wordmark image metadata.
Example_Apps/BugSplatTest-SwiftUI/BugSplatTest-SwiftUI/Assets.xcassets/AppIcon.appiconset/Contents.json Updates iPad app icon filename mappings.
Example_Apps/BugSplatTest-SwiftUI/BugSplatTest-SwiftUI/ActivityLog.swift Adds SwiftUI recent-activity persistence.
Example_Apps/BugSplatTest-SwiftUI/BugSplatTest-SwiftUI.xcodeproj/project.pbxproj Adds new SwiftUI support files to the target.
Example_Apps/BugSplatTest-macOS-UIKit-ObjC/BugSplatTest-macOS-UIKit-ObjC/ViewController.m Rebuilds macOS AppKit sample UI and interactions.
Example_Apps/BugSplatTest-macOS-UIKit-ObjC/BugSplatTest-macOS-UIKit-ObjC/BSPDemoViews.m Adds reusable AppKit demo views.
Example_Apps/BugSplatTest-macOS-UIKit-ObjC/BugSplatTest-macOS-UIKit-ObjC/BSPDemoViews.h Declares reusable AppKit demo views.
Example_Apps/BugSplatTest-macOS-UIKit-ObjC/BugSplatTest-macOS-UIKit-ObjC/BSPDemoTheme.m Adds AppKit color palette implementation.
Example_Apps/BugSplatTest-macOS-UIKit-ObjC/BugSplatTest-macOS-UIKit-ObjC/BSPDemoTheme.h Declares AppKit theme colors.
Example_Apps/BugSplatTest-macOS-UIKit-ObjC/BugSplatTest-macOS-UIKit-ObjC/BSPActivityLog.m Adds macOS activity persistence.
Example_Apps/BugSplatTest-macOS-UIKit-ObjC/BugSplatTest-macOS-UIKit-ObjC/BSPActivityLog.h Declares macOS activity log API/constants.
Example_Apps/BugSplatTest-macOS-UIKit-ObjC/BugSplatTest-macOS-UIKit-ObjC/Assets.xcassets/splat_hang.imageset/Contents.json Adds hang card image metadata.
Example_Apps/BugSplatTest-macOS-UIKit-ObjC/BugSplatTest-macOS-UIKit-ObjC/Assets.xcassets/splat_feedback.imageset/Contents.json Adds feedback card image metadata.
Example_Apps/BugSplatTest-macOS-UIKit-ObjC/BugSplatTest-macOS-UIKit-ObjC/Assets.xcassets/splat_error.imageset/Contents.json Adds error card image metadata.
Example_Apps/BugSplatTest-macOS-UIKit-ObjC/BugSplatTest-macOS-UIKit-ObjC/Assets.xcassets/splat_crash.imageset/Contents.json Adds crash card image metadata.
Example_Apps/BugSplatTest-macOS-UIKit-ObjC/BugSplatTest-macOS-UIKit-ObjC/Assets.xcassets/bugsplat_wordmark.imageset/Contents.json Adds wordmark image metadata.
Example_Apps/BugSplatTest-macOS-UIKit-ObjC/BugSplatTest-macOS-UIKit-ObjC/Assets.xcassets/AppIcon.appiconset/Contents.json Adds macOS app icon filename mappings.
Example_Apps/BugSplatTest-macOS-UIKit-ObjC/BugSplatTest-macOS-UIKit-ObjC/AppDelegate.m Adds Edit menu setup for text-field shortcuts.
Example_Apps/BugSplatTest-macOS-UIKit-ObjC/BugSplatTest-macOS-UIKit-ObjC.xcodeproj/project.pbxproj Adds new AppKit source files to the target.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Example_Apps/BugSplatTest-SwiftUI/BugSplatTest-SwiftUI/ActivityLog.swift Outdated
Comment thread Example_Apps/BugSplatTest-UIKit-Swift/BugSplatTest-UIKit-Swift/ActivityLog.swift Outdated
Comment thread Example_Apps/BugSplatTest-UIKit-ObjC/BugSplatTest-UIKit-ObjC/BSPActivityLog.m Outdated
Comment thread Example_Apps/BugSplatTest-UIKit-ObjC/BugSplatTest-UIKit-ObjC/BSPDemoTheme.m Outdated
- ActivityLog (4 platforms): synchronize() on hang as well as crash, since
  the demo asks users to force-quit during a hang and SIGKILL can drop
  pending UserDefaults writes.
- macOS feedback: footer now shows "Sending…", success, or the error
  description so submission status is visible, matching iOS.
- UIKit-Swift: drop unused titleRowDatabaseBadgeContainer property.
- UIKit-ObjC title row: drop title's compression resistance from required
  to defaultHigh+1 and enable tail truncation so a long database badge
  can't produce unsatisfiable constraints on compact iPhone widths.
- BSPDemoViews.h: correct doc - the card initializer only builds the
  view; callers wire actions via -addTarget:action:forControlEvents:.
- DemoTheme (4 platforms): darken textSecondary/textTertiary so small
  section headers and footers meet WCAG AA (≥4.5:1) on white/screen bg.
  Old tertiary #9CA3AF was ~2.5:1.
- macOS BSPActivityLog .h/.m: fix file headers that named the iOS
  UIKit-ObjC sample after a copy/paste.
@bobbyg603 bobbyg603 merged commit 47f9414 into main May 18, 2026
4 checks passed
@bobbyg603 bobbyg603 deleted the feat/samples-revamp-v2 branch May 18, 2026 19:08
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.

3 participants