MotionEyes Animation Debug
Overview
Use MotionEyes as temporary observability for SwiftUI animation debugging. Instrument targeted values and geometry, capture time-series logs, compare observed motion against expected motion, apply fixes, re-validate, and clean up all agent-added tracing.
Workflow
Follow this exact order:
- Confirm the complaint and expected behavior in measurable terms.
- Locate the target view and the state values that drive the animation.
- Ensure MotionEyes availability; if missing, auto-integrate the MotionEyes package into the app target before continuing.
- Add temporary
.motionTrace(...)instrumentation withTrace.value,Trace.geometry, and (for scroll issues)Trace.scrollGeometrymetrics named after user intent. - Run the app and reproduce the issue.
- Capture logs with XcodeBuildMCP first; fallback to CLI log streaming if MCP is unavailable.
- Analyze how values evolve over time versus expected behavior.
- Implement a fix, rerun, and verify the motion now matches intent.
- Remove only agent-added MotionEyes imports/modifiers/trace metrics from this run; never remove user-authored pre-existing MotionEyes code.
Instrumentation Rules
Add instrumentation only to the minimum set of views needed to test the complaint.
- Use stable, semantic trace names that match the user complaint.
- Set the values to the same name as the property, so it's easier to identify.
- Use geometry tracing when motion is relative to container or sibling layout, or when checking true on-screen presentation movement.
- Use scroll geometry tracing when the bug involves
ScrollViewoffset, visible region, content size, insets, or restoration behavior. - Place
Trace.scrollGeometryon theScrollViewcontainer (or an immediate descendant bound to the same scroll context), not on unrelated overlays.
Choose geometry mode based on intent:
- Layout relationship in SwiftUI coordinates:
space: .swiftUI(.global), source: .layout - Window-relative layout motion:
space: .window, source: .layout - Physical screen-visible motion (including presentation wiggle):
space: .screen, source: .presentation
Example template:
import MotionEyes
import SwiftUI
struct CardMotionExample: View {
@State private var opacity = 1.0
@State private var offset = CGSize.zero
var body: some View {
RoundedRectangle(cornerRadius: 16)
.fill(.orange)
.frame(width: 180, height: 120)
.opacity(opacity)
.offset(offset)
.motionTrace("Card Motion", fps: 30) {
Trace.value("opacity", opacity)
Trace.value("offset", CGPoint(x: offset.width, y: offset.height))
Trace.geometry(
"cardFrame",
properties: [.minX, .minY, .width, .height],
space: .swiftUI(.global),
source: .layout
)
}
.onTapGesture {
withAnimation(.easeInOut(duration: 0.6)) {
opacity = opacity == 1 ? 0.4 : 1
offset = offset == .zero ? CGSize(width: 0, height: 36) : .zero
}
}
}
}
Scroll-focused template:
ScrollView {
content
}
.motionTrace("Chat Scroll", fps: 30) {
Trace.scrollGeometry(
"scrollMetrics",
properties: [.contentOffsetY, .visibleRectMinY, .visibleRectHeight]
)
}
Log Capture
Prefer XcodeBuildMCP:
- Call
mcp__XcodeBuildMCP__session_show_defaults. - Set missing defaults with
mcp__XcodeBuildMCP__session_set_defaults. - Build and run with
mcp__XcodeBuildMCP__build_run_simif needed. - Start capture with
mcp__XcodeBuildMCP__start_sim_log_cap:captureConsole: truesubsystemFilter: "MotionEyes"(or broader"all"when needed)
- Reproduce the animation.
- Stop capture with
mcp__XcodeBuildMCP__stop_sim_log_capand inspect returned logs.
Fallback CLI if MCP is unavailable:
xcrun simctl spawn booted log stream \
--style compact \
--level debug \
--predicate 'subsystem == "MotionEyes"'
MotionEyes Log Analysis
Use these signatures:
- Value samples:
[MotionEyes][View][Metric] key=value ... - Change burst start:
[MotionEyes][View][Metric] -- Start timestamp -- - Change burst end:
[MotionEyes][View][Metric] -- End timestamp --
Analyze:
- Direction: sign and trend of sampled values.
- Timing: time delta from intent trigger to first
Startand toEnd. - Shape: monotonic change, overshoot, reversals, oscillation, flat segments.
- Relationship: compare two traces over the same time window when behavior is relative.
Do not force fixed thresholds globally; evaluate against the user’s stated expectation.
Cleanup Rules
At the end of every run:
- Remove all MotionEyes instrumentation introduced by the agent during this debugging run.
- Keep all MotionEyes instrumentation that already existed before the run.
- Remove agent-added
import MotionEyesonly if it was added solely for temporary tracing and is no longer needed. - Confirm code compiles after cleanup.
Scenarios to Support
- Fade timing bug: trace
opacityand verify fade begins/ends when expected. - Wrong direction bug: trace Y-related value and confirm sign/trend match expected motion.
- Relative motion bug: trace two objects and verify their positional relationship over time.
- Scroll jump/restoration bug: trace
Trace.scrollGeometryon theScrollViewand verifycontentOffset/visibleRectprogression through navigation and return paths. - No motion desired: if something is meant to remain static during transition.
- Existing instrumentation safety: preserve user-authored MotionEyes traces.
- MCP unavailable: use CLI log stream and continue analysis.
- Missing package: auto-integrate MotionEyes, then execute normal workflow.
Reference
Load references/motioneyes-observability-patterns.md when choosing metrics or interpreting trace behavior.
