Skip to content

Commit 63076c7

Browse files
committed
Update Agent documentation structure
1 parent 1b1897d commit 63076c7

File tree

7 files changed

+381
-375
lines changed

7 files changed

+381
-375
lines changed

AGENTS.md

Lines changed: 57 additions & 336 deletions
Large diffs are not rendered by default.

CLAUDE.md

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
66

77
This is the Datadog SDK for iOS and tvOS — a modular Swift/Objective-C library for observability (Logs, Traces, RUM, Session Replay, Crash Reporting, WebView Tracking, and Feature Flags).
88

9-
**Read `AGENTS.md` for architecture, conventions, and development rules.** All changes must follow those guidelines.
9+
**Start with `AGENTS.md`** — it is the entry point to all SDK documentation. Follow its pointers to `docs/` for deeper context on architecture, conventions, testing, and development recipes.
1010

1111
## Available Skills
1212

@@ -20,44 +20,6 @@ Use these skills (via `/skill-name`) for common workflows:
2020
| `dd-sdk-ios:running-tests` | Running unit, module, or integration tests |
2121
| `dd-sdk-ios:xcode-file-management` | Adding, removing, moving, or renaming Swift source files |
2222

23-
## Build & Test Quick Reference
24-
25-
```bash
26-
# Setup
27-
make # Full setup (env-check, repo-setup, dependencies)
28-
29-
# Testing
30-
make test-ios SCHEME="DatadogCore iOS" # Run specific iOS scheme
31-
make test-ios-all # Run all iOS unit tests
32-
make test-tvos-all # Run all tvOS unit tests
33-
make ui-test TEST_PLAN="Default" # Run UI test plan
34-
make sr-snapshot-test # Run Session Replay snapshot tests
35-
36-
# Building
37-
make spm-build-ios # Build for iOS via SPM
38-
39-
# Code Quality
40-
make lint # Run SwiftLint
41-
./tools/lint/run-linter.sh --fix # Auto-fix lint violations
42-
make api-surface # Generate API surface files
43-
make api-surface-verify # Verify API surface hasn't changed
44-
make license-check # Check license headers
45-
46-
# Model Generation (DO NOT hand-edit generated models)
47-
make rum-models-generate GIT_REF=master # Generate RUM data models
48-
make sr-models-generate GIT_REF=master # Generate Session Replay models
49-
50-
# Cleanup
51-
make clean # Clean derived data, pods, xcconfigs
52-
```
53-
54-
## Module-Specific Documentation
55-
56-
For deep dives into specific modules, read the `*_FEATURE.md` file in the module directory:
57-
58-
- `DatadogRUM/RUM_FEATURE.md` — RUM public API, configuration, key files, troubleshooting
59-
- `DatadogSessionReplay/SESSION_REPLAY_FEATURE.md` — Session Replay configuration and usage
60-
6123
## CI Environment
6224

6325
The `ENV=ci` flag enables CI-specific behaviors (e.g., different API surface output paths). Set this when debugging CI failures locally.

