Scope:
Sources/**. Also follow root instructions.
- Prefer Swift for new code when target boundaries allow it
- Use
[[Class alloc] init], not[Class new] - Wrap headers with
NS_ASSUME_NONNULL_BEGINandNS_ASSUME_NONNULL_END - Mark nullable parameters and properties explicitly
- Prefer protocol-oriented design when it improves testability or composition
- Follow the Swift API Design Guidelines
- Default to
internaland expose only intentional SDK API aspublic - Prefer
privateoverfileprivate - Mark classes
finalunless designed for subclassing
- Never let the SDK crash the host app
- Wrap public entry points in
do/catchor equivalent - Prefer
Result<T, Error>or optional returns over throwing for internal APIs - Log production errors through
SentryLoginstead of assertions - Always use explicit capture lists when capturing
self - Prefer
[weak self]withguard let selffor closures stored by the SDK
- Prefer
SentryDependencyContainerproviders for SDK-owned dependencies - Use
getLazyVarorgetOptionalLazyVarfor lazily-created defaults - Override dependencies through
SentryDependencyContainer.sharedInstance()in tests - Do not use
PrivateSentrySDKOnlyfor test injection when a provider fits - Do not add static shared service singletons
- Put lifecycle-owned shared services in
SentryDependencyContainer - When process-lifetime hooks require global access, use a narrow proxy with synchronized weak lifecycle targets, following
SentryNetworkTrackerProxy
- Comment non-obvious rationale, workarounds, and gotchas, not visible behavior
- Add headerdocs for public API
- Do not remove or rename public symbols without deprecating them with migration guidance
- Design new public API Swift-first
- Do not add
@objc,NSObjectinheritance,NS_SWIFT_NAME, or Objective-C wrappers unless an existing contract or identified consumer requires Objective-C support - Use
@_spi(Private)for unstable API consumed by hybrid SDKs - Keep
@_spi(Private)out of public headers - Document
PrivateSentrySDKOnlyinstability in headerdocs - Use
SENTRY_NO_INITfor types that must not be publicly instantiated - Follow
develop-docs/SENTRY-OBJC.mdfor wrappers and API placement
- Use compile-time gates for code that cannot compile or link on a target, not for ordinary runtime behavior
- In Swift, use
os(...),targetEnvironment(...), andcanImport(...)for platform and module capabilities - Gate UI code by both supported platforms and
!SENTRY_NO_UI_FRAMEWORK - In Objective-C and C/C++, prefer capability macros from
SentryDefines.handSentryProfilingConditionals.h, includingSENTRY_HAS_UIKIT,SENTRY_TARGET_REPLAY_SUPPORTED, andSENTRY_TARGET_PROFILING_SUPPORTED - Keep Swift, Objective-C, and
SentryObjCdeclaration and implementation gates aligned - Preserve older Xcode compatibility with
#if swift(>=...), compiler feature probes fromSentryCompiler.h, andcanImportor__has_includechecks - Use
#availableor@availablefor runtime OS availability only after the code can compile with the oldest supported Xcode - When newer SDK symbols are absent from older Xcode headers, combine runtime availability with a compile-time gate or dynamic lookup instead of referencing the symbol directly
- Follow
SentryNetworkTracker.swiftwhen a service needs protocol-based mocking without retaining the abstraction in release builds - Under
#if SENTRY_TEST || SENTRY_TEST_CI || DEBUG, define the service protocol, conform the concrete service, and narrow its dependency protocols for mock injection - Under
#else, typealias the service protocol name to the concrete generic implementation and its dependency protocol name toSentryDependencyContainer - This strips the protocol abstraction from release builds while preserving the same dependency-facing names in every configuration
- Keep consumers typed against those shared names so test, debug, and release builds use the same call sites
- Assume public methods can be called from arbitrary queues
- Use
SentryMutex<T>for new Swift mutable state - Group related protected values in a private
Statestruct - Do not use
NSLock,NSRecursiveLock, or legacysynchronizedhelpers for new Swift code - Refactor reentrant locking before adopting non-recursive
SentryMutex - Use
@synchronized(self)for Objective-C mutable state in scope, hub, and client code - Prefer
SentryDispatchQueueWrapperover raw queues for testability - Never do synchronous work on the main thread in production paths
- Check upstream KSCrash when investigating relevant bugs
- Signal handlers must remain async-signal-safe
- Do not allocate heap memory, acquire locks, or send Objective-C messages in signal handlers
- Accepted signal-handler operations include
write(),vsnprintf,strerror_r, C11 atomics, andSENTRY_ASYNC_SAFE_LOG_*macros - Bounds-check fixed buffers, validate indices, and use
snprintfinstead ofsprintf - Treat
pthread_self()as an explicit accepted exception to strict signal safety