Curated summary
How we brought Datadog's data visualization to iOS: A focus on performance
Datadog built DogGraphs, a native SwiftUI graphing library, to support complex data visualizations across its iOS app and widgets. Because existing libraries did not meet its needs and the app supported iOS 14, the team had to optimize SwiftUI rendering without newer APIs such as Canvas. By combining careful API design, profiling, and a better understanding of SwiftUI’s update model, DogGraphs became a reusable framework used across multiple Datadog products.
Building DogGraphs for Complex Visualizations
- DogGraphs began with the Service Catalog and was designed to support additional Datadog products.
- It needed to provide:
- Native Swift and SwiftUI rendering
- iOS 14 compatibility
- Flexible, easy-to-use APIs
- Datadog’s default visual style and behavior
- Fast rendering for a responsive user experience
- The library now powers visualizations in logs, services, dashboards, Bits AI, and mobile widgets.
- It supports increasingly diverse graph types as new products integrate with the mobile application.
A Declarative, Type-Safe API
- DogGraphs uses Swift features such as result builders to describe complex graph configurations declaratively, in a style similar to SwiftUI.
- Graph definitions can be generated dynamically from server-provided dashboard or widget configurations.
- Compile-time type checking prevents invalid combinations, such as stacking incompatible Bar and Line graphs.
- Progressive disclosure provides sensible Datadog defaults while still allowing customization when necessary.
Profiling SwiftUI Performance
Datadog’s visualizations can involve metrics, logs, traces, multiple aggregation strategies, arithmetic operations, axes, labels, scales, and color configuration. Query responses are preprocessed by a shared internal service so that formatting and visual behavior remain consistent across platforms.
To optimize rendering, the team focused on two primary measurements:
- SwiftUI view body evaluations
- Excessive body evaluations can degrade performance, especially when many views are involved.
- Expensive computation should be moved outside view bodies.
_printChanges()can reveal why a view is being reevaluated, though it is a private API unsuitable for production use.
- Time Profiler
- Instruments helps identify slow function calls and locate expensive work in the rendering pipeline.
Important profiling scenarios included:
- Initial graph rendering
- Updates caused by window changes, tooltip selection, or layer visibility changes
- Device rotation and light/dark mode changes
- Interactions with unrelated views such as scroll views, toggles, and buttons
Understanding SwiftUI’s Update Model
The team used Apple’s “Demystify SwiftUI” session to build a mental model for how SwiftUI determines when views should update.
- Identity: How SwiftUI determines whether an element is the same as, or different from, a previous element.
- Lifetime: How SwiftUI tracks a view and its associated data over time.
- Dependencies: How SwiftUI determines which changes require an interface update.
- Diffing: SwiftUI compares view values to determine what changed, although the exact diffing mechanism is undocumented.
Understanding these concepts helps developers explain unexpected view updates and identify the sources of rendering bottlenecks.
Practical Recommendation
For complex SwiftUI components, measure real interaction scenarios rather than relying on assumptions. Track body evaluations and expensive function calls, keep costly work out of body, and design APIs that provide efficient defaults while preserving type safety and flexibility.
Related reading
Continue with another curated summary.