diff --git a/Samples/SentrySampleShared/SentrySampleShared/EnvironmentVariableTableViewCell.swift b/Samples/SentrySampleShared/SentrySampleShared/EnvironmentVariableTableViewCell.swift
index c908a5f3c50..2de9efb6d99 100644
--- a/Samples/SentrySampleShared/SentrySampleShared/EnvironmentVariableTableViewCell.swift
+++ b/Samples/SentrySampleShared/SentrySampleShared/EnvironmentVariableTableViewCell.swift
@@ -1,4 +1,4 @@
-#if !os(macOS) && !os(tvOS) && !os(watchOS)
+#if !os(macOS) && !os(tvOS) && !os(watchOS) && !os(visionOS)
import UIKit
class EnvironmentVariableTableViewCell: UITableViewCell, UITextFieldDelegate {
@@ -10,7 +10,6 @@ class EnvironmentVariableTableViewCell: UITableViewCell, UITextFieldDelegate {
return field
}()
- var float: Bool = false
var override: (any SentrySDKOverride)?
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
@@ -27,30 +26,31 @@ class EnvironmentVariableTableViewCell: UITableViewCell, UITextFieldDelegate {
fatalError("init(coder:) has not been implemented")
}
- func configure(with override: any SentrySDKOverride, float: Bool) {
- titleLabel.text = override.rawValue as? String
+ func textFieldDidEndEditing(_ textField: UITextField) {
+ if override?.overrideType == .float {
+ override?.floatValue = textField.text.flatMap { Float($0) }
+ } else {
+ override?.stringValue = textField.text
+ }
+ SentrySDKWrapper.shared.startSentry()
+ }
+}
+
+extension EnvironmentVariableTableViewCell: FeatureFlagCell {
+ func configure(with override: any SentrySDKOverride) {
+ titleLabel.text = override.rawValue
var text: String
- if let value = override.floatValue {
+ if override.overrideType == .float, let value = override.floatValue {
text = String(format: "%.2f", value)
- } else if let value = override.stringValue {
+ } else if override.overrideType == .string, let value = override.stringValue {
text = value
} else {
text = "nil"
}
valueField.text = text
- self.float = float
self.override = override
}
-
- func textFieldDidEndEditing(_ textField: UITextField) {
- if self.float {
- override?.floatValue = textField.text.flatMap { Float($0) }
- } else {
- override?.stringValue = textField.text
- }
- SentrySDKWrapper.shared.startSentry()
- }
}
-#endif // !os(macOS) && !os(tvOS) && !os(watchOS)
+#endif // !os(macOS) && !os(tvOS) && !os(watchOS) && !os(visionOS)
diff --git a/Samples/SentrySampleShared/SentrySampleShared/FeatureFlagCell.swift b/Samples/SentrySampleShared/SentrySampleShared/FeatureFlagCell.swift
new file mode 100644
index 00000000000..c64b317d502
--- /dev/null
+++ b/Samples/SentrySampleShared/SentrySampleShared/FeatureFlagCell.swift
@@ -0,0 +1,9 @@
+#if !os(macOS) && !os(tvOS) && !os(watchOS) && !os(visionOS)
+
+import UIKit
+
+protocol FeatureFlagCell: UITableViewCell {
+ func configure(with override: any SentrySDKOverride)
+}
+
+#endif // !os(macOS) && !os(tvOS) && !os(watchOS) && !os(visionOS)
diff --git a/Samples/SentrySampleShared/SentrySampleShared/FeaturesViewController.swift b/Samples/SentrySampleShared/SentrySampleShared/FeaturesViewController.swift
index 809637a04d7..ab7e2ae298b 100644
--- a/Samples/SentrySampleShared/SentrySampleShared/FeaturesViewController.swift
+++ b/Samples/SentrySampleShared/SentrySampleShared/FeaturesViewController.swift
@@ -1,4 +1,5 @@
#if !os(macOS) && !os(tvOS) && !os(watchOS) && !os(visionOS)
+
import UIKit
public class FeaturesViewController: UIViewController {
@@ -45,90 +46,38 @@ public class FeaturesViewController: UIViewController {
@objc func resetDefaults() {
SentrySDKOverrides.resetDefaults()
+ SentrySDKWrapper.shared.startSentry()
tableView.reloadData()
}
}
extension FeaturesViewController: UITableViewDataSource {
public func numberOfSections(in tableView: UITableView) -> Int {
- 6
+ SentrySDKOverrides.allCases.count
}
public func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
- if section == 0 {
- return "Special"
- } else if section == 1 {
- return "Performance"
- } else if section == 2 {
- return "Tracing"
- } else if section == 3 {
- return "Profiling"
- } else if section == 4 {
- return "Feedback"
- } else if section == 5 {
- return "Other"
- }
- return nil
+ SentrySDKOverrides.allCases[section].rawValue
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
- if section == 0 {
- return SentrySDKOverrides.Special.allCases.count
- } else if section == 1 {
- return SentrySDKOverrides.Performance.allCases.count
- } else if section == 2 {
- return SentrySDKOverrides.Tracing.allCases.count
- } else if section == 3 {
- return SentrySDKOverrides.Profiling.allCases.count
- } else if section == 4 {
- return SentrySDKOverrides.Feedback.allCases.count
- } else if section == 5 {
- return SentrySDKOverrides.Other.allCases.count
- }
- return 0
+ SentrySDKOverrides.allCases[section].featureFlags.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
- if indexPath.section == 2 {
- if SentrySDKOverrides.Tracing.boolValues.contains(SentrySDKOverrides.Tracing.allCases[indexPath.row]) {
- let cell = tableView.dequeueReusableCell(withIdentifier: "launchArgumentCell", for: indexPath) as! LaunchArgumentTableViewCell
- cell.configure(with: SentrySDKOverrides.Tracing.allCases[indexPath.row])
- return cell
- } else {
- let cell = tableView.dequeueReusableCell(withIdentifier: "environmentVariableCell", for: indexPath) as! EnvironmentVariableTableViewCell
- cell.configure(with: SentrySDKOverrides.Tracing.allCases[indexPath.row], float: true)
- return cell
- }
- } else if indexPath.section == 3 {
- if SentrySDKOverrides.Profiling.boolValues.contains(SentrySDKOverrides.Profiling.allCases[indexPath.row]) {
- let cell = tableView.dequeueReusableCell(withIdentifier: "launchArgumentCell", for: indexPath) as! LaunchArgumentTableViewCell
- cell.configure(with: SentrySDKOverrides.Profiling.allCases[indexPath.row])
- return cell
- } else {
- let cell = tableView.dequeueReusableCell(withIdentifier: "environmentVariableCell", for: indexPath) as! EnvironmentVariableTableViewCell
- cell.configure(with: SentrySDKOverrides.Profiling.allCases[indexPath.row], float: true)
- return cell
- }
- } else if indexPath.section == 5 {
- if SentrySDKOverrides.Other.boolValues.contains(SentrySDKOverrides.Other.allCases[indexPath.row]) {
- let cell = tableView.dequeueReusableCell(withIdentifier: "launchArgumentCell", for: indexPath) as! LaunchArgumentTableViewCell
- cell.configure(with: SentrySDKOverrides.Other.allCases[indexPath.row])
- return cell
- } else {
- let cell = tableView.dequeueReusableCell(withIdentifier: "environmentVariableCell", for: indexPath) as! EnvironmentVariableTableViewCell
- cell.configure(with: SentrySDKOverrides.Other.allCases[indexPath.row], float: false)
- return cell
- }
+ let featureType = SentrySDKOverrides.allCases[indexPath.section]
+ let featureFlag = featureType.featureFlags[indexPath.row]
+
+ let reuseIdentifier: String
+ switch featureFlag.overrideType {
+ case .boolean:
+ reuseIdentifier = "launchArgumentCell"
+ case .float, .string:
+ reuseIdentifier = "environmentVariableCell"
}
- let cell = tableView.dequeueReusableCell(withIdentifier: "launchArgumentCell", for: indexPath) as! LaunchArgumentTableViewCell
- if indexPath.section == 0 {
- cell.configure(with: SentrySDKOverrides.Special.allCases[indexPath.row])
- } else if indexPath.section == 1 {
- cell.configure(with: SentrySDKOverrides.Performance.allCases[indexPath.row])
- } else if indexPath.section == 4 {
- cell.configure(with: SentrySDKOverrides.Feedback.allCases[indexPath.row])
- }
+ let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! FeatureFlagCell
+ cell.configure(with: featureFlag)
return cell
}
}
diff --git a/Samples/SentrySampleShared/SentrySampleShared/LaunchArgumentTableViewCell.swift b/Samples/SentrySampleShared/SentrySampleShared/LaunchArgumentTableViewCell.swift
index 26df23cfdab..c636d966149 100644
--- a/Samples/SentrySampleShared/SentrySampleShared/LaunchArgumentTableViewCell.swift
+++ b/Samples/SentrySampleShared/SentrySampleShared/LaunchArgumentTableViewCell.swift
@@ -1,4 +1,4 @@
-#if !os(macOS) && !os(tvOS) && !os(watchOS)
+#if !os(macOS) && !os(tvOS) && !os(watchOS) && !os(visionOS)
import UIKit
class LaunchArgumentTableViewCell: UITableViewCell {
@@ -26,11 +26,13 @@ class LaunchArgumentTableViewCell: UITableViewCell {
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
+}
+extension LaunchArgumentTableViewCell: FeatureFlagCell {
func configure(with override: any SentrySDKOverride) {
- titleLabel.text = override.rawValue as? String
+ titleLabel.text = override.rawValue
flagSwitch.isOn = override.boolValue
self.override = override
}
}
-#endif // !os(macOS) && !os(tvOS) && !os(watchOS)
+#endif // !os(macOS) && !os(tvOS) && !os(watchOS) && !os(visionOS)
diff --git a/Samples/SentrySampleShared/SentrySampleShared/SampleAppDebugMenu.swift b/Samples/SentrySampleShared/SentrySampleShared/SampleAppDebugMenu.swift
index be7d98302b2..22acbef24bc 100644
--- a/Samples/SentrySampleShared/SentrySampleShared/SampleAppDebugMenu.swift
+++ b/Samples/SentrySampleShared/SentrySampleShared/SampleAppDebugMenu.swift
@@ -4,7 +4,7 @@ import UIKit
public class SampleAppDebugMenu: NSObject {
public static let shared = SampleAppDebugMenu()
- static var displayingForm = false
+ static var displayingMenu = false
let window = {
if #available(iOS 13.0, *) {
@@ -42,8 +42,11 @@ public class SampleAppDebugMenu: NSObject {
}
@objc func displayDebugMenu() {
- SampleAppDebugMenu.displayingForm = true
- rootVC.present(FeaturesViewController(nibName: nil, bundle: nil), animated: true)
+ SampleAppDebugMenu.displayingMenu = true
+
+ let listVC = FeaturesViewController(nibName: nil, bundle: nil)
+ listVC.presentationController?.delegate = self
+ rootVC.present(listVC, animated: true)
}
class Window: UIWindow {
@@ -68,7 +71,7 @@ public class SampleAppDebugMenu: NSObject {
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
- guard !SampleAppDebugMenu.displayingForm else {
+ guard !SampleAppDebugMenu.displayingMenu else {
return super.hitTest(point, with: event)
}
@@ -86,7 +89,7 @@ public class SampleAppDebugMenu: NSObject {
extension SampleAppDebugMenu: UIAdaptivePresentationControllerDelegate {
public func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
rootVC.dismiss(animated: true)
- SampleAppDebugMenu.displayingForm = false
+ SampleAppDebugMenu.displayingMenu = false
}
}
#endif // !os(macOS) && !os(tvOS) && !os(watchOS) && !os(visionOS)
diff --git a/Samples/SentrySampleShared/SentrySampleShared/SentrySDKOverrides.swift b/Samples/SentrySampleShared/SentrySampleShared/SentrySDKOverrides.swift
index a615ce16bdf..146caf03e9f 100644
--- a/Samples/SentrySampleShared/SentrySampleShared/SentrySDKOverrides.swift
+++ b/Samples/SentrySampleShared/SentrySampleShared/SentrySDKOverrides.swift
@@ -1,32 +1,46 @@
import Foundation
-public protocol SentrySDKOverride: RawRepresentable, CaseIterable {
+public enum OverrideType {
+ case boolean
+ case float
+ case string
+}
+
+/// This protocol defines the typed value access for a specific feature flag.
+public protocol SentrySDKOverride: RawRepresentable, CaseIterable where Self.RawValue == String {
+ var overrideType: OverrideType { get }
+
var boolValue: Bool { get set }
var floatValue: Float? { get set }
var stringValue: String? { get set }
}
-extension SentrySDKOverride {
- public var boolValue: Bool { get { false } set { } }
- public var floatValue: Float? { get { nil } set { } }
- public var stringValue: String? { get { nil } set { } }
-}
-
-public enum SentrySDKOverrides {
- private static let defaults = UserDefaults.standard
+/// This enum contains nested enums, to group feature flags by any kind of category you choose, whether it's feature area, or just a kitchen sink of otherwise unclassifiable flags (like Special/Other).
+///
+/// The parent enum has a case for each child enum type, to help dynamically access them when driving a table view (see
+public enum SentrySDKOverrides: String, CaseIterable {
+ public static func resetDefaults() {
+ for override in SentrySDKOverrides.allCases {
+ for flag in override.featureFlags {
+ UserDefaults.standard.removeObject(forKey: flag.rawValue)
+ }
+ }
+ }
public static var schemaPrecedenceForEnvironmentVariables: Bool {
ProcessInfo.processInfo.arguments.contains("--io.sentry.schema-environment-variable-precedence")
}
- public static func resetDefaults() {
- let allKeys = Tracing.allCases.map(\.rawValue)
- + Profiling.allCases.map(\.rawValue)
- + Performance.allCases.map(\.rawValue)
- + Other.allCases.map(\.rawValue)
- + Feedback.allCases.map(\.rawValue)
- for key in allKeys {
- defaults.removeObject(forKey: key)
+ /// Helps quickly traverse using an NSIndexPath for driving a table view.
+ var featureFlags: [any SentrySDKOverride] {
+ switch self {
+ case .special: return SentrySDKOverrides.Special.allCases
+ case .feedback: return SentrySDKOverrides.Feedback.allCases
+ case .performance: return SentrySDKOverrides.Performance.allCases
+ case .sessionReplay: return SentrySDKOverrides.SessionReplay.allCases
+ case .other: return SentrySDKOverrides.Other.allCases
+ case .tracing: return SentrySDKOverrides.Tracing.allCases
+ case .profiling: return SentrySDKOverrides.Profiling.allCases
}
}
@@ -34,16 +48,9 @@ public enum SentrySDKOverrides {
case wipeDataOnLaunch = "--io.sentry.wipe-data"
case disableEverything = "--io.sentry.disable-everything"
case skipSDKInit = "--skip-sentry-init"
-
- public var boolValue: Bool {
- get {
- return getBoolOverride(for: rawValue)
- }
- set(newValue) {
- setBoolOverride(for: rawValue, value: newValue)
- }
- }
+ case disableDebugMode = "--io.sentry.disable-debug-mode"
}
+ case special = "Special"
public enum Feedback: String, SentrySDKOverride {
case allDefaults = "--io.sentry.feedback.all-defaults"
@@ -56,16 +63,10 @@ public enum SentrySDKOverrides {
case noAnimations = "--io.sentry.feedback.no-animations"
case injectScreenshot = "--io.sentry.feedback.inject-screenshot"
case useCustomFeedbackButton = "--io.sentry.feedback.use-custom-feedback-button"
-
- public var boolValue: Bool {
- get {
- return getBoolOverride(for: rawValue)
- }
- set(newValue) {
- setBoolOverride(for: rawValue, value: newValue)
- }
- }
+ case noScreenshots = "--io.sentry.feedback.no-screenshots"
+ case noShakeGesture = "--io.sentry.feedback.no-shake-gesture"
}
+ case feedback = "Feedback"
public enum Performance: String, SentrySDKOverride {
case disableTimeToFullDisplayTracing = "--disable-time-to-full-display-tracing"
@@ -81,100 +82,51 @@ public enum SentrySDKOverrides {
case disableUITracing = "--disable-ui-tracing"
case disablePrewarmedAppStartTracing = "--disable-prewarmed-app-start-tracing"
case disablePerformanceTracing = "--disable-auto-performance-tracing"
+ case sessionTrackingIntervalMillis = "--io.sentry.sessionTrackingIntervalMillis"
+ }
+ case performance = "Performance"
- public var boolValue: Bool {
- get {
- return getBoolOverride(for: "--io.sentry.disable-everything") || getBoolOverride(for: rawValue)
- }
- set(newValue) {
- setBoolOverride(for: rawValue, value: newValue)
- }
- }
+ public enum SessionReplay: String, SentrySDKOverride {
+ case disableSessionReplay = "--disable-session-replay"
+ case disableViewRendererV2 = "--io.sentry.session-replay.disableViewRendereV2"
+ case enableFastViewRendering = "--io.sentry.session-replay.enableFastViewRendering"
+ case sampleRate = "--io.sentry.sessionReplaySampleRate"
+ case onErrorSampleRate = "--io.sentry.sessionReplayOnErrorSampleRate"
+ case quality = "--io.sentry.sessionReplayQuality"
+ case disableMaskAllText = "--io.sentry.session-replay.disable-mask-all-text"
+ case disableMaskAllImages = "--io.sentry.session-replay.disable-mask-all-images"
}
+ case sessionReplay = "Session Replay"
public enum Other: String, SentrySDKOverride {
case disableAttachScreenshot = "--disable-attach-screenshot"
case disableAttachViewHierarchy = "--disable-attach-view-hierarchy"
- case disableSessionReplay = "--disable-session-replay"
+ case rejectAllEvents = "--reject-all-events"
+ case rejectAllSpans = "--reject-all-spans"
+ case rejectScreenshots = "--reject-screenshots-in-before-capture-screenshot"
+ case rejectViewHierarchy = "--reject-view-hierarchy-in-before-capture-view-hierarchy"
case disableMetricKit = "--disable-metrickit-integration"
+ case disableMetricKitRawPayloads = "--disable-metrickit-raw-payloads"
case disableBreadcrumbs = "--disable-automatic-breadcrumbs"
case disableNetworkBreadcrumbs = "--disable-network-breadcrumbs"
case disableSwizzling = "--disable-swizzling"
case disableCrashHandling = "--disable-crash-handler"
case disableSpotlight = "--disable-spotlight"
case disableFileManagerSwizzling = "--disable-filemanager-swizzling"
- case userName = "--io.sentry.user.name"
+ case username = "--io.sentry.user.username"
+ case userFullName = "--io.sentry.user.name"
case userEmail = "--io.sentry.user.email"
-
- public var boolValue: Bool {
- get {
- switch self {
- case .userName, .userEmail: fatalError("Use stringValue to get the value of this override")
- default: return getBoolOverride(for: "--io.sentry.disable-everything") || getBoolOverride(for: rawValue)
- }
- }
- set(newValue) {
- setBoolOverride(for: rawValue, value: newValue)
- }
- }
-
- public var stringValue: String? {
- get {
- switch self {
- case .userName, .userEmail: return getStringValueOverride(for: rawValue)
- default: fatalError("Use boolValue to get the value of this override")
- }
- }
- set(newValue) {
- switch self {
- case .userName, .userEmail: return setStringOverride(for: rawValue, value: newValue)
- default: fatalError("Use boolValue to get the value of this override")
- }
- }
- }
-
- public static var boolValues: [Other] { [.disableAttachScreenshot, .disableAttachViewHierarchy, .disableSessionReplay, .disableMetricKit, .disableBreadcrumbs, .disableNetworkBreadcrumbs, .disableSwizzling, .disableCrashHandling, .disableSpotlight, .disableFileManagerSwizzling] }
- public static var stringVars: [Other] { [.userName, .userEmail] }
+ case userID = "--io.sentry.user.id"
+ case environment = "--io.sentry.sdk-environment"
}
+ case other = "Other"
public enum Tracing: String, SentrySDKOverride {
case sampleRate = "--io.sentry.tracesSampleRate"
case samplerValue = "--io.sentry.tracesSamplerValue"
case disableTracing = "--io.sentry.disable-tracing"
-
- public var boolValue: Bool {
- get {
- switch self {
- case .sampleRate, .samplerValue: fatalError("Use floatValue to get the value of this override")
- default: return getBoolOverride(for: "--io.sentry.disable-everything") || getBoolOverride(for: rawValue)
- }
- }
- set(newValue) {
- switch self {
- case .sampleRate, .samplerValue: fatalError("Use floatValue to get the value of this override")
- default: setBoolOverride(for: rawValue, value: newValue)
- }
- }
- }
-
- public var floatValue: Float? {
- get {
- switch self {
- case .disableTracing: fatalError("Use boolValue to get the value of this override")
- default: return getFloatValueOverride(for: rawValue)
- }
- }
- set(newValue) {
- switch self {
- case .disableTracing: fatalError("Use boolValue to get the value of this override")
- default: setFloatOverride(for: rawValue, value: newValue)
- }
- }
- }
-
- public static var boolValues: [Tracing] { [.disableTracing] }
- public static var floatValues: [Tracing] { [.sampleRate, .samplerValue] }
}
+ case tracing = "Tracing"
public enum Profiling: String, SentrySDKOverride {
case sampleRate = "--io.sentry.profilesSampleRate"
@@ -183,63 +135,69 @@ public enum SentrySDKOverrides {
case manualLifecycle = "--io.sentry.profile-lifecycle-manual"
case sessionSampleRate = "--io.sentry.profile-session-sample-rate"
case disableUIProfiling = "--io.sentry.disable-ui-profiling"
+ }
+ case profiling = "Profiling"
+}
- public var boolValue: Bool {
- get {
- switch self {
- case .sampleRate, .samplerValue, .sessionSampleRate: fatalError("Use floatValue to get the value of this override")
- case .disableUIProfiling, .disableAppStartProfiling: return getBoolOverride(for: "--io.sentry.disable-everything") || getBoolOverride(for: rawValue)
- default: return getBoolOverride(for: rawValue)
- }
- }
- set(newValue) {
- switch self {
- case .sampleRate, .samplerValue, .sessionSampleRate: fatalError("Use floatValue to get the value of this override")
- default: setBoolOverride(for: rawValue, value: newValue)
- }
- }
+// MARK: Public flag/variable value access
+
+public extension SentrySDKOverride {
+ var boolValue: Bool {
+ get {
+ guard overrideType == .boolean else { fatalError("Unsupported bool override: \(self.rawValue)") }
+ return Self.getBoolOverride(for: rawValue)
}
+ set(newValue) {
+ guard overrideType == .boolean else { fatalError("Unsupported bool override: \(self.rawValue)") }
+ Self.setBoolOverride(for: rawValue, value: newValue)
+ }
+ }
- public var floatValue: Float? {
- get {
- switch self {
- case .disableUIProfiling, .disableAppStartProfiling, .manualLifecycle: fatalError("Use boolValue to get the value of this override")
- default: return getFloatValueOverride(for: rawValue)
- }
- }
- set(newValue) {
- switch self {
- case .disableUIProfiling, .disableAppStartProfiling, .manualLifecycle: fatalError("Use boolValue to get the value of this override")
- default: setFloatOverride(for: rawValue, value: newValue)
- }
- }
+ var floatValue: Float? {
+ get {
+ guard overrideType == .float else { fatalError("Unsupported float override: \(self.rawValue)") }
+ return Self.getFloatValueOverride(for: rawValue)
+ }
+ set(newValue) {
+ guard overrideType == .float else { fatalError("Unsupported float override: \(self.rawValue)") }
+ Self.setFloatOverride(for: rawValue, value: newValue)
}
+ }
- public static var boolValues: [Profiling] { [.disableUIProfiling, .disableAppStartProfiling, .manualLifecycle] }
- public static var floatValues: [Profiling] { [.sampleRate, .samplerValue, .sessionSampleRate] }
+ var stringValue: String? {
+ get {
+ guard overrideType == .string else { fatalError("Unsupported string override: \(self.rawValue)") }
+ return Self.getStringValueOverride(for: rawValue)
+ }
+ set(newValue) {
+ guard overrideType == .string else { fatalError("Unsupported string override: \(self.rawValue)") }
+ Self.setStringOverride(for: rawValue, value: newValue)
+ }
}
}
-private extension SentrySDKOverrides {
+// MARK: Private flag/variable value access helpers
+
+private extension SentrySDKOverride {
static func getBoolOverride(for key: String) -> Bool {
- ProcessInfo.processInfo.arguments.contains(key) || defaults.bool(forKey: key)
+ ProcessInfo.processInfo.arguments.contains(key) || UserDefaults.standard.bool(forKey: key)
}
static func setBoolOverride(for key: String, value: Bool) {
- defaults.set(value, forKey: key)
+ UserDefaults.standard.set(value, forKey: key)
}
static func setFloatOverride(for key: String, value: Float?) {
guard let value = value else {
- defaults.removeObject(forKey: key)
+ UserDefaults.standard.removeObject(forKey: key)
return
}
-
+
setStringOverride(for: key, value: String(format: "%f", value))
}
static func setStringOverride(for key: String, value: String?) {
- defaults.set(value, forKey: key)
+ UserDefaults.standard.set(value, forKey: key)
}
static func getFloatValueOverride(for key: String) -> Float? {
@@ -252,12 +210,78 @@ private extension SentrySDKOverrides {
schemaEnvironmentVariable = value
}
- let defaultsValue = defaults.string(forKey: key)
+ let defaultsValue = UserDefaults.standard.string(forKey: key)
- if schemaPrecedenceForEnvironmentVariables {
+ if SentrySDKOverrides.schemaPrecedenceForEnvironmentVariables {
return schemaEnvironmentVariable ?? defaultsValue
} else {
return defaultsValue ?? schemaEnvironmentVariable
}
}
}
+
+// MARK: Feature flag types
+
+// These are listed exhaustively, without using default cases, so that when new cases are added to the enums above, the compiler helps remind you to annotate what type it is down here.
+
+extension SentrySDKOverrides.Profiling {
+ public var overrideType: OverrideType {
+ switch self {
+ case .sampleRate, .samplerValue, .sessionSampleRate: return .float
+ case .disableAppStartProfiling, .manualLifecycle, .disableUIProfiling: return .boolean
+ }
+ }
+}
+
+extension SentrySDKOverrides.Tracing {
+ public var overrideType: OverrideType {
+ switch self {
+ case .sampleRate, .samplerValue: return .float
+ case .disableTracing: return .boolean
+ }
+ }
+}
+
+extension SentrySDKOverrides.Other {
+ public var overrideType: OverrideType {
+ switch self {
+ case .disableAttachScreenshot, .disableAttachViewHierarchy, .rejectScreenshots, .rejectViewHierarchy, .disableMetricKit, .disableMetricKitRawPayloads, .disableBreadcrumbs, .disableNetworkBreadcrumbs, .disableSwizzling, .disableCrashHandling, .disableSpotlight, .disableFileManagerSwizzling, .rejectAllSpans, .rejectAllEvents: return .boolean
+ case .username, .userFullName, .userEmail, .userID, .environment: return .string
+ }
+ }
+}
+
+extension SentrySDKOverrides.Performance {
+ public var overrideType: OverrideType {
+ switch self {
+ case .disableTimeToFullDisplayTracing, .disablePerformanceV2, .disableAppHangTrackingV2, .disableSessionTracking, .disableFileIOTracing, .disableUIVCTracing, .disableNetworkTracing, .disableCoreDataTracing, .disableANRTracking, .disableWatchdogTracking, .disableUITracing, .disablePrewarmedAppStartTracing, .disablePerformanceTracing: return .boolean
+ case .sessionTrackingIntervalMillis: return .string
+ }
+ }
+}
+
+extension SentrySDKOverrides.SessionReplay {
+ public var overrideType: OverrideType {
+ switch self {
+ case .disableSessionReplay, .disableViewRendererV2, .enableFastViewRendering, .disableMaskAllText, .disableMaskAllImages: return .boolean
+ case .onErrorSampleRate, .sampleRate: return .float
+ case .quality: return .string
+ }
+ }
+}
+
+extension SentrySDKOverrides.Feedback {
+ public var overrideType: OverrideType {
+ switch self {
+ case .allDefaults, .disableAutoInject, .noWidgetText, .noWidgetIcon, .noUserInjection, .requireEmail, .requireName, .noAnimations, .injectScreenshot, .useCustomFeedbackButton, .noScreenshots, .noShakeGesture: return .boolean
+ }
+ }
+}
+
+extension SentrySDKOverrides.Special {
+ public var overrideType: OverrideType {
+ switch self {
+ case .wipeDataOnLaunch, .disableEverything, .skipSDKInit, .disableDebugMode: return .boolean
+ }
+ }
+}
diff --git a/Samples/SentrySampleShared/SentrySampleShared/SentrySDKWrapper.swift b/Samples/SentrySampleShared/SentrySampleShared/SentrySDKWrapper.swift
index 9921a12374c..b76d5a2d3dd 100644
--- a/Samples/SentrySampleShared/SentrySampleShared/SentrySDKWrapper.swift
+++ b/Samples/SentrySampleShared/SentrySampleShared/SentrySDKWrapper.swift
@@ -33,30 +33,40 @@ public struct SentrySDKWrapper {
func configureSentryOptions(options: Options) {
options.dsn = dsn
- options.beforeSend = { $0 }
- options.beforeSendSpan = { $0 }
- options.beforeCaptureScreenshot = { _ in true }
- options.beforeCaptureViewHierarchy = { _ in true }
- options.debug = true
+ options.beforeSend = {
+ guard !SentrySDKOverrides.Other.rejectAllEvents.boolValue else { return nil }
+ return $0
+ }
+ options.beforeSendSpan = {
+ guard !SentrySDKOverrides.Other.rejectAllSpans.boolValue else { return nil }
+ return $0
+ }
+ options.beforeCaptureScreenshot = { _ in !SentrySDKOverrides.Other.rejectScreenshots.boolValue }
+ options.beforeCaptureViewHierarchy = { _ in !SentrySDKOverrides.Other.rejectViewHierarchy.boolValue }
+ options.debug = !SentrySDKOverrides.Special.disableDebugMode.boolValue
#if !os(macOS) && !os(watchOS) && !os(visionOS)
- if #available(iOS 16.0, *), !SentrySDKOverrides.Other.disableSessionReplay.boolValue {
+ if #available(iOS 16.0, *), !SentrySDKOverrides.SessionReplay.disableSessionReplay.boolValue {
options.sessionReplay = SentryReplayOptions(
- sessionSampleRate: 0,
- onErrorSampleRate: 1,
- maskAllText: true,
- maskAllImages: true
+ sessionSampleRate: SentrySDKOverrides.SessionReplay.sampleRate.floatValue ?? 0,
+ onErrorSampleRate: SentrySDKOverrides.SessionReplay.onErrorSampleRate.floatValue ?? 1,
+ maskAllText: !SentrySDKOverrides.SessionReplay.disableMaskAllText.boolValue,
+ maskAllImages: !SentrySDKOverrides.SessionReplay.disableMaskAllImages.boolValue
)
- options.sessionReplay.quality = .high
- options.sessionReplay.enableViewRendererV2 = true
- // Disable the fast view renderering, because we noticed parts (like the tab bar) are not rendered correctly
- options.sessionReplay.enableFastViewRendering = false
+
+ let defaultReplayQuality = SentryReplayOptions.SentryReplayQuality.high
+ options.sessionReplay.quality = SentryReplayOptions.SentryReplayQuality(rawValue: (SentrySDKOverrides.SessionReplay.quality.stringValue as? NSString)?.integerValue ?? defaultReplayQuality.rawValue) ?? defaultReplayQuality
+
+ options.sessionReplay.enableViewRendererV2 = !SentrySDKOverrides.SessionReplay.disableViewRendererV2.boolValue
+
+ // Disable the fast view rendering, because we noticed parts (like the tab bar) are not rendered correctly
+ options.sessionReplay.enableFastViewRendering = SentrySDKOverrides.SessionReplay.enableFastViewRendering.boolValue
}
#if !os(tvOS)
if #available(iOS 15.0, *), !SentrySDKOverrides.Other.disableMetricKit.boolValue {
options.enableMetricKit = true
- options.enableMetricKitRawPayload = true
+ options.enableMetricKitRawPayload = !SentrySDKOverrides.Other.disableMetricKitRawPayloads.boolValue
}
#endif // !os(tvOS)
#endif // !os(macOS) && !os(watchOS) && !os(visionOS)
@@ -76,7 +86,7 @@ public struct SentrySDKWrapper {
#endif // !os(tvOS) && !os(watchOS) && !os(visionOS)
options.enableAutoSessionTracking = !SentrySDKOverrides.Performance.disableSessionTracking.boolValue
- if let sessionTrackingIntervalMillis = env["--io.sentry.sessionTrackingIntervalMillis"] {
+ if let sessionTrackingIntervalMillis = SentrySDKOverrides.Performance.sessionTrackingIntervalMillis.stringValue {
options.sessionTrackingIntervalMillis = UInt((sessionTrackingIntervalMillis as NSString).integerValue)
}
@@ -141,7 +151,7 @@ public struct SentrySDKWrapper {
}
func configureInitialScope(scope: Scope) -> Scope {
- if let environmentOverride = self.env["--io.sentry.sdk-environment"] {
+ if let environmentOverride = SentrySDKOverrides.Other.environment.stringValue {
scope.setEnvironment(environmentOverride)
} else if isBenchmarking {
scope.setEnvironment("benchmarking")
@@ -161,8 +171,8 @@ public struct SentrySDKWrapper {
injectGitInformation(scope: scope)
- let user = User(userId: "1")
- user.email = self.env["--io.sentry.user.email"] ?? "tony@example.com"
+ let user = User(userId: SentrySDKOverrides.Other.userID.stringValue ?? "1")
+ user.email = SentrySDKOverrides.Other.userEmail.stringValue ?? "tony@example.com"
user.username = username
user.name = userFullName
scope.setUser(user)
@@ -176,7 +186,7 @@ public struct SentrySDKWrapper {
}
var userFullName: String {
- let name = self.env["--io.sentry.user.name"] ?? NSFullUserName()
+ let name = SentrySDKOverrides.Other.userFullName.stringValue ?? NSFullUserName()
guard !name.isEmpty else {
return "cocoa developer"
}
@@ -184,7 +194,7 @@ public struct SentrySDKWrapper {
}
var username: String {
- let username = self.env["--io.sentry.user.username"] ?? NSUserName()
+ let username = SentrySDKOverrides.Other.username.stringValue ?? NSUserName()
guard !username.isEmpty else {
return (self.env["SIMULATOR_HOST_HOME"] as? NSString)?
.lastPathComponent ?? "cocoadev"
@@ -200,10 +210,7 @@ extension SentrySDKWrapper {
var layoutOffset: UIOffset { UIOffset(horizontal: 25, vertical: 75) }
func configureFeedbackWidget(config: SentryUserFeedbackWidgetConfiguration) {
- guard !SentrySDKOverrides.Feedback.disableAutoInject.boolValue else {
- config.autoInject = false
- return
- }
+ config.autoInject = !SentrySDKOverrides.Feedback.disableAutoInject.boolValue
if Locale.current.languageCode == "ar" { // arabic
config.labelText = "﷽"
@@ -221,9 +228,7 @@ extension SentrySDKWrapper {
if SentrySDKOverrides.Feedback.noWidgetText.boolValue {
config.labelText = nil
}
- if SentrySDKOverrides.Feedback.noWidgetIcon.boolValue {
- config.showIcon = false
- }
+ config.showIcon = !SentrySDKOverrides.Feedback.noWidgetIcon.boolValue
}
func configureFeedbackForm(config: SentryUserFeedbackFormConfiguration) {
@@ -275,8 +280,8 @@ extension SentrySDKWrapper {
}
config.animations = !SentrySDKOverrides.Feedback.noAnimations.boolValue
- config.useShakeGesture = true
- config.showFormForScreenshots = true
+ config.useShakeGesture = !SentrySDKOverrides.Feedback.noShakeGesture.boolValue
+ config.showFormForScreenshots = !SentrySDKOverrides.Feedback.noScreenshots.boolValue
config.configureWidget = configureFeedbackWidget(config:)
config.configureForm = configureFeedbackForm(config:)
config.configureTheme = configureFeedbackTheme(config:)
diff --git a/Samples/Shared/feature-flags.yml b/Samples/Shared/feature-flags.yml
index 3025987c720..a6721f05de8 100644
--- a/Samples/Shared/feature-flags.yml
+++ b/Samples/Shared/feature-flags.yml
@@ -2,19 +2,21 @@ schemeTemplates:
SampleAppScheme:
run:
commandLineArguments:
+ "--io.sentry.schema-environment-variable-precedence": true
"--io.sentry.disable-everything": false
+ "--skip-sentry-init": false
+ "--io.sentry.wipe-data": false
+ "--io.sentry.disable-debug-mode": false
+
+ # session replay
+ "--disable-session-replay": false
+ "--io.sentry.session-replay.disableViewRendereV2": false
+ "--io.sentry.session-replay.enableFastViewRendering": false
+ "--io.sentry.session-replay.disable-mask-all-text": false
+ "--io.sentry.session-replay.disable-mask-all-images": false
+
+ # user feedback
"--io.sentry.ui-test.use-custom-feedback-button": true
- "--io.sentry.disable-ui-profiling": false
- "--disable-app-hang-tracking-v2": false
- "--io.sentry.schema-environment-variable-precedence": true
- "--io.sentry.profile-lifecycle-manual": false
- "--disable-time-to-full-display-tracing": false
- "--disable-performance-v2": false
- "--disable-attach-view-hierarchy": false
- "--disable-attach-screenshot": false
- "--io.sentry.base64-attachment-data": false
- "--io.sentry.disable-http-transport": false
- "--disable-file-io-tracing": false
"--io.sentry.feedback.dont-use-sentry-user": false
"--io.sentry.feedback.require-name": false
"--io.sentry.feedback.require-email": false
@@ -22,16 +24,28 @@ schemeTemplates:
"--io.sentry.feedback.no-widget-icon": false
"--io.sentry.feedback.no-widget-text": false
"--io.sentry.feedback.all-defaults": false
- "--skip-sentry-init": false
- "--disable-spotlight": false
+ "--io.sentry.feedback.no-auto-inject-widget": false
+ "--io.sentry.feedback.no-screenshots": false
+ "--io.sentry.feedback.no-shake-gesture": false
+
+ # profiling
+ "--io.sentry.disable-ui-profiling": false
+ "--io.sentry.profile-lifecycle-manual": false
+ "--io.sentry.slow-load-method": false
+ "--io.sentry.disable-app-start-profiling": false
+
+ # performance
+ "--disable-app-hang-tracking-v2": false
+ "--disable-time-to-full-display-tracing": false
+ "--disable-performance-v2": false
+ "--disable-attach-view-hierarchy": false
+ "--disable-attach-screenshot": false
+ "--disable-file-io-tracing": false
"--disable-automatic-session-tracking": false
"--disable-metrickit-integration": false
- "--disable-session-replay": false
- "--io.sentry.wipe-data": false
- "--io.sentry.slow-load-method": false
+ "--disable-metrickit-raw-payloads": false
"--disable-watchdog-tracking": false
"--disable-tracing": false
- "--io.sentry.disable-app-start-profiling": false
"--disable-crash-handler": false
"--disable-swizzling": false
"--disable-network-breadcrumbs": false
@@ -42,12 +56,35 @@ schemeTemplates:
"--disable-anr-tracking": false
"--disable-auto-performance-tracing": false
"--disable-ui-tracing": false
- "--io.sentry.feedback.no-auto-inject-widget": false
"--disable-filemanager-swizzling": false
+
+ # other
+ "--io.sentry.base64-attachment-data": false
+ "--io.sentry.disable-http-transport": false
+ "--disable-spotlight": false
+ "--reject-screenshots-in-before-capture-screenshot": false
+ "--reject-view-hierarchy-in-before-capture-view-hierarchy": false
+ "--reject-all-events": false
+ "--reject-all-spans": false
+
environmentVariables:
+ # session replay
+ - variable: "--io.sentry.sessionReplaySampleRate"
+ value:
+ isEnabled: false
+ - variable: "--io.sentry.sessionReplayOnErrorSampleRate"
+ value:
+ isEnabled: false
+ - variable: "--io.sentry.sessionReplayQuality"
+ value:
+ isEnabled: false
+
- variable: "--io.sentry.tracesSampleRate"
value:
isEnabled: false
+ - variable: "--io.sentry.sessionTrackingIntervalMillis"
+ value:
+ isEnabled: false
- variable: "--io.sentry.profilesSampleRate"
value:
isEnabled: false
@@ -66,6 +103,12 @@ schemeTemplates:
- variable: "--io.sentry.user.username"
value:
isEnabled: false
+ - variable: "--io.sentry.user.name"
+ value:
+ isEnabled: false
+ - variable: "--io.sentry.sdk-environment"
+ value:
+ isEnabled: false
- variable: "--io.sentry.user.email"
value:
isEnabled: false
@@ -75,6 +118,9 @@ schemeTemplates:
- variable: "--io.sentry.user.name"
value:
isEnabled: false
+ - variable: "--io.sentry.user.id"
+ value:
+ isEnabled: false
- variable: "--io.sentry.profile-session-sample-rate"
value:
isEnabled: false
diff --git a/Samples/iOS-Swift/iOS-Swift/Base.lproj/Main.storyboard b/Samples/iOS-Swift/iOS-Swift/Base.lproj/Main.storyboard
index 0608e3e9756..4311950d0ab 100644
--- a/Samples/iOS-Swift/iOS-Swift/Base.lproj/Main.storyboard
+++ b/Samples/iOS-Swift/iOS-Swift/Base.lproj/Main.storyboard
@@ -1294,16 +1294,6 @@
-
diff --git a/Samples/iOS-SwiftUI/iOS-SwiftUI/SwiftUIApp.swift b/Samples/iOS-SwiftUI/iOS-SwiftUI/SwiftUIApp.swift
index 55c6d7cb0f9..3646b7fb59e 100644
--- a/Samples/iOS-SwiftUI/iOS-SwiftUI/SwiftUIApp.swift
+++ b/Samples/iOS-SwiftUI/iOS-SwiftUI/SwiftUIApp.swift
@@ -4,6 +4,8 @@ import SwiftUI
@main
struct SwiftUIApp: App {
+ @UIApplicationDelegateAdaptor private var appDelegate: MyAppDelegate
+
init() {
SentrySDKWrapper.shared.startSentry()
}
@@ -14,3 +16,24 @@ struct SwiftUIApp: App {
}
}
}
+
+class MyAppDelegate: NSObject, UIApplicationDelegate, ObservableObject {
+ func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
+ let configuration = UISceneConfiguration(
+ name: nil,
+ sessionRole: connectingSceneSession.role)
+ if connectingSceneSession.role == .windowApplication {
+ configuration.delegateClass = MySceneDelegate.self
+ }
+ return configuration
+ }
+}
+
+class MySceneDelegate: NSObject, UIWindowSceneDelegate, ObservableObject {
+ var initializedSentry = false
+ func sceneDidBecomeActive(_ scene: UIScene) {
+ guard !initializedSentry else { return }
+ SampleAppDebugMenu.shared.display()
+ initializedSentry = true
+ }
+}
diff --git a/Samples/iOS15-SwiftUI/iOS15-SwiftUI/App.swift b/Samples/iOS15-SwiftUI/iOS15-SwiftUI/App.swift
index 61537bcc2f7..3646b7fb59e 100644
--- a/Samples/iOS15-SwiftUI/iOS15-SwiftUI/App.swift
+++ b/Samples/iOS15-SwiftUI/iOS15-SwiftUI/App.swift
@@ -1,15 +1,39 @@
+import Foundation
import SentrySampleShared
import SwiftUI
@main
struct SwiftUIApp: App {
+ @UIApplicationDelegateAdaptor private var appDelegate: MyAppDelegate
+
init() {
SentrySDKWrapper.shared.startSentry()
}
-
+
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
+
+class MyAppDelegate: NSObject, UIApplicationDelegate, ObservableObject {
+ func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
+ let configuration = UISceneConfiguration(
+ name: nil,
+ sessionRole: connectingSceneSession.role)
+ if connectingSceneSession.role == .windowApplication {
+ configuration.delegateClass = MySceneDelegate.self
+ }
+ return configuration
+ }
+}
+
+class MySceneDelegate: NSObject, UIWindowSceneDelegate, ObservableObject {
+ var initializedSentry = false
+ func sceneDidBecomeActive(_ scene: UIScene) {
+ guard !initializedSentry else { return }
+ SampleAppDebugMenu.shared.display()
+ initializedSentry = true
+ }
+}