docs/ARCHITECTURE.md

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
# SDK Architecture
2+
3+
## Module Structure
4+
5+
The SDK is a **modular monorepo**:
6+
7+
```
8+
DatadogInternal (shared protocols, types — Foundation only, no external deps)
9+
├── DatadogCore (initialization, storage, upload)
10+
├── DatadogLogs
11+
├── DatadogTrace
12+
├── DatadogRUM
13+
├── DatadogSessionReplay
14+
├── DatadogCrashReporting
15+
├── DatadogWebViewTracking
16+
├── DatadogFlags
17+
├── DatadogProfiling
18+
└── TestUtilities (test-only, shared mocks/matchers)
19+
```
20+
21+
### Module Boundaries
22+
23+
- Feature modules MUST NOT import each other
24+
- Only `DatadogCore` orchestrates feature lifecycles
25+
- `DatadogInternal` is the ONLY allowed place for shared types — it defines interfaces; `DatadogCore` provides concrete implementations
26+
- Platform support: iOS 12.0+, tvOS 12.0+, macOS 12.6+, watchOS 7.0+ (limited modules), visionOS
27+
28+
### Call Site Synchronization
29+
30+
**When modifying code in feature modules (Logs, Trace, RUM, etc.), you MUST check if any corresponding call sites in `DatadogCore` and `DatadogInternal` need to be updated.**
31+
32+
Common oversights:
33+
- Forgetting to update how `DatadogCore` registers the feature
34+
- Forgetting to update shared types in `DatadogInternal`
35+
- Forgetting to update manual data encoders (`SpanEventEncoder`, `LogEventEncoder`, ...) — new attributes won't be reported
36+
- Forgetting to update ObjC bridges
37+
- Forgetting to update `.pbxproj` files when adding, removing, or moving files
38+
39+
**Always search for usages across the entire codebase before considering a change complete.**
40+
41+
## Data Flow
42+
43+
### RUM Event Emission Pipeline
44+
45+
1. App calls public API (e.g., `RUMMonitor.shared().startView(...)`)
46+
2. `Monitor` (concrete `RUMMonitorProtocol` implementation) creates a `RUMCommand` with timestamp, attributes, user ID
47+
3. Command is enqueued to `FeatureScope` (async serial queue in `DatadogCore`)
48+
4. `FeatureScope` invokes scope hierarchy: `RUMApplicationScope.process()``RUMSessionScope.process()``RUMViewScope.process()`
49+
5. Each scope decides whether to accept, transform, or reject the command (returns `Bool``true` = scope stays open, `false` = scope is closed and removed from parent)
50+
6. If valid, scope serializes to RUM event JSON and calls `writer.write(data:)`
51+
7. `Writer` appends data to in-memory buffer or disk file
52+
8. `DataUploadWorker` periodically reads batches of events from disk
53+
9. `RequestBuilder` wraps batch in HTTP POST to Datadog intake
54+
10. `HTTPClient` sends request; on success files are deleted; on failure backoff/retry applies
55+
56+
### Storage Pipeline
57+
58+
```
59+
Feature writes event → AsyncWriter → FileWriter → FilesOrchestrator → disk file
60+
61+
DataUploadWorker (periodic) → DataReader → RequestBuilder → HTTPClient → Datadog backend
62+
```
63+
64+
- File-based storage in Application Support sandbox — no database
65+
- Directory structure: `[AppSupport]/Datadog/[site]/[feature]/`
66+
- Format: JSON for events, binary TLV encoding for compact storage
67+
- Optional encryption via `DataEncryption` protocol
68+
- Caching explicitly disabled at URLSession level (ephemeral config, `urlCache = nil`)
69+
- Key-value storage: `FeatureDataStore` for feature-specific persistent data
70+
71+
### Feature Registration Lifecycle
72+
73+
1. App calls `Datadog.initialize(with:trackingConsent:)` — creates `DatadogCore` instance
74+
2. `DatadogCore` is registered in `CoreRegistry` (singleton lookup)
75+
3. App calls feature-specific `enable()` (e.g., `RUM.enable(with:in:)`)
76+
4. Feature creates its plugin (e.g., `RUMFeature`) and registers with core
77+
5. Core allocates storage directory and upload worker for the feature
78+
6. Feature can now write events and receive messages via the bus
79+
80+
### State Management (Context)
81+
82+
`DatadogContext` is the central context object containing device info, app state, user info, network state, etc. It is built by `DatadogContextProvider` from multiple `ContextValuePublisher` instances that subscribe to system notifications and update context in real-time. Context is passed to every scope during command processing and attached to events before writing.
83+
84+
## Key Abstractions
85+
86+
| Abstraction | Purpose | Examples |
87+
|-------------|---------|----------|
88+
| **Feature** | Represents a module (RUM, Logs, Trace). Conforms to `DatadogFeature` or `DatadogRemoteFeature`. | `RUMFeature`, `LogsFeature` |
89+
| **Scope** | Hierarchical state container. Implements `process(command:context:writer:)` returning `Bool` (`true` = scope stays open, `false` = scope is closed and removed). | `RUMApplicationScope`, `RUMSessionScope`, `RUMViewScope` |
90+
| **Command** | User action or system event triggering state changes. Struct with timestamp, attributes. | `RUMStartViewCommand`, `RUMAddUserActionCommand` |
91+
| **Storage & Upload** | Persist events and batch-transmit to backend. | `FeatureStorage`, `FileWriter`, `DataUploadWorker` |
92+
| **Context Provider** | Publishes system/app state changes. Implements `ContextValuePublisher`. | `UserInfoPublisher`, `NetworkConnectionInfoPublisher` |
93+
| **Message Bus** | Inter-feature pub/sub communication. Protocol (`FeatureMessageReceiver`) in `DatadogInternal/Sources/MessageBus/`; concrete `MessageBus` in `DatadogCore`. | `MessageBus`, `FeatureMessageReceiver` |
94+
95+
## Key Protocols
96+
97+
| Protocol | Purpose | Location |
98+
|----------|---------|----------|
99+
| `DatadogCoreProtocol` | Central injectable core interface | `DatadogInternal/Sources/DatadogCoreProtocol.swift` |
100+
| `DatadogFeature` | Base protocol for feature modules | `DatadogInternal/Sources/DatadogFeature.swift` |
101+
| `DatadogRemoteFeature` | Extension adding `requestBuilder` for features that upload data | `DatadogInternal/Sources/DatadogFeature.swift` |
102+
| `FeatureScope` | Provides features with event writing, context, and storage | `DatadogInternal/Sources/DatadogCoreProtocol.swift` |
103+
| `FeatureMessageReceiver` | Receives inter-feature messages via the bus | `DatadogInternal/Sources/MessageBus/` |
104+
| `ContextValuePublisher` | Publishes context value changes | `DatadogCore/Sources/Core/Context/ContextValuePublisher.swift` |
105+
| `DataEncryption` | Optional encryption for on-disk data | `DatadogCore/Sources/Core/Storage/DataEncryption.swift` |
106+
107+
## Error Handling Strategy
108+
109+
The SDK must **never throw exceptions** to customer code:
110+
111+
- **NOP implementations**: `NOPMonitor`, `NOPDatadogCore` silently accept all API calls when SDK is not initialized or a feature is disabled.
112+
- **Validation at boundaries**: Invalid input is logged via `DD.logger` and ignored.
113+
- **Upload backoff**: Upload failures trigger exponential backoff and retry. Network errors are logged but never crash.
114+
- **User callback safety**: Exceptions in user-provided callbacks (e.g., event mappers) are caught and logged — original event is sent.
115+
- **Event mappers**: View events cannot be dropped (mapper must return a value). All other event types can be dropped by returning `nil`.
116+
117+
## Thread Safety Rules
118+
119+
- **`@ReadWriteLock`**: Property wrapper for concurrent read, exclusive write access. Use for shared mutable state.
120+
- **Serial queues**: Scope processing uses serial dispatch queues (`FeatureScope` is serial).
121+
- **No `DispatchQueue.main.sync`**: Forbidden — prevents deadlocks.
122+
- **NSLock exception**: `NSLock` is used in method swizzling code (`DatadogInternal/Sources/Swizzling/`, `DatadogInternal/Sources/NetworkInstrumentation/`) where low-level synchronization is required — do not refactor those.
123+
- **No thread spawning**: SDK uses system background queues (`qos: .utility`), never creates threads.
124+
125+
## HTTP Upload Details
126+
127+
- **Auth**: Client token passed as `DD-API-KEY` header
128+
- **Custom headers**: `DD-EVP-ORIGIN`, `DD-EVP-ORIGIN-VERSION`, `DD-REQUEST-ID`
129+
- **Formats**: JSON, NDJSON (batches), multipart/form-data (Session Replay, crashes)
130+
- **Compression**: Gzip (`Content-Encoding: gzip`)
131+
- **Endpoints by site**: `.us1``browser-intake-datadoghq.com`, `.eu1``browser-intake-datadoghq.eu`, etc.
132+
- **Header builder**: `DatadogInternal/Sources/Upload/URLRequestBuilder.swift`
133+
- **Site definitions**: `DatadogInternal/Sources/Context/DatadogSite.swift`
134+
135+
## Dependencies
136+
137+
- **KSCrash 2.5.0**: Crash detection and reporting (`DatadogCrashReporting`)
138+
- **opentelemetry-swift-core 2.3.0+**: OpenTelemetry API for distributed tracing (`DatadogTrace`).
139+
140+
Avoid adding new dependencies unless absolutely necessary (small footprint principle).
141+
142+
## Extension Libraries
143+
144+
- **Datadog Integration for Apollo iOS**: https://github.com/DataDog/dd-sdk-ios-apollo-interceptor — extracts GraphQL Operation information from requests to let DatadogRUM enrich GraphQL RUM Resources

