chore: rebuild iOS sample apps to match Android demo refresh#57
Merged
Conversation
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>
6 tasks
daveplunkett
approved these changes
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.
There was a problem hiding this comment.
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.
- 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rebased onto
mainnow 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:
Test plan
Related
🤖 Generated with Claude Code