From 889f05a7e62ee477bb0206e587944766406883be Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Fri, 3 Jan 2025 15:43:36 -0900 Subject: [PATCH 1/5] refactor to extract sdk config out of app delegate --- .../iOS-Swift.xcodeproj/project.pbxproj | 6 + Samples/iOS-Swift/iOS-Swift/AppDelegate.swift | 344 +----------------- .../Tools/DSNDisplayViewController.swift | 4 +- 3 files changed, 20 insertions(+), 334 deletions(-) diff --git a/Samples/iOS-Swift/iOS-Swift.xcodeproj/project.pbxproj b/Samples/iOS-Swift/iOS-Swift.xcodeproj/project.pbxproj index 88802079bd7..27c68de6882 100644 --- a/Samples/iOS-Swift/iOS-Swift.xcodeproj/project.pbxproj +++ b/Samples/iOS-Swift/iOS-Swift.xcodeproj/project.pbxproj @@ -44,6 +44,8 @@ 84BE546F287503F100ACC735 /* SentrySDKPerformanceBenchmarkTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 84BE546E287503F100ACC735 /* SentrySDKPerformanceBenchmarkTests.m */; }; 84BE547E287645B900ACC735 /* SentryProcessInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 84BE54792876451D00ACC735 /* SentryProcessInfo.m */; }; 84DBC6252CE6D321000C4904 /* UserFeedbackUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DBC61F2CE6D31C000C4904 /* UserFeedbackUITests.swift */; }; + 84EEE6622D28B35700010A9D /* SentrySDK.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84EEE6612D28B35700010A9D /* SentrySDK.swift */; }; + 84EEE6632D28B35700010A9D /* SentrySDK.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84EEE6612D28B35700010A9D /* SentrySDK.swift */; }; 84FB812A284001B800F3A94A /* SentryBenchmarking.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84FB8129284001B800F3A94A /* SentryBenchmarking.mm */; }; 84FB812B284001B800F3A94A /* SentryBenchmarking.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84FB8129284001B800F3A94A /* SentryBenchmarking.mm */; }; 8E8C57AF25EF16E6001CEEFA /* TraceTestViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E8C57AE25EF16E6001CEEFA /* TraceTestViewController.swift */; }; @@ -290,6 +292,7 @@ 84BE54782876451D00ACC735 /* SentryProcessInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentryProcessInfo.h; sourceTree = ""; }; 84BE54792876451D00ACC735 /* SentryProcessInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryProcessInfo.m; sourceTree = ""; }; 84DBC61F2CE6D31C000C4904 /* UserFeedbackUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserFeedbackUITests.swift; sourceTree = ""; }; + 84EEE6612D28B35700010A9D /* SentrySDK.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentrySDK.swift; sourceTree = ""; }; 84FB8125284001B800F3A94A /* SentryBenchmarking.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentryBenchmarking.h; sourceTree = ""; }; 84FB8129284001B800F3A94A /* SentryBenchmarking.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SentryBenchmarking.mm; sourceTree = ""; }; 84FB812C2840021B00F3A94A /* iOS-Swift-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "iOS-Swift-Bridging-Header.h"; sourceTree = ""; }; @@ -483,6 +486,7 @@ D8DBDA73274D4DF900007380 /* ViewControllers */, 63F93AA9245AC91600A500DB /* iOS-Swift.entitlements */, 637AFDA9243B02760034958B /* AppDelegate.swift */, + 84EEE6612D28B35700010A9D /* SentrySDK.swift */, 637AFDAD243B02760034958B /* TransactionsViewController.swift */, 84AB90782A50031B0054C99A /* Profiling */, D80D021229EE93630084393D /* ErrorsViewController.swift */, @@ -1133,6 +1137,7 @@ 84BA71F12C8BC55A0045B828 /* Toasts.swift in Sources */, 629EC8AD2B0B537400858855 /* TriggerAppHang.swift in Sources */, D8AE48C92C57DC2F0092A2A6 /* WebViewController.swift in Sources */, + 84EEE6632D28B35700010A9D /* SentrySDK.swift in Sources */, D8DBDA78274D5FC400007380 /* SplitViewController.swift in Sources */, 84ACC43C2A73CB5900932A18 /* ProfilingNetworkScanner.swift in Sources */, D80D021A29EE936F0084393D /* ExtraViewController.swift in Sources */, @@ -1182,6 +1187,7 @@ 924857562C89A86300774AC3 /* MainViewController.swift in Sources */, D8F3D058274E57D600B56F8C /* TableViewController.swift in Sources */, 7B5525B62938B644006A2932 /* DiskWriteException.swift in Sources */, + 84EEE6622D28B35700010A9D /* SentrySDK.swift in Sources */, D8269A58274C0FC700BD5BD5 /* TransactionsViewController.swift in Sources */, 844DA821282584C300E6B62E /* CoreDataViewController.swift in Sources */, D8444E55275F79570042F4DE /* SpanExtension.swift in Sources */, diff --git a/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift b/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift index 7014f0b7265..3dbb7bbb1b5 100644 --- a/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift +++ b/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift @@ -5,13 +5,19 @@ import UIKit class AppDelegate: UIResponder, UIApplicationDelegate { private var randomDistributionTimer: Timer? var window: UIWindow? + + var args: [String] { + let args = ProcessInfo.processInfo.arguments + print("[iOS-Swift] [debug] launch arguments: \(args)") + return args + } func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { if args.contains("--io.sentry.wipe-data") { removeAppData() } if !args.contains("--skip-sentry-init") { - startSentry() + SentrySDKWrapper.shared.startSentry() } if #available(iOS 15.0, *) { @@ -54,338 +60,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate { private func removeAppData() { print("[iOS-Swift] [debug] removing app data") let cache = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first! - guard let files = FileManager.default.enumerator(atPath: cache) else { return } - for item in files { - try! FileManager.default.removeItem(atPath: (cache as NSString).appendingPathComponent((item as! String))) - } - } -} - -// MARK: SDK Configuration -extension AppDelegate { - func startSentry() { - SentrySDK.start(configureOptions: configureSentryOptions(options:)) - } - - func configureSentryOptions(options: Options) { - options.dsn = dsn - options.beforeSend = { $0 } - options.beforeSendSpan = { $0 } - options.beforeCaptureScreenshot = { _ in true } - options.beforeCaptureViewHierarchy = { _ in true } - options.debug = true - - if #available(iOS 16.0, *), enableSessionReplay { - options.sessionReplay = SentryReplayOptions(sessionSampleRate: 0, onErrorSampleRate: 1, maskAllText: true, maskAllImages: true) - options.sessionReplay.quality = .high - } - - if #available(iOS 15.0, *), enableMetricKit { - options.enableMetricKit = true - options.enableMetricKitRawPayload = true - } - - options.tracesSampleRate = tracesSampleRate - options.tracesSampler = tracesSampler - options.profilesSampleRate = profilesSampleRate - options.profilesSampler = profilesSampler - options.enableAppLaunchProfiling = enableAppLaunchProfiling - - options.enableAutoSessionTracking = enableSessionTracking - if let sessionTrackingIntervalMillis = env["--io.sentry.sessionTrackingIntervalMillis"] { - options.sessionTrackingIntervalMillis = UInt((sessionTrackingIntervalMillis as NSString).integerValue) - } - - options.add(inAppInclude: "iOS_External") - - options.enableUserInteractionTracing = enableUITracing - options.enableAppHangTracking = enableANRTracking - options.enableWatchdogTerminationTracking = enableWatchdogTracking - options.enableAutoPerformanceTracing = enablePerformanceTracing - options.enablePreWarmedAppStartTracing = enablePrewarmedAppStartTracing - options.enableFileIOTracing = enableFileIOTracing - options.enableAutoBreadcrumbTracking = enableBreadcrumbs - options.enableUIViewControllerTracing = enableUIVCTracing - options.enableNetworkTracking = enableNetworkTracing - options.enableCoreDataTracing = enableCoreDataTracing - options.enableNetworkBreadcrumbs = enableNetworkBreadcrumbs - options.enableSwizzling = enableSwizzling - options.enableCrashHandler = enableCrashHandling - options.enableTracing = enableTracing - options.enablePersistingTracesWhenCrashing = true - options.attachScreenshot = enableAttachScreenshot - options.attachViewHierarchy = enableAttachViewHierarchy - options.enableTimeToFullDisplayTracing = enableTimeToFullDisplayTracing - options.enablePerformanceV2 = enablePerformanceV2 - options.failedRequestStatusCodes = [ HttpStatusCodeRange(min: 400, max: 599) ] - - options.beforeBreadcrumb = { breadcrumb in - //Raising notifications when a new breadcrumb is created in order to use this information - //to validate whether proper breadcrumb are being created in the right places. - NotificationCenter.default.post(name: .init("io.sentry.newbreadcrumb"), object: breadcrumb) - return breadcrumb - } - - options.initialScope = configureInitialScope(scope:) - options.configureUserFeedback = configureFeedback(config:) - } - - func configureInitialScope(scope: Scope) -> Scope { - if let environmentOverride = self.env["--io.sentry.sdk-environment"] { - scope.setEnvironment(environmentOverride) - } else if isBenchmarking { - scope.setEnvironment("benchmarking") - } else { -#if targetEnvironment(simulator) - scope.setEnvironment("simulator") -#else - scope.setEnvironment("device") -#endif // targetEnvironment(simulator) - } - - scope.setTag(value: "swift", key: "language") - - scope.injectGitInformation() - - let user = User(userId: "1") - user.email = self.env["--io.sentry.user.email"] ?? "tony@example.com" - // first check if the username has been overridden in the scheme for testing purposes; then try to use the system username so each person gets an automatic way to easily filter things on the dashboard; then fall back on a hardcoded value if none of these are present - let username = self.env["--io.sentry.user.username"] ?? (self.env["SIMULATOR_HOST_HOME"] as? NSString)? - .lastPathComponent ?? "cocoadev" - user.username = username - user.name = self.env["--io.sentry.user.name"] ?? "cocoa developer" - scope.setUser(user) - - if let path = Bundle.main.path(forResource: "Tongariro", ofType: "jpg") { - scope.addAttachment(Attachment(path: path, filename: "Tongariro.jpg", contentType: "image/jpeg")) - } - if let data = "hello".data(using: .utf8) { - scope.addAttachment(Attachment(data: data, filename: "log.txt")) - } - return scope - } -} - -// MARK: User feedback configuration -extension AppDelegate { - var layoutOffset: UIOffset { UIOffset(horizontal: 25, vertical: 75) } - - func configureFeedbackWidget(config: SentryUserFeedbackWidgetConfiguration) { - if args.contains("--io.sentry.feedback.auto-inject-widget") { - if Locale.current.languageCode == "ar" { // arabic - config.labelText = "﷽" - } else if Locale.current.languageCode == "ur" { // urdu - config.labelText = "نستعلیق" - } else if Locale.current.languageCode == "he" { // hebrew - config.labelText = "עִבְרִית‎" - } else if Locale.current.languageCode == "hi" { // Hindi - config.labelText = "नागरि" - } else { - config.labelText = "Report Jank" - } - config.widgetAccessibilityLabel = "io.sentry.iOS-Swift.button.report-jank" - config.layoutUIOffset = layoutOffset - } else { - config.autoInject = false - } - if args.contains("--io.sentry.feedback.no-widget-text") { - config.labelText = nil - } - if args.contains("--io.sentry.feedback.no-widget-icon") { - config.showIcon = false - } - } - - func configureFeedbackForm(config: SentryUserFeedbackFormConfiguration) { - config.formTitle = "Jank Report" - config.isEmailRequired = args.contains("--io.sentry.feedback.require-email") - config.isNameRequired = args.contains("--io.sentry.feedback.require-name") - config.submitButtonLabel = "Report that jank" - config.addScreenshotButtonLabel = "Show us the jank" - config.removeScreenshotButtonLabel = "Oof too nsfl" - config.cancelButtonLabel = "What, me worry?" - config.messagePlaceholder = "Describe the nature of the jank. Its essence, if you will." - config.namePlaceholder = "Yo name" - config.emailPlaceholder = "Yo email" - config.messageLabel = "Thy complaint" - config.emailLabel = "Thine email" - config.nameLabel = "Thy name" - } - - func configureFeedbackTheme(config: SentryUserFeedbackThemeConfiguration) { - let fontFamily: String - if Locale.current.languageCode == "ar" { // arabic; ar_EG - fontFamily = "Damascus" - } else if Locale.current.languageCode == "ur" { // urdu; ur_PK - fontFamily = "NotoNastaliq" - } else if Locale.current.languageCode == "he" { // hebrew; he_IL - fontFamily = "Arial Hebrew" - } else if Locale.current.languageCode == "hi" { // Hindi; hi_IN - fontFamily = "DevanagariSangamMN" - } else { - fontFamily = "ChalkboardSE-Regular" - } - config.fontFamily = fontFamily - config.outlineStyle = .init(outlineColor: .purple) - config.foreground = .purple - config.background = .init(red: 0.95, green: 0.9, blue: 0.95, alpha: 1) - config.submitBackground = .orange - config.submitForeground = .purple - config.buttonBackground = .purple - config.buttonForeground = .white - } - - func configureFeedback(config: SentryUserFeedbackConfiguration) { - guard !args.contains("--io.sentry.feedback.all-defaults") else { - config.configureWidget = { widget in - widget.layoutUIOffset = self.layoutOffset + let appSupport = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first! + [cache, appSupport].forEach { + guard let files = FileManager.default.enumerator(atPath: $0) else { return } + for item in files { + try! FileManager.default.removeItem(atPath: ($0 as NSString).appendingPathComponent((item as! String))) } - return - } - - config.useSentryUser = args.contains("--io.sentry.feedback.use-sentry-user") - config.animations = !args.contains("--io.sentry.feedback.no-animations") - config.useShakeGesture = true - config.showFormForScreenshots = true - config.configureWidget = configureFeedbackWidget(config:) - config.configureForm = configureFeedbackForm(config:) - config.configureTheme = configureFeedbackTheme(config:) - config.onFormOpen = { - let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first! - let dir = "\(appSupportDirectory)/io.sentry/feedback" - try! FileManager.default.createDirectory(atPath: dir, withIntermediateDirectories: true) - assert(FileManager.default.createFile(atPath: "\(dir)/onFormOpen", contents: nil)) - } - config.onFormClose = { - let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first! - let dir = "\(appSupportDirectory)/io.sentry/feedback" - try! FileManager.default.createDirectory(atPath: dir, withIntermediateDirectories: true) - assert(FileManager.default.createFile(atPath: "\(dir)/onFormClose", contents: nil)) - } - config.onSubmitSuccess = { info in - let name = info["name"] ?? "$shakespearean_insult_name" - let alert = UIAlertController(title: "Thanks?", message: "We have enough jank of our own, we really didn't need yours too, \(name).", preferredStyle: .alert) - alert.addAction(.init(title: "Deal with it 🕶️", style: .default)) - self.window?.rootViewController?.present(alert, animated: true) - } - config.onSubmitError = { error in - let alert = UIAlertController(title: "D'oh", message: "You tried to report jank, and encountered more jank. The jank has you now: \(error).", preferredStyle: .alert) - alert.addAction(.init(title: "Derp", style: .default)) - self.window?.rootViewController?.present(alert, animated: true) - } - } -} - -// MARK: Convenience access to SDK configuration via launch arg / environment variable -extension AppDelegate { - static let defaultDSN = "https://6cc9bae94def43cab8444a99e0031c28@o447951.ingest.sentry.io/5428557" - - var args: [String] { - let args = ProcessInfo.processInfo.arguments - print("[iOS-Swift] [debug] launch arguments: \(args)") - return args - } - - var env: [String: String] { - let env = ProcessInfo.processInfo.environment - print("[iOS-Swift] [debug] environment: \(env)") - return env - } - - /// For testing purposes, we want to be able to change the DSN and store it to disk. In a real app, you shouldn't need this behavior. - var dsn: String? { - do { - if let dsn = env["--io.sentry.dsn"] { - try DSNStorage.shared.saveDSN(dsn: dsn) - } - return try DSNStorage.shared.getDSN() ?? AppDelegate.defaultDSN - } catch { - print("[iOS-Swift] Error encountered while reading stored DSN: \(error)") - } - return nil - } - - /// Whether or not profiling benchmarks are being run; this requires disabling certain other features for proper functionality. - var isBenchmarking: Bool { args.contains("--io.sentry.test.benchmarking") } - var isUITest: Bool { env["--io.sentry.sdk-environment"] == "ui-tests" } - - func checkDisabled(with arg: String) -> Bool { - args.contains("--io.sentry.disable-everything") || args.contains(arg) - } - - // MARK: features that care about simulator vs device, ui tests and profiling benchmarks - var enableSpotlight: Bool { -#if targetEnvironment(simulator) - !checkDisabled(with: "--disable-spotlight") -#else - false -#endif // targetEnvironment(simulator) - } - - /// - note: the benchmark test starts and stops a custom transaction using a UIButton, and automatic user interaction tracing stops the transaction that begins with that button press after the idle timeout elapses, stopping the profiler (only one profiler runs regardless of the number of concurrent transactions) - var enableUITracing: Bool { !isBenchmarking && !checkDisabled(with: "--disable-ui-tracing") } - var enablePrewarmedAppStartTracing: Bool { !isBenchmarking && !checkDisabled(with: "--disable-prewarmed-app-start-tracing") } - var enablePerformanceTracing: Bool { !isBenchmarking && !checkDisabled(with: "--disable-auto-performance-tracing") } - var enableTracing: Bool { !isBenchmarking && !checkDisabled(with: "--disable-tracing") } - /// - note: UI tests generate false OOMs - var enableWatchdogTracking: Bool { !isUITest && !isBenchmarking && !checkDisabled(with: "--disable-watchdog-tracking") } - /// - note: disable during benchmarks because we run CPU for 15 seconds at full throttle which can trigger ANRs - var enableANRTracking: Bool { !isBenchmarking && !checkDisabled(with: "--disable-anr-tracking") } - - // MARK: Other features - - var enableTimeToFullDisplayTracing: Bool { !checkDisabled(with: "--disable-time-to-full-display-tracing")} - var enableAttachScreenshot: Bool { !checkDisabled(with: "--disable-attach-screenshot")} - var enableAttachViewHierarchy: Bool { !checkDisabled(with: "--disable-attach-view-hierarchy")} - var enablePerformanceV2: Bool { !checkDisabled(with: "--disable-performance-v2")} - var enableSessionReplay: Bool { !checkDisabled(with: "--disable-session-replay") } - var enableMetricKit: Bool { !checkDisabled(with: "--disable-metrickit-integration") } - var enableSessionTracking: Bool { !checkDisabled(with: "--disable-automatic-session-tracking") } - var enableFileIOTracing: Bool { !checkDisabled(with: "--disable-file-io-tracing") } - var enableBreadcrumbs: Bool { !checkDisabled(with: "--disable-automatic-breadcrumbs") } - var enableUIVCTracing: Bool { !checkDisabled(with: "--disable-uiviewcontroller-tracing") } - var enableNetworkTracing: Bool { !checkDisabled(with: "--disable-network-tracking") } - var enableCoreDataTracing: Bool { !checkDisabled(with: "--disable-core-data-tracing") } - var enableNetworkBreadcrumbs: Bool { !checkDisabled(with: "--disable-network-breadcrumbs") } - var enableSwizzling: Bool { !checkDisabled(with: "--disable-swizzling") } - var enableCrashHandling: Bool { !checkDisabled(with: "--disable-crash-handler") } - - var tracesSampleRate: NSNumber { - guard let tracesSampleRateOverride = env["--io.sentry.tracesSampleRate"] else { - return 1 } - return NSNumber(value: (tracesSampleRateOverride as NSString).integerValue) } - - var tracesSampler: ((SamplingContext) -> NSNumber?)? { - guard let tracesSamplerValue = env["--io.sentry.tracesSamplerValue"] else { - return nil - } - - return { _ in - return NSNumber(value: (tracesSamplerValue as NSString).integerValue) - } - } - - var profilesSampleRate: NSNumber? { - if args.contains("--io.sentry.enableContinuousProfiling") { - return nil - } else if let profilesSampleRateOverride = env["--io.sentry.profilesSampleRate"] { - return NSNumber(value: (profilesSampleRateOverride as NSString).integerValue) - } else { - return 1 - } - } - - var profilesSampler: ((SamplingContext) -> NSNumber?)? { - guard let profilesSamplerValue = env["--io.sentry.profilesSamplerValue"] else { - return nil - } - - return { _ in - return NSNumber(value: (profilesSamplerValue as NSString).integerValue) - } - } - - var enableAppLaunchProfiling: Bool { args.contains("--profile-app-launches") } } diff --git a/Samples/iOS-Swift/iOS-Swift/Tools/DSNDisplayViewController.swift b/Samples/iOS-Swift/iOS-Swift/Tools/DSNDisplayViewController.swift index 9d0e7c7e15b..47beac220b5 100644 --- a/Samples/iOS-Swift/iOS-Swift/Tools/DSNDisplayViewController.swift +++ b/Samples/iOS-Swift/iOS-Swift/Tools/DSNDisplayViewController.swift @@ -137,7 +137,7 @@ class DSNDisplayViewController: UIViewController { func updateDSNLabel() { do { - let dsn = try DSNStorage.shared.getDSN() ?? AppDelegate.defaultDSN + let dsn = try DSNStorage.shared.getDSN() ?? SentrySDKWrapper.defaultDSN self.label.attributedText = dsnFieldTitleString(dsn: dsn) } catch { SentrySDK.capture(error: error) @@ -150,7 +150,7 @@ class DSNDisplayViewController: UIViewController { func dsnFieldTitleString(dsn: String) -> NSAttributedString { let defaultAnnotation = "(default)" let overriddenAnnotation = "(overridden)" - guard dsn != AppDelegate.defaultDSN else { + guard dsn != SentrySDKWrapper.defaultDSN else { let title = "DSN \(defaultAnnotation):" let stringContents = "\(title): \(dsn)" let attributedString = NSMutableAttributedString(string: stringContents) From 2bc4a96b8d6dc9aad814e10881d4102f3ea04b90 Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Fri, 3 Jan 2025 15:46:06 -0900 Subject: [PATCH 2/5] fixup! refactor to extract sdk config out of app delegate --- Samples/iOS-Swift/iOS-Swift/ExtraViewController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Samples/iOS-Swift/iOS-Swift/ExtraViewController.swift b/Samples/iOS-Swift/iOS-Swift/ExtraViewController.swift index 29955471085..819e6b680bc 100644 --- a/Samples/iOS-Swift/iOS-Swift/ExtraViewController.swift +++ b/Samples/iOS-Swift/iOS-Swift/ExtraViewController.swift @@ -163,7 +163,7 @@ class ExtraViewController: UIViewController { @IBAction func startSDK(_ sender: UIButton) { highlightButton(sender) - (UIApplication.shared.delegate as? AppDelegate)?.startSentry() + SentrySDKWrapper.shared.startSentry() } @IBAction func causeFrozenFrames(_ sender: Any) { @@ -220,7 +220,7 @@ class ExtraViewController: UIViewController { return nil } let fm = FileManager.default - guard let dsnHash = try? SentryDsn(string: AppDelegate.defaultDSN).getHash() else { + guard let dsnHash = try? SentryDsn(string: SentrySDKWrapper.defaultDSN).getHash() else { displayError(message: "Couldn't compute DSN hash.") return nil } From 5f706ac508ff5e25f5c89b83616d1fbd90f31560 Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Fri, 3 Jan 2025 15:46:19 -0900 Subject: [PATCH 3/5] fixup! fixup! refactor to extract sdk config out of app delegate --- Samples/iOS-Swift/iOS-Swift/SentrySDK.swift | 369 ++++++++++++++++++++ 1 file changed, 369 insertions(+) create mode 100644 Samples/iOS-Swift/iOS-Swift/SentrySDK.swift diff --git a/Samples/iOS-Swift/iOS-Swift/SentrySDK.swift b/Samples/iOS-Swift/iOS-Swift/SentrySDK.swift new file mode 100644 index 00000000000..8aaff908826 --- /dev/null +++ b/Samples/iOS-Swift/iOS-Swift/SentrySDK.swift @@ -0,0 +1,369 @@ +import Foundation + +struct SentrySDKWrapper { + static let shared = SentrySDKWrapper() + + func startSentry() { + SentrySDK.start(configureOptions: configureSentryOptions(options:)) + } + + func configureSentryOptions(options: Options) { + options.dsn = dsn + options.beforeSend = { $0 } + options.beforeSendSpan = { $0 } + options.beforeCaptureScreenshot = { _ in true } + options.beforeCaptureViewHierarchy = { _ in true } + options.debug = true + + if #available(iOS 16.0, *), enableSessionReplay { + options.sessionReplay = SentryReplayOptions(sessionSampleRate: 0, onErrorSampleRate: 1, maskAllText: true, maskAllImages: true) + options.sessionReplay.quality = .high + } + + if #available(iOS 15.0, *), enableMetricKit { + options.enableMetricKit = true + options.enableMetricKitRawPayload = true + } + + options.tracesSampleRate = tracesSampleRate + options.tracesSampler = tracesSampler + options.profilesSampleRate = profilesSampleRate + options.profilesSampler = profilesSampler + options.enableAppLaunchProfiling = enableAppLaunchProfiling + + options.enableAutoSessionTracking = enableSessionTracking + if let sessionTrackingIntervalMillis = env["--io.sentry.sessionTrackingIntervalMillis"] { + options.sessionTrackingIntervalMillis = UInt((sessionTrackingIntervalMillis as NSString).integerValue) + } + + options.add(inAppInclude: "iOS_External") + + options.enableUserInteractionTracing = enableUITracing + options.enableAppHangTracking = enableANRTracking + options.enableWatchdogTerminationTracking = enableWatchdogTracking + options.enableAutoPerformanceTracing = enablePerformanceTracing + options.enablePreWarmedAppStartTracing = enablePrewarmedAppStartTracing + options.enableFileIOTracing = enableFileIOTracing + options.enableAutoBreadcrumbTracking = enableBreadcrumbs + options.enableUIViewControllerTracing = enableUIVCTracing + options.enableNetworkTracking = enableNetworkTracing + options.enableCoreDataTracing = enableCoreDataTracing + options.enableNetworkBreadcrumbs = enableNetworkBreadcrumbs + options.enableSwizzling = enableSwizzling + options.enableCrashHandler = enableCrashHandling + options.enableTracing = enableTracing + options.enablePersistingTracesWhenCrashing = true + options.attachScreenshot = enableAttachScreenshot + options.attachViewHierarchy = enableAttachViewHierarchy + options.enableTimeToFullDisplayTracing = enableTimeToFullDisplayTracing + options.enablePerformanceV2 = enablePerformanceV2 + options.failedRequestStatusCodes = [ HttpStatusCodeRange(min: 400, max: 599) ] + + options.beforeBreadcrumb = { breadcrumb in + //Raising notifications when a new breadcrumb is created in order to use this information + //to validate whether proper breadcrumb are being created in the right places. + NotificationCenter.default.post(name: .init("io.sentry.newbreadcrumb"), object: breadcrumb) + return breadcrumb + } + + options.initialScope = configureInitialScope(scope:) + options.configureUserFeedback = configureFeedback(config:) + } + + func configureInitialScope(scope: Scope) -> Scope { + if let environmentOverride = self.env["--io.sentry.sdk-environment"] { + scope.setEnvironment(environmentOverride) + } else if isBenchmarking { + scope.setEnvironment("benchmarking") + } else { +#if targetEnvironment(simulator) + scope.setEnvironment("simulator") +#else + scope.setEnvironment("device") +#endif // targetEnvironment(simulator) + } + + scope.setTag(value: "swift", key: "language") + + scope.injectGitInformation() + + let user = User(userId: "1") + user.email = self.env["--io.sentry.user.email"] ?? "tony@example.com" + // first check if the username has been overridden in the scheme for testing purposes; then try to use the system username so each person gets an automatic way to easily filter things on the dashboard; then fall back on a hardcoded value if none of these are present + let username = self.env["--io.sentry.user.username"] ?? (self.env["SIMULATOR_HOST_HOME"] as? NSString)? + .lastPathComponent ?? "cocoadev" + user.username = username + user.name = self.env["--io.sentry.user.name"] ?? "cocoa developer" + scope.setUser(user) + + if let path = Bundle.main.path(forResource: "Tongariro", ofType: "jpg") { + scope.addAttachment(Attachment(path: path, filename: "Tongariro.jpg", contentType: "image/jpeg")) + } + if let data = "hello".data(using: .utf8) { + scope.addAttachment(Attachment(data: data, filename: "log.txt")) + } + return scope + } +} + +// MARK: User feedback configuration +extension SentrySDKWrapper { + var layoutOffset: UIOffset { UIOffset(horizontal: 25, vertical: 75) } + + func configureFeedbackWidget(config: SentryUserFeedbackWidgetConfiguration) { + if args.contains("--io.sentry.feedback.auto-inject-widget") { + if Locale.current.languageCode == "ar" { // arabic + config.labelText = "﷽" + } else if Locale.current.languageCode == "ur" { // urdu + config.labelText = "نستعلیق" + } else if Locale.current.languageCode == "he" { // hebrew + config.labelText = "עִבְרִית‎" + } else if Locale.current.languageCode == "hi" { // Hindi + config.labelText = "नागरि" + } else { + config.labelText = "Report Jank" + } + config.widgetAccessibilityLabel = "io.sentry.iOS-Swift.button.report-jank" + config.layoutUIOffset = layoutOffset + } else { + config.autoInject = false + } + if args.contains("--io.sentry.feedback.no-widget-text") { + config.labelText = nil + } + if args.contains("--io.sentry.feedback.no-widget-icon") { + config.showIcon = false + } + } + + func configureFeedbackForm(config: SentryUserFeedbackFormConfiguration) { + config.formTitle = "Jank Report" + config.isEmailRequired = args.contains("--io.sentry.feedback.require-email") + config.isNameRequired = args.contains("--io.sentry.feedback.require-name") + config.submitButtonLabel = "Report that jank" + config.addScreenshotButtonLabel = "Show us the jank" + config.removeScreenshotButtonLabel = "Oof too nsfl" + config.cancelButtonLabel = "What, me worry?" + config.messagePlaceholder = "Describe the nature of the jank. Its essence, if you will." + config.namePlaceholder = "Yo name" + config.emailPlaceholder = "Yo email" + config.messageLabel = "Thy complaint" + config.emailLabel = "Thine email" + config.nameLabel = "Thy name" + } + + func configureFeedbackTheme(config: SentryUserFeedbackThemeConfiguration) { + let fontFamily: String + if Locale.current.languageCode == "ar" { // arabic; ar_EG + fontFamily = "Damascus" + } else if Locale.current.languageCode == "ur" { // urdu; ur_PK + fontFamily = "NotoNastaliq" + } else if Locale.current.languageCode == "he" { // hebrew; he_IL + fontFamily = "Arial Hebrew" + } else if Locale.current.languageCode == "hi" { // Hindi; hi_IN + fontFamily = "DevanagariSangamMN" + } else { + fontFamily = "ChalkboardSE-Regular" + } + config.fontFamily = fontFamily + config.outlineStyle = .init(outlineColor: .purple) + config.foreground = .purple + config.background = .init(red: 0.95, green: 0.9, blue: 0.95, alpha: 1) + config.submitBackground = .orange + config.submitForeground = .purple + config.buttonBackground = .purple + config.buttonForeground = .white + } + + func configureFeedback(config: SentryUserFeedbackConfiguration) { + guard !args.contains("--io.sentry.feedback.all-defaults") else { + config.configureWidget = { widget in + widget.layoutUIOffset = self.layoutOffset + } + configureHooks(config: config) + return + } + + config.useSentryUser = args.contains("--io.sentry.feedback.use-sentry-user") + config.animations = !args.contains("--io.sentry.feedback.no-animations") + config.useShakeGesture = true + config.showFormForScreenshots = true + config.configureWidget = configureFeedbackWidget(config:) + config.configureForm = configureFeedbackForm(config:) + config.configureTheme = configureFeedbackTheme(config:) + configureHooks(config: config) + } + + func configureHooks(config: SentryUserFeedbackConfiguration) { + config.onFormOpen = { + createHookFile(name: "onFormOpen") + } + config.onFormClose = { + createHookFile(name: "onFormClose") + } + config.onSubmitSuccess = { info in + let name = info["name"] ?? "$shakespearean_insult_name" + let alert = UIAlertController(title: "Thanks?", message: "We have enough jank of our own, we really didn't need yours too, \(name).", preferredStyle: .alert) + alert.addAction(.init(title: "Deal with it 🕶️", style: .default)) + UIApplication.shared.delegate?.window??.rootViewController?.present(alert, animated: true) + createHookFile(name: "onSubmitSuccess") + } + config.onSubmitError = { error in + let alert = UIAlertController(title: "D'oh", message: "You tried to report jank, and encountered more jank. The jank has you now: \(error).", preferredStyle: .alert) + alert.addAction(.init(title: "Derp", style: .default)) + UIApplication.shared.delegate?.window??.rootViewController?.present(alert, animated: true) + createHookFile(name: "onSubmitError") + } + } + + func createHookFile(name: String) { + guard let appSupportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first else { + print("[iOS-Swift] Couldn't retrieve path to application support directory.") + return + } + let fm = FileManager.default + let dir = "\(appSupportDirectory)/io.sentry/feedback" + do { + try fm.createDirectory(atPath: dir, withIntermediateDirectories: true) + } catch { + print("[iOS-Swift] Couldn't create directory structure for user feedback form hook marker files: \(error).") + return + } + let path = "\(dir)/\(name)" + if !fm.createFile(atPath: path, contents: nil) { + print("[iOS-Swift] Couldn't create user feedback form hook marker file at \(path).") + } else { + print("[iOS-Swift] Created user feedback form hook marker file at \(path).") + } + + func removeHookFile(name: String) { + let path = "\(dir)/\(name)" + do { + try fm.removeItem(atPath: path) + } catch { + print("[iOS-Swift] Couldn't remove user feedback form hook marker file \(path): \(error).") + } + } + switch name { + case "onFormOpen": removeHookFile(name: "onFormClose") + case "onFormClose": removeHookFile(name: "onFormOpen") + case "onSubmitSuccess": removeHookFile(name: "onSubmitError") + case "onSubmitError": removeHookFile(name: "onSubmitSuccess") + default: fatalError("Unexpected marker file name") + } + } +} + +// MARK: Convenience access to SDK configuration via launch arg / environment variable +extension SentrySDKWrapper { + static let defaultDSN = "https://6cc9bae94def43cab8444a99e0031c28@o447951.ingest.sentry.io/5428557" + + var args: [String] { + let args = ProcessInfo.processInfo.arguments + print("[iOS-Swift] [debug] launch arguments: \(args)") + return args + } + + var env: [String: String] { + let env = ProcessInfo.processInfo.environment + print("[iOS-Swift] [debug] environment: \(env)") + return env + } + + /// For testing purposes, we want to be able to change the DSN and store it to disk. In a real app, you shouldn't need this behavior. + var dsn: String? { + do { + if let dsn = env["--io.sentry.dsn"] { + try DSNStorage.shared.saveDSN(dsn: dsn) + } + return try DSNStorage.shared.getDSN() ?? SentrySDKWrapper.defaultDSN + } catch { + print("[iOS-Swift] Error encountered while reading stored DSN: \(error)") + } + return nil + } + + /// Whether or not profiling benchmarks are being run; this requires disabling certain other features for proper functionality. + var isBenchmarking: Bool { args.contains("--io.sentry.test.benchmarking") } + var isUITest: Bool { env["--io.sentry.sdk-environment"] == "ui-tests" } + + func checkDisabled(with arg: String) -> Bool { + args.contains("--io.sentry.disable-everything") || args.contains(arg) + } + + // MARK: features that care about simulator vs device, ui tests and profiling benchmarks + var enableSpotlight: Bool { +#if targetEnvironment(simulator) + !checkDisabled(with: "--disable-spotlight") +#else + false +#endif // targetEnvironment(simulator) + } + + /// - note: the benchmark test starts and stops a custom transaction using a UIButton, and automatic user interaction tracing stops the transaction that begins with that button press after the idle timeout elapses, stopping the profiler (only one profiler runs regardless of the number of concurrent transactions) + var enableUITracing: Bool { !isBenchmarking && !checkDisabled(with: "--disable-ui-tracing") } + var enablePrewarmedAppStartTracing: Bool { !isBenchmarking && !checkDisabled(with: "--disable-prewarmed-app-start-tracing") } + var enablePerformanceTracing: Bool { !isBenchmarking && !checkDisabled(with: "--disable-auto-performance-tracing") } + var enableTracing: Bool { !isBenchmarking && !checkDisabled(with: "--disable-tracing") } + /// - note: UI tests generate false OOMs + var enableWatchdogTracking: Bool { !isUITest && !isBenchmarking && !checkDisabled(with: "--disable-watchdog-tracking") } + /// - note: disable during benchmarks because we run CPU for 15 seconds at full throttle which can trigger ANRs + var enableANRTracking: Bool { !isBenchmarking && !checkDisabled(with: "--disable-anr-tracking") } + + // MARK: Other features + + var enableTimeToFullDisplayTracing: Bool { !checkDisabled(with: "--disable-time-to-full-display-tracing")} + var enableAttachScreenshot: Bool { !checkDisabled(with: "--disable-attach-screenshot")} + var enableAttachViewHierarchy: Bool { !checkDisabled(with: "--disable-attach-view-hierarchy")} + var enablePerformanceV2: Bool { !checkDisabled(with: "--disable-performance-v2")} + var enableSessionReplay: Bool { !checkDisabled(with: "--disable-session-replay") } + var enableMetricKit: Bool { !checkDisabled(with: "--disable-metrickit-integration") } + var enableSessionTracking: Bool { !checkDisabled(with: "--disable-automatic-session-tracking") } + var enableFileIOTracing: Bool { !checkDisabled(with: "--disable-file-io-tracing") } + var enableBreadcrumbs: Bool { !checkDisabled(with: "--disable-automatic-breadcrumbs") } + var enableUIVCTracing: Bool { !checkDisabled(with: "--disable-uiviewcontroller-tracing") } + var enableNetworkTracing: Bool { !checkDisabled(with: "--disable-network-tracking") } + var enableCoreDataTracing: Bool { !checkDisabled(with: "--disable-core-data-tracing") } + var enableNetworkBreadcrumbs: Bool { !checkDisabled(with: "--disable-network-breadcrumbs") } + var enableSwizzling: Bool { !checkDisabled(with: "--disable-swizzling") } + var enableCrashHandling: Bool { !checkDisabled(with: "--disable-crash-handler") } + + var tracesSampleRate: NSNumber { + guard let tracesSampleRateOverride = env["--io.sentry.tracesSampleRate"] else { + return 1 + } + return NSNumber(value: (tracesSampleRateOverride as NSString).integerValue) + } + + var tracesSampler: ((SamplingContext) -> NSNumber?)? { + guard let tracesSamplerValue = env["--io.sentry.tracesSamplerValue"] else { + return nil + } + + return { _ in + return NSNumber(value: (tracesSamplerValue as NSString).integerValue) + } + } + + var profilesSampleRate: NSNumber? { + if args.contains("--io.sentry.enableContinuousProfiling") { + return nil + } else if let profilesSampleRateOverride = env["--io.sentry.profilesSampleRate"] { + return NSNumber(value: (profilesSampleRateOverride as NSString).integerValue) + } else { + return 1 + } + } + + var profilesSampler: ((SamplingContext) -> NSNumber?)? { + guard let profilesSamplerValue = env["--io.sentry.profilesSamplerValue"] else { + return nil + } + + return { _ in + return NSNumber(value: (profilesSamplerValue as NSString).integerValue) + } + } + + var enableAppLaunchProfiling: Bool { args.contains("--profile-app-launches") } +} From 812d5dff172c4f04fb4ddaa7bcaec5572c923cb7 Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Mon, 6 Jan 2025 15:31:48 -0900 Subject: [PATCH 4/5] fix some imports and file name --- .../iOS-Swift/iOS-Swift.xcodeproj/project.pbxproj | 12 ++++++------ Samples/iOS-Swift/iOS-Swift/AppDelegate.swift | 1 - .../{SentrySDK.swift => SentrySDKWrapper.swift} | 3 ++- 3 files changed, 8 insertions(+), 8 deletions(-) rename Samples/iOS-Swift/iOS-Swift/{SentrySDK.swift => SentrySDKWrapper.swift} (99%) diff --git a/Samples/iOS-Swift/iOS-Swift.xcodeproj/project.pbxproj b/Samples/iOS-Swift/iOS-Swift.xcodeproj/project.pbxproj index 27c68de6882..e21817e2367 100644 --- a/Samples/iOS-Swift/iOS-Swift.xcodeproj/project.pbxproj +++ b/Samples/iOS-Swift/iOS-Swift.xcodeproj/project.pbxproj @@ -44,8 +44,8 @@ 84BE546F287503F100ACC735 /* SentrySDKPerformanceBenchmarkTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 84BE546E287503F100ACC735 /* SentrySDKPerformanceBenchmarkTests.m */; }; 84BE547E287645B900ACC735 /* SentryProcessInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 84BE54792876451D00ACC735 /* SentryProcessInfo.m */; }; 84DBC6252CE6D321000C4904 /* UserFeedbackUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DBC61F2CE6D31C000C4904 /* UserFeedbackUITests.swift */; }; - 84EEE6622D28B35700010A9D /* SentrySDK.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84EEE6612D28B35700010A9D /* SentrySDK.swift */; }; - 84EEE6632D28B35700010A9D /* SentrySDK.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84EEE6612D28B35700010A9D /* SentrySDK.swift */; }; + 84EEE6632D28B35700010A9D /* SentrySDKWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84EEE6612D28B35700010A9D /* SentrySDKWrapper.swift */; }; + 84EEE6642D2CABF500010A9D /* SentrySDKWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84EEE6612D28B35700010A9D /* SentrySDKWrapper.swift */; }; 84FB812A284001B800F3A94A /* SentryBenchmarking.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84FB8129284001B800F3A94A /* SentryBenchmarking.mm */; }; 84FB812B284001B800F3A94A /* SentryBenchmarking.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84FB8129284001B800F3A94A /* SentryBenchmarking.mm */; }; 8E8C57AF25EF16E6001CEEFA /* TraceTestViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E8C57AE25EF16E6001CEEFA /* TraceTestViewController.swift */; }; @@ -292,7 +292,7 @@ 84BE54782876451D00ACC735 /* SentryProcessInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentryProcessInfo.h; sourceTree = ""; }; 84BE54792876451D00ACC735 /* SentryProcessInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryProcessInfo.m; sourceTree = ""; }; 84DBC61F2CE6D31C000C4904 /* UserFeedbackUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserFeedbackUITests.swift; sourceTree = ""; }; - 84EEE6612D28B35700010A9D /* SentrySDK.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentrySDK.swift; sourceTree = ""; }; + 84EEE6612D28B35700010A9D /* SentrySDKWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentrySDKWrapper.swift; sourceTree = ""; }; 84FB8125284001B800F3A94A /* SentryBenchmarking.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentryBenchmarking.h; sourceTree = ""; }; 84FB8129284001B800F3A94A /* SentryBenchmarking.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SentryBenchmarking.mm; sourceTree = ""; }; 84FB812C2840021B00F3A94A /* iOS-Swift-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "iOS-Swift-Bridging-Header.h"; sourceTree = ""; }; @@ -486,7 +486,7 @@ D8DBDA73274D4DF900007380 /* ViewControllers */, 63F93AA9245AC91600A500DB /* iOS-Swift.entitlements */, 637AFDA9243B02760034958B /* AppDelegate.swift */, - 84EEE6612D28B35700010A9D /* SentrySDK.swift */, + 84EEE6612D28B35700010A9D /* SentrySDKWrapper.swift */, 637AFDAD243B02760034958B /* TransactionsViewController.swift */, 84AB90782A50031B0054C99A /* Profiling */, D80D021229EE93630084393D /* ErrorsViewController.swift */, @@ -1137,7 +1137,7 @@ 84BA71F12C8BC55A0045B828 /* Toasts.swift in Sources */, 629EC8AD2B0B537400858855 /* TriggerAppHang.swift in Sources */, D8AE48C92C57DC2F0092A2A6 /* WebViewController.swift in Sources */, - 84EEE6632D28B35700010A9D /* SentrySDK.swift in Sources */, + 84EEE6632D28B35700010A9D /* SentrySDKWrapper.swift in Sources */, D8DBDA78274D5FC400007380 /* SplitViewController.swift in Sources */, 84ACC43C2A73CB5900932A18 /* ProfilingNetworkScanner.swift in Sources */, D80D021A29EE936F0084393D /* ExtraViewController.swift in Sources */, @@ -1187,7 +1187,7 @@ 924857562C89A86300774AC3 /* MainViewController.swift in Sources */, D8F3D058274E57D600B56F8C /* TableViewController.swift in Sources */, 7B5525B62938B644006A2932 /* DiskWriteException.swift in Sources */, - 84EEE6622D28B35700010A9D /* SentrySDK.swift in Sources */, + 84EEE6642D2CABF500010A9D /* SentrySDKWrapper.swift in Sources */, D8269A58274C0FC700BD5BD5 /* TransactionsViewController.swift in Sources */, 844DA821282584C300E6B62E /* CoreDataViewController.swift in Sources */, D8444E55275F79570042F4DE /* SpanExtension.swift in Sources */, diff --git a/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift b/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift index 3dbb7bbb1b5..a42ad680bab 100644 --- a/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift +++ b/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift @@ -1,4 +1,3 @@ -import Sentry import UIKit @UIApplicationMain diff --git a/Samples/iOS-Swift/iOS-Swift/SentrySDK.swift b/Samples/iOS-Swift/iOS-Swift/SentrySDKWrapper.swift similarity index 99% rename from Samples/iOS-Swift/iOS-Swift/SentrySDK.swift rename to Samples/iOS-Swift/iOS-Swift/SentrySDKWrapper.swift index 8aaff908826..e11b57fb984 100644 --- a/Samples/iOS-Swift/iOS-Swift/SentrySDK.swift +++ b/Samples/iOS-Swift/iOS-Swift/SentrySDKWrapper.swift @@ -1,4 +1,5 @@ -import Foundation +import Sentry +import UIKit struct SentrySDKWrapper { static let shared = SentrySDKWrapper() From 0ea61fb59ac49a11ff69adece1956ba4267c141f Mon Sep 17 00:00:00 2001 From: Andrew McKnight Date: Tue, 7 Jan 2025 14:14:36 -0900 Subject: [PATCH 5/5] fix iOS13-Swift build --- Samples/iOS-Swift/iOS13-Swift/iOS13-Swift-Bridging-Header.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Samples/iOS-Swift/iOS13-Swift/iOS13-Swift-Bridging-Header.h b/Samples/iOS-Swift/iOS13-Swift/iOS13-Swift-Bridging-Header.h index dfdc4e5eeef..e40c9af3526 100644 --- a/Samples/iOS-Swift/iOS13-Swift/iOS13-Swift-Bridging-Header.h +++ b/Samples/iOS-Swift/iOS13-Swift/iOS13-Swift-Bridging-Header.h @@ -1,4 +1,5 @@ #import "SentryBenchmarking.h" #import "SentryUIApplication.h" #import +#import #import