docs/CONVENTIONS.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Coding Conventions
2+
3+
## Naming Conventions
4+
5+
**Types:** `PascalCase` for classes, structs, enums, protocols
6+
**Functions/Properties:** `camelCase`
7+
**Protocols:** Named as capabilities or contracts (e.g., `DatadogCoreProtocol`, `FeatureScope`, `MessageBusReceiver`)
8+
**Internal types:** Prefixed with module context (e.g., `RUMCommand`, `RUMViewScope`)
9+
**Mock types:** Suffixed with `Mock` or `Spy` (e.g., `HTTPClientMock`, `SnapshotProcessorSpy`)
10+
**Test files:** Mirror source path with `Tests` suffix (e.g., `RUMViewScopeTests.swift`)
11+
12+
**File naming patterns:**
13+
- Feature entry point: `{Feature}.swift` (e.g., `RUM.swift`, `Logs.swift`)
14+
- Feature configuration: `{Feature}Configuration.swift`
15+
- Feature plugin: `Feature/{Feature}Feature.swift`
16+
- Scope files: `RUM{ScopeName}Scope.swift`
17+
18+
## SwiftLint Rules (sources)
19+
20+
- `explicit_top_level_acl` — all top-level declarations must have explicit access control
21+
- `force_cast`, `force_try`, `force_unwrapping` — forbidden in source code
22+
- `todo_without_jira` — all TODOs must reference JIRA (e.g., `TODO: RUM-123`)
23+
- `unsafe_uiapplication_shared` — use `UIApplication.managedShared` instead
24+
- `required_reason_api_name` — symbol names must not conflict with Apple's Required Reason APIs
25+
26+
Config: `tools/lint/sources.swiftlint.yml` (sources), `tools/lint/tests.swiftlint.yml` (tests)
27+
28+
Do not disable lint rules except where the rule is incorrect and a Jira ticket exists to track reinstating it.
29+
30+
## Conditional Compilation
31+
32+
- `SPM_BUILD` — defined when building via Swift Package Manager
33+
- `DD_BENCHMARK` — defined for benchmark builds
34+
- `DD_COMPILED_FOR_INTEGRATION_TESTS` — toggles `@testable` imports for integration tests
35+
- Platform checks: `#if os(iOS)`, `#if canImport(UIKit)`, `#if os(tvOS)`
36+
37+
## Generated Models — DO NOT EDIT
38+
39+
Files in `DatadogInternal/Sources/Models/` are auto-generated from the [rum-events-format](https://github.com/DataDog/rum-events-format) schema. Never hand-edit. Regenerate with `make rum-models-generate GIT_REF=master`, verify with `make rum-models-verify`.
40+
41+
## File Headers
42+
43+
All source files must include the Apache License header:
44+
```swift
45+
/*
46+
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
47+
* This product includes software developed at Datadog (https://www.datadoghq.com/).
48+
* Copyright 2019-Present Datadog, Inc.
49+
*/
50+
```
51+
52+
## Commit & PR Conventions
53+
54+
### Commit Requirements
55+
- **All commits MUST be signed** (GPG or SSH signature)
56+
- **Prefix**: `[PROJECT-XXXX]` where PROJECT is the JIRA Project shortname (RUM, FFL, ...) and XXXX is the JIRA ticket number. It applies only for internal development. Third party contributions do not need it.
57+
- Example: `[RUM-1234] Add baggage header merging support`, `[FFL-213] Add Feature Flags support`
58+
59+
### PR Requirements
60+
- **Always follow `.github/PULL_REQUEST_TEMPLATE.md`** when creating PRs
61+
- **Title prefix**: `[PROJECT-XXXX]` matching the JIRA ticket
62+
- Include thorough test coverage
63+
- Pass all CI checks (lint, tests, API surface verification)

docs/DEVELOPMENT.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Development Recipes
2+
3+
## Where to Add New Code
4+
5+
### New Feature Module (e.g., DatadogNotifications)
6+
1. Create `DatadogNotifications/` with `Sources/` and `Tests/` subdirectories
7+
2. Entry point: `Notifications.swift`, config: `NotificationsConfiguration.swift`
8+
3. Feature plugin: `Feature/NotificationsFeature.swift` (implements `DatadogRemoteFeature`)
9+
4. Update `Datadog.xcworkspace` and any relevant `.pbxproj` files
10+
11+
### New RUM Instrumentation
12+
1. Create files in `DatadogRUM/Sources/Instrumentation/<InstrumentationName>/`
13+
2. Follow existing patterns (e.g., `Resources/`, `Actions/`, `AppHangs/`, `Views/`)
14+
3. Register in `RUMInstrumentation.swift`
15+
4. Add tests in `DatadogRUM/Tests/RUMTests/Instrumentation/`
16+
17+
### New RUM Command
18+
1. Add struct to `DatadogRUM/Sources/RUMMonitor/RUMCommand.swift` (implements `RUMCommand` protocol)
19+
2. Include timestamp, attributes, and any decision hints (e.g., `canStartBackgroundView`)
20+
3. Add public API method to `RUMMonitorProtocol.swift` and implement in `Monitor.swift`
21+
4. Add processing logic in the appropriate scope
22+
5. Add tests in `DatadogRUM/Tests/RUMTests/Scopes/`
23+
6. Update API surface: `make api-surface`
24+
25+
### New Context Provider
26+
1. Add the property to `DatadogContext` in `DatadogInternal/Sources/Context/`
27+
2. Create `DatadogCore/Sources/Core/Context/<ProviderName>Publisher.swift` implementing `ContextValuePublisher`
28+
3. Subscribe to relevant system notifications
29+
4. Register the publisher in `DatadogContextProvider` initialization
30+
5. Add tests in `DatadogCore/Tests/`
31+
32+
### Shared Internal Types (used by multiple features)
33+
1. Add to `DatadogInternal/Sources/` in the appropriate subdirectory
34+
2. Add tests in `DatadogInternal/Tests/`
35+
3. Changes here affect ALL modules — proceed with extreme caution
36+
37+
## RFC Process for Major Changes
38+
39+
If you're about to make a change that modifies public API significantly, changes data collection behavior, affects initialization/lifecycle, introduces new configuration options, or changes network request format/frequency — **STOP and inform the engineer.** Such changes require internal RFC approval and cross-platform alignment.
40+
41+
## Quick Reference
42+
43+
| Task | Command |
44+
|------|---------|
45+
| Setup | `make` |
46+
| Lint | `./tools/lint/run-linter.sh` |
47+
| Test iOS | `make test-ios SCHEME="<scheme>"` |
48+
| All iOS tests | `make test-ios-all` |
49+
| UI tests | `make ui-test TEST_PLAN="Default"` |
50+
| Build SPM | `make spm-build-ios` |
51+
| API surface | `make api-surface` |
52+
| Verify API surface | `make api-surface-verify` |
53+
| License check | `make license-check` |
54+
| Generate RUM models | `make rum-models-generate GIT_REF=master` |
55+
| Generate SR models | `make sr-models-generate GIT_REF=master` |
56+
| Clean | `make clean` |

docs/KNOWN_CONCERNS.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Known Concerns & Fragile Areas
2+
3+
These areas require extra caution when modifying. Changes here have caused production incidents or are inherently fragile.
4+
5+
| Area | Location | Risk |
6+
|------|----------|------|
7+
| **SwiftUI view name extraction** | `DatadogRUM/Sources/Instrumentation/Views/SwiftUI/` | Uses `Mirror`/`String(describing:)` reflection — fragile across Swift compiler versions. Do not change without extensive testing. |
8+
| **UIKit method swizzling** | `DatadogRUM/Sources/Instrumentation/` | Depends on UIKit internal method signatures — iOS version changes could break silently. See `docs/SWIZZLING.md`. |
9+
| **KSCrash report parsing** | `DatadogCrashReporting/Sources/` | Parsing C-level crash reports depends on KSCrash output format |
10+
| **Optional precondition in RUMSessionScope** | `RUMMonitor/Scopes/RUMSessionScope.swift` | Silent in production, crashes in debug — masks invalid state |
11+
| **500 concurrent feature operations** | `RUMFeatureOperationManager.swift` | Active operations capped at 500 with `Set<String>` tracking |
12+
| **Message bus queue unbounded** | `DatadogCore/Sources/Core/MessageBus.swift` | No queue depth limit — burst messaging could cause memory pressure |
13+
| **User action 100ms window** | `RUMUserActionScope` | Discrete user actions have a hardcoded 100ms window — actions arriving after are dropped |

0 commit comments

Comments
 (0)