diff --git a/.build-sample-apps.tmuxinator.yml b/.build-sample-apps.tmuxinator.yml new file mode 100644 index 00000000000..ceaa03cc4be --- /dev/null +++ b/.build-sample-apps.tmuxinator.yml @@ -0,0 +1,25 @@ + name: build-sample-apps + + windows: + - build: + layout: tiled + panes: + - bash -c 'cd ./Samples/iOS13-Swift && xcodebuild build -scheme iOS13-Swift | xcbeautify; echo $? > /tmp/iOS13-Swift.exit' + - bash -c 'cd ./Samples/iOS-SwiftUI && xcodebuild build -scheme iOS-SwiftUI | xcbeautify; echo $? > /tmp/iOS-SwiftUI.exit' + - bash -c 'cd ./Samples/iOS-Swift6 && xcodebuild build -scheme iOS-Swift6 | xcbeautify; echo $? > /tmp/iOS-Swift6.exit' + - bash -c 'cd ./Samples/iOS-Swift && xcodebuild build -scheme iOS-Swift | xcbeautify; echo $? > /tmp/iOS-Swift.exit' + - bash -c 'cd ./Samples/watchOS-Swift && xcodebuild build -scheme "watchOS-Swift WatchKit App" | xcbeautify; echo $? > /tmp/watchOS-Swift.exit' + - bash -c 'cd ./Samples/visionOS-Swift && xcodebuild build -scheme visionOS-Swift | xcbeautify; echo $? > /tmp/visionOS-Swift.exit' + - bash -c 'cd ./Samples/tvOS-Swift && xcodebuild build -scheme tvOS-Swift | xcbeautify; echo $? > /tmp/tvOS-Swift.exit' + - bash -c 'cd ./Samples/macOS-SwiftUI && xcodebuild build -scheme macOS-SwiftUI | xcbeautify; echo $? > /tmp/macOS-SwiftUI.exit' + - bash -c 'cd ./Samples/macOS-Swift && xcodebuild build -scheme macOS-Swift | xcbeautify; echo $? > /tmp/macOS-Swift.exit' + - bash -c 'cd ./Samples/iOS15-SwiftUI && xcodebuild build -scheme iOS15-SwiftUI | xcbeautify; echo $? > /tmp/iOS15-SwiftUI.exit' + - bash -c 'cd ./Samples/iOS-ObjectiveC && xcodebuild build -scheme iOS-ObjectiveC | xcbeautify; echo $? > /tmp/iOS-ObjectiveC.exit' + - | + while [ ! -f /tmp/iOS13-Swift.exit ] || [ ! -f /tmp/iOS-SwiftUI.exit ] || [ ! -f /tmp/iOS-Swift6.exit ] || [ ! -f /tmp/iOS-Swift.exit ] || [ ! -f /tmp/watchOS-Swift.exit ] || [ ! -f /tmp/visionOS-Swift.exit ] || [ ! -f /tmp/tvOS-Swift.exit ] || [ ! -f /tmp/macOS-SwiftUI.exit ] || [ ! -f /tmp/macOS-Swift.exit ] || [ ! -f /tmp/iOS15-SwiftUI.exit ] || [ ! -f /tmp/iOS-ObjectiveC.exit ]; do + sleep 1 + done + for file in /tmp/*.exit; do + echo "$file: $(cat $file)" + done + rm /tmp/*.exit diff --git a/.gitignore b/.gitignore index 7be9f91383a..b18312c5127 100644 --- a/.gitignore +++ b/.gitignore @@ -237,3 +237,4 @@ Samples/macOS-SwiftUI/macOS-SwiftUI.xcodeproj Samples/tvOS-Swift/tvOS-Swift.xcodeproj Samples/visionOS-Swift/visionOS-Swift.xcodeproj Samples/watchOS-Swift/watchOS-Swift.xcodeproj +Samples/SentrySampleShared/SentrySampleShared.xcodeproj diff --git a/Brewfile b/Brewfile index 1a0e3ad2b98..0868b4a91c8 100644 --- a/Brewfile +++ b/Brewfile @@ -4,4 +4,6 @@ brew 'pre-commit' brew 'python3' brew 'xcbeautify' brew 'rbenv' +brew 'tmux' +brew 'tmuxinator' brew 'xcodegen' diff --git a/Makefile b/Makefile index 9f0b23579ff..454554564fa 100644 --- a/Makefile +++ b/Makefile @@ -145,6 +145,7 @@ release-pod: pod trunk push SentrySwiftUI.podspec xcode: + xcodegen --spec Samples/SentrySampleShared/SentrySampleShared.yml xcodegen --spec Samples/SessionReplay-CameraTest/SessionReplay-CameraTest.yml xcodegen --spec Samples/iOS-ObjectiveC/iOS-ObjectiveC.yml xcodegen --spec Samples/iOS-Swift/iOS-Swift.yml @@ -158,3 +159,8 @@ xcode: xcodegen --spec Samples/visionOS-Swift/visionOS-Swift.yml xcodegen --spec Samples/watchOS-Swift/watchOS-Swift.yml open Sentry.xcworkspace + +.PHONY: build-sample-apps + +build-sample-apps: + tmuxinator start -p .build-sample-apps.tmuxinator.yml diff --git a/Samples/SentrySampleShared/SentrySampleShared.xcconfig b/Samples/SentrySampleShared/SentrySampleShared.xcconfig new file mode 100644 index 00000000000..fdc3071a28a --- /dev/null +++ b/Samples/SentrySampleShared/SentrySampleShared.xcconfig @@ -0,0 +1,32 @@ +#include "../Shared/Config/Architectures.xcconfig" +#include "../Shared/Config/BuildOptions.xcconfig" +#include "../Shared/Config/Deployment.xcconfig" +#include "../Shared/Config/Linking.xcconfig" +#include "../Shared/Config/Localization.xcconfig" +#include "../Shared/Config/Packaging.xcconfig" +#include "../Shared/Config/SearchPaths.xcconfig" +#include "../Shared/Config/Signing.xcconfig" +#include "../Shared/Config/Versioning.xcconfig" +#include "../Shared/Config/CodeGeneration.xcconfig" +#include "../Shared/Config/ClangLanguage.xcconfig" +#include "../Shared/Config/ClangCppLanguage.xcconfig" +#include "../Shared/Config/ClangModules.xcconfig" +#include "../Shared/Config/ClangObjCLanguage.xcconfig" +#include "../Shared/Config/ClangPreprocessing.xcconfig" +#include "../Shared/Config/ClangWarnings.xcconfig" +#include "../Shared/Config/ClangWarningsCpp.xcconfig" +#include "../Shared/Config/ClangWarningsObjC.xcconfig" +#include "../Shared/Config/AssetCatalog.xcconfig" +#include "../Shared/Config/ClangAnalyzer.xcconfig" +#include "../Shared/Config/Swift.xcconfig" +#include "../Shared/Config/Metal.xcconfig" + +PRODUCT_BUNDLE_IDENTIFIER = io.sentry.SentrySampleShared +INFOPLIST_FILE = SentrySampleShared/Info.plist +SUPPORTED_PLATFORMS = xrsimulator xros watchsimulator watchos macosx iphonesimulator iphoneos driverkit appletvsimulator appletvos + +CODE_SIGN_STYLE = Manual +CODE_SIGN_IDENTITY = +CODE_SIGN_IDENTITY[sdk=macosx*] = +PROVISIONING_PROFILE_SPECIFIER = +DEVELOPMENT_TEAM = diff --git a/Samples/SentrySampleShared/SentrySampleShared.yml b/Samples/SentrySampleShared/SentrySampleShared.yml new file mode 100644 index 00000000000..7de3cd50420 --- /dev/null +++ b/Samples/SentrySampleShared/SentrySampleShared.yml @@ -0,0 +1,29 @@ +name: SentrySampleShared +createIntermediateGroups: true +generateEmptyDirectories: true +configs: + Debug: debug + Test: debug + TestCI: debug + Release: release +projectReferences: + Sentry: + path: ../../Sentry.xcodeproj +fileGroups: + - ../Shared/Config + - SentrySampleShared.yml +options: + bundleIdPrefix: io.sentry +targets: + SentrySampleShared: + type: framework + platform: auto + sources: + - SentrySampleShared + dependencies: + - target: Sentry/Sentry + configFiles: + Debug: SentrySampleShared.xcconfig + Release: SentrySampleShared.xcconfig + Test: SentrySampleShared.xcconfig + TestCI: SentrySampleShared.xcconfig diff --git a/Samples/SentrySampleShared/SentrySampleShared/AssertView.swift b/Samples/SentrySampleShared/SentrySampleShared/AssertView.swift new file mode 100644 index 00000000000..baccd3bd977 --- /dev/null +++ b/Samples/SentrySampleShared/SentrySampleShared/AssertView.swift @@ -0,0 +1,94 @@ +#if !os(macOS) && !os(watchOS) + +import Foundation +import Sentry +import UIKit + +public class AssertView: UIView { + var span: Span? + public var autoHide = true + + private var assertLabel: UILabel! + private var errorLabel: UILabel! + + var message: String? { + get { + return assertLabel.text + } + set { + assertLabel.text = newValue + setNeedsLayout() + } + } + + var errorMessage: String? { + get { + return errorLabel.text + } + set { + errorLabel.text = newValue + setNeedsLayout() + } + } + + convenience init() { + self.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) + } + + override init(frame: CGRect) { + super.init(frame: frame) + initialize() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + initialize() + } + + private func initialize() { + backgroundColor = UIColor(white: 0.3, alpha: 1) + + assertLabel = UILabel().forAutoLayout() + assertLabel.textColor = UIColor(white: 1, alpha: 1) + assertLabel.accessibilityIdentifier = "ASSERT_MESSAGE" + addSubview(assertLabel) + + errorLabel = UILabel().forAutoLayout() + errorLabel.textColor = UIColor(white: 1, alpha: 1) + errorLabel.accessibilityIdentifier = "ASSERT_ERROR" + errorLabel.numberOfLines = 0 + addSubview(errorLabel) + + let guide = self.safeAreaLayoutGuide + + let constraints = [ + assertLabel.topAnchor.constraint(equalTo: guide.topAnchor, constant: 16), + assertLabel.leftAnchor.constraint(equalTo: guide.leftAnchor, constant: 16), + assertLabel.rightAnchor.constraint(equalTo: guide.rightAnchor, constant: -16), + + errorLabel.topAnchor.constraint(equalTo: assertLabel.bottomAnchor, constant: 16), + errorLabel.leftAnchor.constraint(equalTo: guide.leftAnchor, constant: 16), + errorLabel.rightAnchor.constraint(equalTo: guide.rightAnchor, constant: -16), + errorLabel.bottomAnchor.constraint(equalTo: guide.bottomAnchor, constant: 0) + ] + NSLayoutConstraint.activate(constraints) + } + + public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + //If a tap occurs outside the view, it disappears + let result = super.hitTest(point, with: event) + if result == nil { + close() + } + return result + } + + private func close() { + UIAssert.shared.reset() + if autoHide { + removeFromSuperview() + } + } +} + +#endif // !os(macOS) && !os(watchOS) diff --git a/Samples/Shared/DSNStorage.swift b/Samples/SentrySampleShared/SentrySampleShared/DSNStorage.swift similarity index 80% rename from Samples/Shared/DSNStorage.swift rename to Samples/SentrySampleShared/SentrySampleShared/DSNStorage.swift index 0680b4831fe..7f2a48818af 100644 --- a/Samples/Shared/DSNStorage.swift +++ b/Samples/SentrySampleShared/SentrySampleShared/DSNStorage.swift @@ -4,35 +4,33 @@ import Sentry /** * Stores the DSN to a file in the cache directory. */ -class DSNStorage { - - static let shared = DSNStorage() - +public class DSNStorage { + public static let shared = DSNStorage() private let dsnFile: URL - + private init() { // swiftlint:disable force_unwrapping let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! // swiftlint:enable force_unwrapping dsnFile = cachesDirectory.appendingPathComponent("dsn") } - - func saveDSN(dsn: String) throws { + + public func saveDSN(dsn: String) throws { try deleteDSN() try dsn.write(to: dsnFile, atomically: true, encoding: .utf8) } - - func getDSN() throws -> String? { + + public func getDSN() throws -> String? { let fileManager = FileManager.default - + guard fileManager.fileExists(atPath: dsnFile.path) else { return nil } - + return try String(contentsOfFile: dsnFile.path) } - - func deleteDSN() throws { + + public func deleteDSN() throws { let fileManager = FileManager.default if fileManager.fileExists(atPath: dsnFile.path) { try fileManager.removeItem(at: dsnFile) diff --git a/Samples/iOS-Swift/iOS-Swift/EnvironmentVariableTableViewCell.swift b/Samples/SentrySampleShared/SentrySampleShared/EnvironmentVariableTableViewCell.swift similarity index 91% rename from Samples/iOS-Swift/iOS-Swift/EnvironmentVariableTableViewCell.swift rename to Samples/SentrySampleShared/SentrySampleShared/EnvironmentVariableTableViewCell.swift index f33f689d36c..b8528236dd7 100644 --- a/Samples/iOS-Swift/iOS-Swift/EnvironmentVariableTableViewCell.swift +++ b/Samples/SentrySampleShared/SentrySampleShared/EnvironmentVariableTableViewCell.swift @@ -1,3 +1,4 @@ +#if !os(macOS) && !os(tvOS) && !os(watchOS) import UIKit class EnvironmentVariableTableViewCell: UITableViewCell, UITextFieldDelegate { @@ -49,5 +50,7 @@ class EnvironmentVariableTableViewCell: UITableViewCell, UITextFieldDelegate { } else { override?.stringValue = textField.text } + SentrySDKWrapper.shared.startSentry() } } +#endif // !os(macOS) && !os(tvOS) && !os(watchOS) diff --git a/Samples/iOS-Swift/iOS-Swift/FeaturesViewController.swift b/Samples/SentrySampleShared/SentrySampleShared/FeaturesViewController.swift similarity index 89% rename from Samples/iOS-Swift/iOS-Swift/FeaturesViewController.swift rename to Samples/SentrySampleShared/SentrySampleShared/FeaturesViewController.swift index 5ff6a92da9f..d35ac05fd7e 100644 --- a/Samples/iOS-Swift/iOS-Swift/FeaturesViewController.swift +++ b/Samples/SentrySampleShared/SentrySampleShared/FeaturesViewController.swift @@ -1,7 +1,8 @@ +#if !os(macOS) && !os(tvOS) && !os(watchOS) import UIKit -class FeaturesViewController: UITableViewController { - override func viewDidLoad() { +public class FeaturesViewController: UITableViewController { + public override func viewDidLoad() { super.viewDidLoad() tableView.register(LaunchArgumentTableViewCell.self, forCellReuseIdentifier: "launchArgumentCell") tableView.register(EnvironmentVariableTableViewCell.self, forCellReuseIdentifier: "environmentVariableCell") @@ -34,11 +35,11 @@ class FeaturesViewController: UITableViewController { tableView.reloadData() } - override func numberOfSections(in tableView: UITableView) -> Int { + public override func numberOfSections(in tableView: UITableView) -> Int { 6 } - override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + public override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { if section == 0 { return "Special" } else if section == 1 { @@ -55,7 +56,7 @@ class FeaturesViewController: UITableViewController { return nil } - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + public override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if section == 0 { return SentrySDKOverrides.Special.allCases.count } else if section == 1 { @@ -72,7 +73,7 @@ class FeaturesViewController: UITableViewController { return 0 } - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + public override 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 @@ -116,3 +117,4 @@ class FeaturesViewController: UITableViewController { return cell } } +#endif // !os(macOS) && !os(tvOS) && !os(watchOS) diff --git a/Samples/SentrySampleShared/SentrySampleShared/GitInjections.swift b/Samples/SentrySampleShared/SentrySampleShared/GitInjections.swift new file mode 100644 index 00000000000..1bfdea9d647 --- /dev/null +++ b/Samples/SentrySampleShared/SentrySampleShared/GitInjections.swift @@ -0,0 +1,29 @@ +import Foundation +import Sentry + +extension Bundle { + var gitCommitHash: String? { + infoDictionary?["GIT_COMMIT_HASH"] as? String + } + var gitBranchName: String? { + infoDictionary?["GIT_BRANCH"] as? String + } + var gitStatusClean: Bool { + (infoDictionary?["GIT_STATUS_CLEAN"] as? String) == "1" + } +} + +public func injectGitInformation(scope: Scope) { + if let commitHash = Bundle.main.gitCommitHash { + scope.setTag(value: "\(commitHash)\(Bundle.main.gitStatusClean ? "" : "-dirty")", key: "git-commit-hash") + } + if let branchName = Bundle.main.gitBranchName { + scope.setTag(value: branchName, key: "git-branch-name") + } +} + +public class GitInjector: NSObject { + @objc public static func objc_injectGitInformation(into scope: Scope) { + injectGitInformation(scope: scope) + } +} diff --git a/Samples/SentrySampleShared/SentrySampleShared/Info.plist b/Samples/SentrySampleShared/SentrySampleShared/Info.plist new file mode 100644 index 00000000000..8797543fb42 --- /dev/null +++ b/Samples/SentrySampleShared/SentrySampleShared/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSHumanReadableCopyright + + + diff --git a/Samples/iOS-Swift/iOS-Swift/LaunchArgumentTableViewCell.swift b/Samples/SentrySampleShared/SentrySampleShared/LaunchArgumentTableViewCell.swift similarity index 87% rename from Samples/iOS-Swift/iOS-Swift/LaunchArgumentTableViewCell.swift rename to Samples/SentrySampleShared/SentrySampleShared/LaunchArgumentTableViewCell.swift index f29a3d07b17..b70d480c607 100644 --- a/Samples/iOS-Swift/iOS-Swift/LaunchArgumentTableViewCell.swift +++ b/Samples/SentrySampleShared/SentrySampleShared/LaunchArgumentTableViewCell.swift @@ -1,3 +1,5 @@ +#if !os(macOS) && !os(tvOS) && !os(watchOS) +import Sentry import UIKit class LaunchArgumentTableViewCell: UITableViewCell { @@ -11,6 +13,7 @@ class LaunchArgumentTableViewCell: UITableViewCell { @objc func toggleFlag() { override?.boolValue = flagSwitch.isOn + SentrySDKWrapper.shared.startSentry() } override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { @@ -31,3 +34,4 @@ class LaunchArgumentTableViewCell: UITableViewCell { self.override = override } } +#endif // !os(macOS) && !os(tvOS) && !os(watchOS) diff --git a/Samples/Shared/LoremIpsum.txt b/Samples/SentrySampleShared/SentrySampleShared/LoremIpsum.txt similarity index 100% rename from Samples/Shared/LoremIpsum.txt rename to Samples/SentrySampleShared/SentrySampleShared/LoremIpsum.txt diff --git a/Samples/SentrySampleShared/SentrySampleShared/LoremIpsumText.swift b/Samples/SentrySampleShared/SentrySampleShared/LoremIpsumText.swift new file mode 100644 index 00000000000..e7e07f71548 --- /dev/null +++ b/Samples/SentrySampleShared/SentrySampleShared/LoremIpsumText.swift @@ -0,0 +1,11 @@ +import Foundation + +public class BundleResourceProvider: NSObject { + public static var loremIpsumTextFilePath: String? { + Bundle(for: self).path(forResource: "LoremIpsum", ofType: "txt") + } + + @objc public static var screenshotURL: URL? { + Bundle(for: self).url(forResource: "screenshot", withExtension: "png") + } +} diff --git a/Samples/Shared/RandomErrors.swift b/Samples/SentrySampleShared/SentrySampleShared/RandomErrors.swift similarity index 84% rename from Samples/Shared/RandomErrors.swift rename to Samples/SentrySampleShared/SentrySampleShared/RandomErrors.swift index 7321b615a81..62e6f8b52ba 100644 --- a/Samples/Shared/RandomErrors.swift +++ b/Samples/SentrySampleShared/SentrySampleShared/RandomErrors.swift @@ -6,9 +6,8 @@ enum SampleError: Error { case awesomeCentaur } -class RandomErrorGenerator { - - static func generate() throws { +public class RandomErrorGenerator { + public static func generate() throws { let random = Int.random(in: 0...2) switch random { case 0: diff --git a/Samples/iOS-Swift/iOS-Swift/SentrySDKOverrides.swift b/Samples/SentrySampleShared/SentrySampleShared/SentrySDKOverrides.swift similarity index 78% rename from Samples/iOS-Swift/iOS-Swift/SentrySDKOverrides.swift rename to Samples/SentrySampleShared/SentrySampleShared/SentrySDKOverrides.swift index 74cec3f3a5d..ef634d1a360 100644 --- a/Samples/iOS-Swift/iOS-Swift/SentrySDKOverrides.swift +++ b/Samples/SentrySampleShared/SentrySampleShared/SentrySDKOverrides.swift @@ -1,6 +1,6 @@ import Foundation -protocol SentrySDKOverride: RawRepresentable, CaseIterable { +public protocol SentrySDKOverride: RawRepresentable, CaseIterable { var boolValue: Bool { get set } var floatValue: Float? { get set } var stringValue: String? { get set } @@ -30,9 +30,10 @@ public enum SentrySDKOverrides { } } - enum Special: String, SentrySDKOverride { + public enum Special: String, SentrySDKOverride { case wipeDataOnLaunch = "--io.sentry.wipe-data" case disableEverything = "--io.sentry.disable-everything" + case skipSDKInit = "--skip-sentry-init" public var boolValue: Bool { get { @@ -44,7 +45,7 @@ public enum SentrySDKOverrides { } } - enum Feedback: String, SentrySDKOverride { + public enum Feedback: String, SentrySDKOverride { case allDefaults = "--io.sentry.feedback.all-defaults" case disableAutoInject = "--io.sentry.feedback.no-auto-inject-widget" case noWidgetText = "--io.sentry.feedback.no-widget-text" @@ -69,7 +70,7 @@ public enum SentrySDKOverrides { } } - enum Performance: String, SentrySDKOverride { + public enum Performance: String, SentrySDKOverride { case disableTimeToFullDisplayTracing = "--disable-time-to-full-display-tracing" case disablePerformanceV2 = "--disable-performance-v2" case disableAppHangTrackingV2 = "--disable-app-hang-tracking-v2" @@ -94,10 +95,63 @@ public enum SentrySDKOverrides { } } - enum Other: String, SentrySDKOverride { + 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 sessionReplaySampleRate = "--io.sentry.sessionReplaySampleRate" + case sessionReplayOnErrorSampleRate = "--io.sentry.sessionReplayOnErrorSampleRate" + case sessionReplayQuality = "--io.sentry.sessionReplayQuality" + + public var booleanValue: Bool { + get { + switch self { + case .sessionReplaySampleRate, .sessionReplayOnErrorSampleRate, .sessionReplayQuality: fatalError("This override doesn't correspond to a boolean value.") + default: return getBoolOverride(for: rawValue) + } + } + set(newValue) { + switch self { + case .sessionReplaySampleRate, .sessionReplayOnErrorSampleRate, .sessionReplayQuality: fatalError("This override doesn't correspond to a boolean value.") + default: setBoolOverride(for: rawValue, value: newValue) + } + } + } + + public var floatValue: Float? { + get { + switch self { + case .sessionReplaySampleRate, .sessionReplayOnErrorSampleRate: return getFloatValueOverride(for: rawValue) + default: fatalError("This override doesn't correspond to a float value.") + } + } + set(newValue) { + switch self { + case .sessionReplaySampleRate, .sessionReplayOnErrorSampleRate: setFloatOverride(for: rawValue, value: newValue) + default: fatalError("This override doesn't correspond to a float value.") + } + } + } + + public var stringValue: String? { + get { + switch self { + case .sessionReplayQuality: return getStringValueOverride(for: rawValue) + default: fatalError("This override doesn't correspond to a string value.") + } + } + set(newValue) { + switch self { + case .sessionReplayQuality: setStringOverride(for: rawValue, value: newValue) + default: fatalError("This override doesn't correspond to a string value.") + } + } + } + } + + public enum Other: String, SentrySDKOverride { case disableAttachScreenshot = "--disable-attach-screenshot" case disableAttachViewHierarchy = "--disable-attach-view-hierarchy" - case disableSessionReplay = "--disable-session-replay" case disableMetricKit = "--disable-metrickit-integration" case disableBreadcrumbs = "--disable-automatic-breadcrumbs" case disableNetworkBreadcrumbs = "--disable-network-breadcrumbs" @@ -120,7 +174,7 @@ public enum SentrySDKOverrides { } } - var stringValue: String? { + public var stringValue: String? { get { switch self { case .userName, .userEmail: return getStringValueOverride(for: rawValue) @@ -135,7 +189,7 @@ public enum SentrySDKOverrides { } } - public static var boolValues: [Other] { [.disableAttachScreenshot, .disableAttachViewHierarchy, .disableSessionReplay, .disableMetricKit, .disableBreadcrumbs, .disableNetworkBreadcrumbs, .disableSwizzling, .disableCrashHandling, .disableSpotlight] } + public static var boolValues: [Other] { [.disableAttachScreenshot, .disableAttachViewHierarchy, .disableMetricKit, .disableBreadcrumbs, .disableNetworkBreadcrumbs, .disableSwizzling, .disableCrashHandling, .disableSpotlight, .disableFileManagerSwizzling] } public static var stringVars: [Other] { [.userName, .userEmail] } } diff --git a/Samples/iOS-Swift/iOS-Swift/SentrySDKWrapper.swift b/Samples/SentrySampleShared/SentrySampleShared/SentrySDKWrapper.swift similarity index 87% rename from Samples/iOS-Swift/iOS-Swift/SentrySDKWrapper.swift rename to Samples/SentrySampleShared/SentrySampleShared/SentrySDKWrapper.swift index 4695f86e2d4..07d6722d5fa 100644 --- a/Samples/iOS-Swift/iOS-Swift/SentrySDKWrapper.swift +++ b/Samples/SentrySampleShared/SentrySampleShared/SentrySDKWrapper.swift @@ -1,12 +1,16 @@ // swiftlint:disable file_length function_body_length import Sentry + +#if !os(macOS) import UIKit +#endif // !os(macOS) -struct SentrySDKWrapper { - static let shared = SentrySDKWrapper() +public struct SentrySDKWrapper { + public static let shared = SentrySDKWrapper() - let feedbackButton = { +#if !os(macOS) && !os(tvOS) && !os(watchOS) + public let feedbackButton = { let button = UIButton(type: .custom) button.setTitle("BYOB Feedback", for: .normal) button.setTitleColor(.blue, for: .normal) @@ -14,11 +18,19 @@ struct SentrySDKWrapper { button.translatesAutoresizingMaskIntoConstraints = false return button }() +#endif // !os(macOS) && !os(tvOS) && !os(watchOS) + + public func startSentry() { + if SentrySDK.isEnabled { + print("SentrySDK already enabled, closing it") + SentrySDK.close() + } - func startSentry() { - SentrySDK.start(configureOptions: configureSentryOptions(options:)) + if !SentrySDKOverrides.Special.skipSDKInit.boolValue { + SentrySDK.start(configureOptions: configureSentryOptions(options:)) + } } - + func configureSentryOptions(options: Options) { options.dsn = dsn options.beforeSend = { $0 } @@ -26,24 +38,32 @@ struct SentrySDKWrapper { options.beforeCaptureScreenshot = { _ in true } options.beforeCaptureViewHierarchy = { _ in true } options.debug = true - - if #available(iOS 16.0, *), !SentrySDKOverrides.Other.disableSessionReplay.boolValue { + +#if !os(macOS) && !os(watchOS) && !os(visionOS) + if #available(iOS 16.0, *), !SentrySDKOverrides.SessionReplay.disableSessionReplay.boolValue { options.sessionReplay = SentryReplayOptions( - sessionSampleRate: 0, - onErrorSampleRate: 1, + sessionSampleRate: SentrySDKOverrides.SessionReplay.sessionReplaySampleRate.floatValue ?? 0, + onErrorSampleRate: SentrySDKOverrides.SessionReplay.sessionReplayOnErrorSampleRate.floatValue ?? 1, maskAllText: true, maskAllImages: true ) - 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.sessionReplayQuality.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 } +#endif // !os(tvOS) +#endif // !os(macOS) && !os(watchOS) && !os(visionOS) options.tracesSampleRate = 1 if let sampleRate = SentrySDKOverrides.Tracing.sampleRate.floatValue { @@ -55,18 +75,28 @@ struct SentrySDKWrapper { } } +#if !os(tvOS) && !os(watchOS) && !os(visionOS) configureProfiling(options) +#endif // !os(tvOS) && !os(watchOS) && !os(visionOS) options.enableAutoSessionTracking = !SentrySDKOverrides.Performance.disableSessionTracking.boolValue if let sessionTrackingIntervalMillis = env["--io.sentry.sessionTrackingIntervalMillis"] { options.sessionTrackingIntervalMillis = UInt((sessionTrackingIntervalMillis as NSString).integerValue) } +#if !os(macOS) && !os(watchOS) options.add(inAppInclude: "iOS_External") // 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) options.enableUserInteractionTracing = !isBenchmarking && !SentrySDKOverrides.Performance.disableUITracing.boolValue + options.enablePreWarmedAppStartTracing = !isBenchmarking && !SentrySDKOverrides.Performance.disablePrewarmedAppStartTracing.boolValue + options.enableUIViewControllerTracing = !SentrySDKOverrides.Performance.disableUIVCTracing.boolValue + options.attachScreenshot = !SentrySDKOverrides.Other.disableAttachScreenshot.boolValue + options.attachViewHierarchy = !SentrySDKOverrides.Other.disableAttachViewHierarchy.boolValue + options.enableAppHangTrackingV2 = !SentrySDKOverrides.Performance.disableAppHangTrackingV2.boolValue +#endif // !os(macOS) && !os(watchOS) + // disable during benchmarks because we run CPU for 15 seconds at full throttle which can trigger ANRs options.enableAppHangTracking = !isBenchmarking && !SentrySDKOverrides.Performance.disableANRTracking.boolValue @@ -74,23 +104,18 @@ struct SentrySDKWrapper { options.enableWatchdogTerminationTracking = !isUITest && !isBenchmarking && !SentrySDKOverrides.Performance.disableWatchdogTracking.boolValue options.enableAutoPerformanceTracing = !isBenchmarking && !SentrySDKOverrides.Performance.disablePerformanceTracing.boolValue - options.enablePreWarmedAppStartTracing = !isBenchmarking && !SentrySDKOverrides.Performance.disablePrewarmedAppStartTracing.boolValue options.enableTracing = !isBenchmarking && !SentrySDKOverrides.Tracing.disableTracing.boolValue options.enableFileIOTracing = !SentrySDKOverrides.Performance.disableFileIOTracing.boolValue options.enableAutoBreadcrumbTracking = !SentrySDKOverrides.Other.disableBreadcrumbs.boolValue - options.enableUIViewControllerTracing = !SentrySDKOverrides.Performance.disableUIVCTracing.boolValue options.enableNetworkTracking = !SentrySDKOverrides.Performance.disableNetworkTracing.boolValue options.enableCoreDataTracing = !SentrySDKOverrides.Performance.disableCoreDataTracing.boolValue options.enableNetworkBreadcrumbs = !SentrySDKOverrides.Other.disableNetworkBreadcrumbs.boolValue options.enableSwizzling = !SentrySDKOverrides.Other.disableSwizzling.boolValue options.enableCrashHandler = !SentrySDKOverrides.Other.disableCrashHandling.boolValue options.enablePersistingTracesWhenCrashing = true - options.attachScreenshot = !SentrySDKOverrides.Other.disableAttachScreenshot.boolValue - options.attachViewHierarchy = !SentrySDKOverrides.Other.disableAttachViewHierarchy.boolValue options.enableTimeToFullDisplayTracing = !SentrySDKOverrides.Performance.disableTimeToFullDisplayTracing.boolValue options.enablePerformanceV2 = !SentrySDKOverrides.Performance.disablePerformanceV2.boolValue - options.enableAppHangTrackingV2 = !SentrySDKOverrides.Performance.disableAppHangTrackingV2.boolValue options.failedRequestStatusCodes = [ HttpStatusCodeRange(min: 400, max: 599) ] #if targetEnvironment(simulator) @@ -105,14 +130,19 @@ struct SentrySDKWrapper { NotificationCenter.default.post(name: .init("io.sentry.newbreadcrumb"), object: breadcrumb) return breadcrumb } - + options.initialScope = configureInitialScope(scope:) - options.configureUserFeedback = configureFeedback(config:) + +#if !os(macOS) && !os(tvOS) && !os(watchOS) && !os(visionOS) + if #available(iOS 13.0, *) { + options.configureUserFeedback = configureFeedback(config:) + } +#endif // !os(macOS) && !os(tvOS) && !os(watchOS) && !os(visionOS) // Experimental features options.experimental.enableFileManagerSwizzling = !SentrySDKOverrides.Other.disableFileManagerSwizzling.boolValue } - + func configureInitialScope(scope: Scope) -> Scope { if let environmentOverride = self.env["--io.sentry.sdk-environment"] { scope.setEnvironment(environmentOverride) @@ -125,17 +155,17 @@ struct SentrySDKWrapper { scope.setEnvironment("device") #endif // targetEnvironment(simulator) } - + scope.setTag(value: "swift", key: "language") - - scope.injectGitInformation() - + + injectGitInformation(scope: scope) + let user = User(userId: "1") user.email = self.env["--io.sentry.user.email"] ?? "tony@example.com" user.username = username user.name = userFullName scope.setUser(user) - + if let path = Bundle.main.path(forResource: "Tongariro", ofType: "jpg") { scope.addAttachment(Attachment(path: path, filename: "Tongariro.jpg", contentType: "image/jpeg")) } @@ -143,7 +173,7 @@ struct SentrySDKWrapper { scope.addAttachment(Attachment(data: data, filename: "log.txt")) return scope } - + var userFullName: String { let name = self.env["--io.sentry.user.name"] ?? NSFullUserName() guard !name.isEmpty else { @@ -151,7 +181,7 @@ struct SentrySDKWrapper { } return name } - + var username: String { let username = self.env["--io.sentry.user.username"] ?? NSUserName() guard !username.isEmpty else { @@ -162,16 +192,18 @@ struct SentrySDKWrapper { } } +#if !os(macOS) && !os(tvOS) && !os(watchOS) && !os(visionOS) // MARK: User feedback configuration +@available(iOS 13.0, *) extension SentrySDKWrapper { var layoutOffset: UIOffset { UIOffset(horizontal: 25, vertical: 75) } - + func configureFeedbackWidget(config: SentryUserFeedbackWidgetConfiguration) { guard !SentrySDKOverrides.Feedback.disableAutoInject.boolValue else { config.autoInject = false return } - + if Locale.current.languageCode == "ar" { // arabic config.labelText = "﷽" } else if Locale.current.languageCode == "ur" { // urdu @@ -184,7 +216,7 @@ extension SentrySDKWrapper { config.labelText = "Report Jank" } config.layoutUIOffset = layoutOffset - + if SentrySDKOverrides.Feedback.noWidgetText.boolValue { config.labelText = nil } @@ -192,7 +224,7 @@ extension SentrySDKWrapper { config.showIcon = false } } - + func configureFeedbackForm(config: SentryUserFeedbackFormConfiguration) { config.useSentryUser = !SentrySDKOverrides.Feedback.noUserInjection.boolValue config.formTitle = "Jank Report" @@ -208,7 +240,7 @@ extension SentrySDKWrapper { config.emailLabel = "Thine email" config.nameLabel = "Thy name" } - + func configureFeedbackTheme(config: SentryUserFeedbackThemeConfiguration) { let fontFamily: String if Locale.current.languageCode == "ar" { // arabic; ar_EG @@ -231,7 +263,7 @@ extension SentrySDKWrapper { config.buttonBackground = .purple config.buttonForeground = .white } - + func configureFeedback(config: SentryUserFeedbackConfiguration) { guard !args.contains("--io.sentry.feedback.all-defaults") else { config.configureWidget = { widget in @@ -240,7 +272,7 @@ extension SentrySDKWrapper { configureHooks(config: config) return } - + config.animations = !SentrySDKOverrides.Feedback.noAnimations.boolValue config.useShakeGesture = true config.showFormForScreenshots = true @@ -253,7 +285,7 @@ extension SentrySDKWrapper { config.customButton = feedbackButton } } - + func configureHooks(config: SentryUserFeedbackConfiguration) { config.onFormOpen = { updateHookMarkers(forEvent: "onFormOpen") @@ -266,13 +298,13 @@ extension SentrySDKWrapper { 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) - + // if there's a screenshot's Data in this dictionary, JSONSerialization crashes _even though_ there's a `try?`, so we'll write the base64 encoding of it var infoToWriteToFile = info if let attachments = info["attachments"] as? [Any], let screenshot = attachments.first as? Data { infoToWriteToFile["attachments"] = [screenshot.base64EncodedString()] } - + let jsonData = (try? JSONSerialization.data(withJSONObject: infoToWriteToFile, options: .sortedKeys)) ?? Data() updateHookMarkers(forEvent: "onSubmitSuccess", with: jsonData.base64EncodedString()) } @@ -285,13 +317,13 @@ extension SentrySDKWrapper { updateHookMarkers(forEvent: "onSubmitError", with: "\(nserror.domain);\(nserror.code);\(nserror.localizedDescription);\(missingFieldsSorted)") } } - + func updateHookMarkers(forEvent name: String, with contents: String? = nil) { 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" let isDirectory = UnsafeMutablePointer.allocate(capacity: 1) @@ -313,9 +345,9 @@ extension SentrySDKWrapper { return } } - + createHookFile(path: "\(dir)/\(name)", contents: contents) - + switch name { case "onFormOpen": removeHookFile(path: "\(dir)/onFormClose") case "onFormClose": removeHookFile(path: "\(dir)/onFormOpen") @@ -324,7 +356,7 @@ extension SentrySDKWrapper { default: fatalError("Unexpected marker file name") } } - + func createHookFile(path: String, contents: String?) { if let contents = contents { do { @@ -338,7 +370,7 @@ extension SentrySDKWrapper { print("[iOS-Swift] Created user feedback form hook marker file at \(path).") } } - + func removeHookFile(path: String) { let fm = FileManager.default guard fm.fileExists(atPath: path) else { return } @@ -349,23 +381,22 @@ extension SentrySDKWrapper { } } } +#endif // !os(macOS) && !os(tvOS) && !os(watchOS) && !os(visionOS) // MARK: Convenience access to SDK configuration via launch arg / environment variable extension SentrySDKWrapper { - static let defaultDSN = "https://6cc9bae94def43cab8444a99e0031c28@o447951.ingest.sentry.io/5428557" - + public 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 { @@ -385,6 +416,7 @@ extension SentrySDKWrapper { } // MARK: Profiling configuration +#if !os(tvOS) && !os(watchOS) && !os(visionOS) extension SentrySDKWrapper { func configureProfiling(_ options: Options) { if let sampleRate = SentrySDKOverrides.Profiling.sampleRate.floatValue { @@ -406,5 +438,6 @@ extension SentrySDKWrapper { } } } +#endif // !os(tvOS) && !os(watchOS) && !os(visionOS) // swiftlint:enable file_length function_body_length diff --git a/Samples/Shared/SpanExtension.swift b/Samples/SentrySampleShared/SentrySampleShared/SpanExtension.swift similarity index 74% rename from Samples/Shared/SpanExtension.swift rename to Samples/SentrySampleShared/SentrySampleShared/SpanExtension.swift index 34a71b57106..456ba1164d2 100644 --- a/Samples/Shared/SpanExtension.swift +++ b/Samples/SentrySampleShared/SentrySampleShared/SpanExtension.swift @@ -1,25 +1,24 @@ import Foundation import Sentry -extension Span { - - //If span is a transaction it has a list of children +public extension Span { + /// If span is a transaction it has a list of children func children() -> [Span]? { let sel = NSSelectorFromString("children") if !self.responds(to: sel) { return nil } - + return self.perform(sel)?.takeUnretainedValue() as? [Span] } - - //If span is a transaction it has a rootSpan + + /// If span is a transaction it has a rootSpan func rootSpan() -> Span? { let sel = NSSelectorFromString("rootSpan") if !self.responds(to: sel) { return nil } - + return self.perform(sel)?.takeUnretainedValue() as? Span } } diff --git a/Samples/Shared/SpanObserver.swift b/Samples/SentrySampleShared/SentrySampleShared/SpanObserver.swift similarity index 84% rename from Samples/Shared/SpanObserver.swift rename to Samples/SentrySampleShared/SentrySampleShared/SpanObserver.swift index 89d1d8e775d..ec9531c79ae 100644 --- a/Samples/Shared/SpanObserver.swift +++ b/Samples/SentrySampleShared/SentrySampleShared/SpanObserver.swift @@ -1,56 +1,54 @@ import Foundation import Sentry -class SpanObserver: NSObject { - +public class SpanObserver: NSObject { let span: Span private var callbacks: [String: (Span) -> Void] = [:] - + init(span: Span) { self.span = span } - + convenience init?(callback: @escaping (Span) -> Void) { guard let span = SentrySDK.span else { return nil } self.init(span: span) self.performOnFinish(callback: callback) } - + deinit { for key in callbacks.keys { removeSpanObserver(forKeyPath: key) } } - + func performOnFinish(callback: @escaping (Span) -> Void) { addSpanObserver(forKeyPath: "timestamp", callback: callback) } - - func releaseOnFinish() { + + public func releaseOnFinish() { if callbacks["timestamp"] != nil { removeSpanObserver(forKeyPath: "timestamp") } } - + func addSpanObserver(forKeyPath keyPath: String, callback: @escaping (Span) -> Void) { callbacks[keyPath] = callback //The given span may be a SentryTracer that wont respond to KVO. We need to get the root Span let spanToObserve = span.rootSpan() ?? span (spanToObserve as? NSObject)?.addObserver(self, forKeyPath: keyPath, options: .new, context: nil) } - + func removeSpanObserver(forKeyPath keyPath: String) { let spanToObserve = span.rootSpan() ?? span //see `addSpanObserver` (spanToObserve as? NSObject)?.removeObserver(self, forKeyPath: keyPath) callbacks.removeValue(forKey: keyPath) } - - override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) { + + public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) { guard let key = keyPath else { return } - + guard let callback = callbacks[key] else { return } - + callback(span) } - } diff --git a/Samples/Shared/UIAssert.swift b/Samples/SentrySampleShared/SentrySampleShared/UIAssert.swift similarity index 85% rename from Samples/Shared/UIAssert.swift rename to Samples/SentrySampleShared/SentrySampleShared/UIAssert.swift index 2769e9909c3..06749879ae6 100644 --- a/Samples/Shared/UIAssert.swift +++ b/Samples/SentrySampleShared/SentrySampleShared/UIAssert.swift @@ -1,45 +1,43 @@ +#if !os(macOS) && !os(watchOS) + import Foundation import Sentry import UIKit -class UIAssert { - - static let shared = UIAssert() - +public class UIAssert { + public static let shared = UIAssert() private let view = AssertView() - private var isFailed = false - - var targetView: AssertView? - + public var targetView: AssertView? + private init() { view.translatesAutoresizingMaskIntoConstraints = false } - + func assert(success: Bool, errorMessage: String?) { if isFailed { return } - + let assetView = targetView ?? view - + assetView.message = success ? "ASSERT: SUCCESS" : "ASSERT: FAIL" assetView.errorMessage = success ? "" : errorMessage isFailed = !success - + if targetView != nil { return } - + guard let window = UIApplication.shared.delegate?.window else { return } guard let target = window else { return } - + DispatchQueue.main.async { if self.view.superview != target { self.view.removeFromSuperview() - + target.addSubview(self.view) - + let constraints = [ self.view.leftAnchor.constraint(equalTo: target.leftAnchor, constant: 0), self.view.rightAnchor.constraint(equalTo: target.rightAnchor, constant: 0), @@ -49,54 +47,56 @@ class UIAssert { } } } - + func reset() { isFailed = false } - + static func isTrue(_ value: Bool, _ errorMessage: String? = nil) { shared.assert(success: value, errorMessage: errorMessage) } - + static func isFalse(_ value: Bool, _ errorMessage: String? = nil) { shared.assert(success: !value, errorMessage: errorMessage) } - + static func isEqual(_ first: T, _ second: T, _ errorMessage: String? = nil) where T: Equatable { shared.assert(success: first == second, errorMessage: errorMessage) } - + static func notNil(_ value: Any?, _ errorMessage: String? = nil) { shared.assert(success: value != nil, errorMessage: errorMessage) } - + static func isNil(_ value: Any?, _ errorMessage: String? = nil) { shared.assert(success: value == nil, errorMessage: errorMessage) } - - static func fail(_ errorMessage: String? = nil) { + + public static func fail(_ errorMessage: String? = nil) { shared.assert(success: false, errorMessage: errorMessage) } - - static func checkForViewControllerLifeCycle(_ transaction: Span, viewController: String, stepsToCheck: [String]? = nil, checkExcess: Bool = false) { + + public static func checkForViewControllerLifeCycle(_ transaction: Span, viewController: String, stepsToCheck: [String]? = nil, checkExcess: Bool = false) { guard var children = transaction.children() else { shared.assert(success: false, errorMessage: "\(viewController) span has no children") return } - + let steps = stepsToCheck ?? ["viewDidLoad", "viewWillAppear", "viewDidAppear"] var missing = [String]() - + steps.forEach { spanDescription in let index = children.firstIndex { $0.spanDescription == spanDescription } - + if let spanIndex = index { children.remove(at: spanIndex) } else { missing.append(spanDescription) } } - + UIAssert.isEqual(missing.count, 0, "Following spans not found: \(missing.joined(separator: ", "))") } } + +#endif // !os(macOS) && !os(watchOS) diff --git a/Samples/Shared/UIViewControllerExtension.swift b/Samples/SentrySampleShared/SentrySampleShared/UIViewControllerExtension.swift similarity index 89% rename from Samples/Shared/UIViewControllerExtension.swift rename to Samples/SentrySampleShared/SentrySampleShared/UIViewControllerExtension.swift index 7b2948782ad..55aef78f3f7 100644 --- a/Samples/Shared/UIViewControllerExtension.swift +++ b/Samples/SentrySampleShared/SentrySampleShared/UIViewControllerExtension.swift @@ -1,8 +1,10 @@ +#if !os(macOS) && !os(watchOS) + import Foundation import Sentry import UIKit -extension UIViewController { +public extension UIViewController { func createTransactionObserver(forCallback: @escaping (Span) -> Void) -> SpanObserver? { let result = SpanObserver(callback: forCallback) if result == nil { @@ -23,3 +25,5 @@ extension UIViewController { } } } + +#endif // !os(macOS) && !os(watchOS) diff --git a/Samples/Shared/UIViewExtension.swift b/Samples/SentrySampleShared/SentrySampleShared/UIViewExtension.swift similarity index 90% rename from Samples/Shared/UIViewExtension.swift rename to Samples/SentrySampleShared/SentrySampleShared/UIViewExtension.swift index 9124dd3cca5..afbbf3376ae 100644 --- a/Samples/Shared/UIViewExtension.swift +++ b/Samples/SentrySampleShared/SentrySampleShared/UIViewExtension.swift @@ -1,14 +1,16 @@ +#if !os(macOS) && !os(watchOS) + import Foundation import UIKit -extension UIView { +public extension UIView { /// A shortcut to disable `translatesAutoresizingMaskIntoConstraints` /// - Returns: self func forAutoLayout() -> Self { translatesAutoresizingMaskIntoConstraints = false return self } - + func matchEdgeAnchors(from other: UIView, leadingPad: CGFloat = 0, trailingPad: CGFloat = 0, topPad: CGFloat = 0, bottomPad: CGFloat = 0) { self.translatesAutoresizingMaskIntoConstraints = false other.translatesAutoresizingMaskIntoConstraints = false @@ -20,3 +22,5 @@ extension UIView { ]) } } + +#endif // !os(macOS) && !os(watchOS) diff --git a/Samples/Shared/screenshot.png b/Samples/SentrySampleShared/SentrySampleShared/screenshot.png similarity index 100% rename from Samples/Shared/screenshot.png rename to Samples/SentrySampleShared/SentrySampleShared/screenshot.png diff --git a/Samples/SessionReplay-CameraTest/SessionReplay-CameraTest.xcconfig b/Samples/SessionReplay-CameraTest/SessionReplay-CameraTest.xcconfig index 4110925fb99..262d67468c6 100644 --- a/Samples/SessionReplay-CameraTest/SessionReplay-CameraTest.xcconfig +++ b/Samples/SessionReplay-CameraTest/SessionReplay-CameraTest.xcconfig @@ -23,5 +23,5 @@ PRODUCT_BUNDLE_IDENTIFIER = io.sentry.sample.SessionReplay-CameraTest INFOPLIST_FILE = Sources/Info.plist - SUPPORTED_PLATFORMS = iphoneos iphonesimulator +IPHONEOS_DEPLOYMENT_TARGET = 13.0 diff --git a/Samples/SessionReplay-CameraTest/SessionReplay-CameraTest.yml b/Samples/SessionReplay-CameraTest/SessionReplay-CameraTest.yml index 01360de8b89..44bb23891dc 100644 --- a/Samples/SessionReplay-CameraTest/SessionReplay-CameraTest.yml +++ b/Samples/SessionReplay-CameraTest/SessionReplay-CameraTest.yml @@ -1,9 +1,13 @@ name: SessionReplay-CameraTest +include: + - ../Shared/feature-flags.yml createIntermediateGroups: true generateEmptyDirectories: true projectReferences: Sentry: path: ../../Sentry.xcodeproj + SentrySampleShared: + path: ../SentrySampleShared/SentrySampleShared.xcodeproj fileGroups: - ../Shared/Config - SessionReplay-CameraTest.yml @@ -15,11 +19,10 @@ targets: platform: iOS sources: - Sources - - ../Shared/GitInjections.swift - - ../Shared/screenshot.png - ../Shared/SampleAssets.xcassets dependencies: - target: Sentry/Sentry + - target: SentrySampleShared/SentrySampleShared configFiles: Debug: SessionReplay-CameraTest.xcconfig Release: SessionReplay-CameraTest.xcconfig @@ -31,3 +34,10 @@ targets: - script: ../Shared/reset-git-info.sh name: Reset Git Fields in Info.plist basedOnDependencyAnalysis: false +schemes: + SessionReplay-CameraTest: + templates: + - SampleAppScheme + build: + targets: + SessionReplay-CameraTest: all diff --git a/Samples/SessionReplay-CameraTest/Sources/AppDelegate.swift b/Samples/SessionReplay-CameraTest/Sources/AppDelegate.swift index 2526c4ad193..588925ae319 100644 --- a/Samples/SessionReplay-CameraTest/Sources/AppDelegate.swift +++ b/Samples/SessionReplay-CameraTest/Sources/AppDelegate.swift @@ -1,49 +1,14 @@ import Sentry +import SentrySampleShared import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { - - static var isSentryEnabled = true - static var isSessionReplayEnabled = true - static var isViewRendererV2Enabled = true - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - AppDelegate.reloadSentrySDK() - + SentrySDKWrapper.shared.startSentry() return true } - static func reloadSentrySDK() { - if SentrySDK.isEnabled { - print("SentrySDK already started, closing it") - SentrySDK.close() - } - - if !isSentryEnabled { - print("SentrySDK disabled") - return - } - - SentrySDK.start { options in - options.dsn = "https://6cc9bae94def43cab8444a99e0031c28@o447951.ingest.sentry.io/5428557" - options.debug = true - - options.tracesSampleRate = 1.0 - options.profilesSampleRate = 1.0 - options.sessionReplay.sessionSampleRate = Self.isSessionReplayEnabled ? 1.0 : 0.0 - options.sessionReplay.enableViewRendererV2 = Self.isViewRendererV2Enabled - // Disable the fast view renderering, because we noticed parts (like the tab bar) are not rendered correctly - options.sessionReplay.enableFastViewRendering = false - - options.initialScope = { scope in - scope.injectGitInformation() - scope.setTag(value: "session-replay-camera-test", key: "sample-project") - return scope - } - } - } - // MARK: UISceneSession Lifecycle func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { diff --git a/Samples/SessionReplay-CameraTest/Sources/Base.lproj/Main.storyboard b/Samples/SessionReplay-CameraTest/Sources/Base.lproj/Main.storyboard index 055522d904b..8f49a1813ce 100644 --- a/Samples/SessionReplay-CameraTest/Sources/Base.lproj/Main.storyboard +++ b/Samples/SessionReplay-CameraTest/Sources/Base.lproj/Main.storyboard @@ -1,8 +1,9 @@ - + - + + @@ -17,96 +18,40 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - + + + - + + - - - - + @@ -115,9 +60,6 @@ - - - diff --git a/Samples/SessionReplay-CameraTest/Sources/ViewController.swift b/Samples/SessionReplay-CameraTest/Sources/ViewController.swift index b6c9cca70d3..b21ada9ed02 100644 --- a/Samples/SessionReplay-CameraTest/Sources/ViewController.swift +++ b/Samples/SessionReplay-CameraTest/Sources/ViewController.swift @@ -1,12 +1,13 @@ import AVFoundation import Sentry +import SentrySampleShared import UIKit class ViewController: UIViewController { @IBOutlet weak var backgroundLabel: UILabel! - @IBOutlet weak var controlsContainerView: UIView! - + @IBOutlet weak var previewContainerView: UIView! + @IBOutlet weak var enableSentrySwitch: UISwitch! @IBOutlet weak var enableSessionReplaySwitch: UISwitch! @IBOutlet weak var useViewRendererV2Switch: UISwitch! @@ -30,15 +31,8 @@ class ViewController: UIViewController { private func setupPreviewView() { let previewView = PreviewView() self.previewView = previewView - view.insertSubview(previewView, belowSubview: controlsContainerView) - - previewView.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - previewView.leadingAnchor.constraint(equalTo: view.leadingAnchor), - previewView.trailingAnchor.constraint(equalTo: view.trailingAnchor), - previewView.topAnchor.constraint(equalTo: view.topAnchor), - previewView.bottomAnchor.constraint(equalTo: view.bottomAnchor) - ]) + previewContainerView.addSubview(previewView) + previewView.matchEdgeAnchors(from: previewContainerView) } private func setupErrorLabel() { @@ -88,17 +82,7 @@ class ViewController: UIViewController { } } - @IBAction func didChangeToggleValue(_ sender: UISwitch) { - if sender === enableSessionReplaySwitch { - AppDelegate.isSessionReplayEnabled = sender.isOn - print("Enable session replay flag changed to: \(AppDelegate.isSessionReplayEnabled)") - } else if sender === useViewRendererV2Switch { - AppDelegate.isViewRendererV2Enabled = sender.isOn - print("Use view renderer V2 flag changed to: \(AppDelegate.isViewRendererV2Enabled)") - } else if sender === enableSentrySwitch { - AppDelegate.isSentryEnabled = sender.isOn - print("Enable Sentry flag changed to: \(AppDelegate.isSentryEnabled)") - } - AppDelegate.reloadSentrySDK() + @IBAction func configureSDK(_ sender: Any) { + present(FeaturesViewController(style: .plain), animated: true) } } diff --git a/Samples/Shared/AssertView.swift b/Samples/Shared/AssertView.swift index bdd0fbf0b28..baccd3bd977 100644 --- a/Samples/Shared/AssertView.swift +++ b/Samples/Shared/AssertView.swift @@ -1,15 +1,16 @@ +#if !os(macOS) && !os(watchOS) + import Foundation import Sentry import UIKit -class AssertView: UIView { - +public class AssertView: UIView { var span: Span? - var autoHide = true - + public var autoHide = true + private var assertLabel: UILabel! private var errorLabel: UILabel! - + var message: String? { get { return assertLabel.text @@ -19,7 +20,7 @@ class AssertView: UIView { setNeedsLayout() } } - + var errorMessage: String? { get { return errorLabel.text @@ -29,42 +30,42 @@ class AssertView: UIView { setNeedsLayout() } } - + convenience init() { self.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) } - + override init(frame: CGRect) { super.init(frame: frame) initialize() } - + required init?(coder: NSCoder) { super.init(coder: coder) initialize() } - + private func initialize() { backgroundColor = UIColor(white: 0.3, alpha: 1) - + assertLabel = UILabel().forAutoLayout() assertLabel.textColor = UIColor(white: 1, alpha: 1) assertLabel.accessibilityIdentifier = "ASSERT_MESSAGE" addSubview(assertLabel) - + errorLabel = UILabel().forAutoLayout() errorLabel.textColor = UIColor(white: 1, alpha: 1) errorLabel.accessibilityIdentifier = "ASSERT_ERROR" errorLabel.numberOfLines = 0 addSubview(errorLabel) - + let guide = self.safeAreaLayoutGuide - + let constraints = [ assertLabel.topAnchor.constraint(equalTo: guide.topAnchor, constant: 16), assertLabel.leftAnchor.constraint(equalTo: guide.leftAnchor, constant: 16), assertLabel.rightAnchor.constraint(equalTo: guide.rightAnchor, constant: -16), - + errorLabel.topAnchor.constraint(equalTo: assertLabel.bottomAnchor, constant: 16), errorLabel.leftAnchor.constraint(equalTo: guide.leftAnchor, constant: 16), errorLabel.rightAnchor.constraint(equalTo: guide.rightAnchor, constant: -16), @@ -72,8 +73,8 @@ class AssertView: UIView { ] NSLayoutConstraint.activate(constraints) } - - override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + + public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { //If a tap occurs outside the view, it disappears let result = super.hitTest(point, with: event) if result == nil { @@ -81,12 +82,13 @@ class AssertView: UIView { } return result } - + private func close() { UIAssert.shared.reset() if autoHide { removeFromSuperview() } } - } + +#endif // !os(macOS) && !os(watchOS) diff --git a/Samples/Shared/Config/Deployment.xcconfig b/Samples/Shared/Config/Deployment.xcconfig index 671ec756b30..eab1adfa58e 100644 --- a/Samples/Shared/Config/Deployment.xcconfig +++ b/Samples/Shared/Config/Deployment.xcconfig @@ -1,3 +1,4 @@ +#include "../../Sources/Configuration/DeploymentTargets.xcconfig" + COPY_PHASE_STRIP = NO -IPHONEOS_DEPLOYMENT_TARGET = 13.0 TARGETED_DEVICE_FAMILY = 1,2 diff --git a/Samples/Shared/GitInjections.swift b/Samples/Shared/GitInjections.swift deleted file mode 100644 index ab36e5892ed..00000000000 --- a/Samples/Shared/GitInjections.swift +++ /dev/null @@ -1,25 +0,0 @@ -import Foundation -import Sentry - -extension Bundle { - var gitCommitHash: String? { - infoDictionary?["GIT_COMMIT_HASH"] as? String - } - var gitBranchName: String? { - infoDictionary?["GIT_BRANCH"] as? String - } - var gitStatusClean: Bool { - (infoDictionary?["GIT_STATUS_CLEAN"] as? String) == "1" - } -} - -extension Scope { - @objc public func injectGitInformation() { - if let commitHash = Bundle.main.gitCommitHash { - setTag(value: "\(commitHash)\(Bundle.main.gitStatusClean ? "" : "-dirty")", key: "git-commit-hash") - } - if let branchName = Bundle.main.gitBranchName { - setTag(value: branchName, key: "git-branch-name") - } - } -} diff --git a/Samples/Shared/feature-flags.yml b/Samples/Shared/feature-flags.yml new file mode 100644 index 00000000000..bce28991772 --- /dev/null +++ b/Samples/Shared/feature-flags.yml @@ -0,0 +1,104 @@ +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 + + # session replay + "--disable-session-replay": false + "--io.sentry.session-replay.disableViewRendereV2": false + "--io.sentry.session-replay.enableFastViewRendering": false + + # user feedback + "--io.sentry.ui-test.use-custom-feedback-button": true + "--io.sentry.feedback.dont-use-sentry-user": false + "--io.sentry.feedback.require-name": false + "--io.sentry.feedback.require-email": false + "--io.sentry.feedback.no-animations": false + "--io.sentry.feedback.no-widget-icon": false + "--io.sentry.feedback.no-widget-text": false + "--io.sentry.feedback.all-defaults": false + "--io.sentry.feedback.no-auto-inject-widget": 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-watchdog-tracking": false + "--disable-tracing": false + "--disable-crash-handler": false + "--disable-swizzling": false + "--disable-network-breadcrumbs": false + "--disable-core-data-tracing": false + "--disable-network-tracking": false + "--disable-uiviewcontroller-tracing": false + "--disable-automatic-breadcrumbs": false + "--disable-anr-tracking": false + "--disable-auto-performance-tracing": false + "--disable-ui-tracing": false + "--disable-filemanager-swizzling": false + + # other + "--io.sentry.base64-attachment-data": false + "--io.sentry.disable-http-transport": false + "--disable-spotlight": 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.profilesSampleRate" + value: + isEnabled: false + - variable: "--io.sentry.tracesSamplerValue" + value: + isEnabled: false + - variable: "--io.sentry.profilesSamplerValue" + value: + isEnabled: false + - variable: "--io.sentry.sdk-environment" + value: + isEnabled: false + - variable: "--io.sentry.dsn" + value: + isEnabled: false + - variable: "--io.sentry.user.username" + value: + isEnabled: false + - variable: "--io.sentry.user.email" + value: + isEnabled: false + - variable: "--io.sentry.sessionTrackingIntervalMillis" + value: + isEnabled: false + - variable: "--io.sentry.user.name" + value: + isEnabled: false + - variable: "--io.sentry.profile-session-sample-rate" + value: + isEnabled: false diff --git a/Samples/iOS-ObjectiveC/iOS-ObjectiveC.xcconfig b/Samples/iOS-ObjectiveC/iOS-ObjectiveC.xcconfig index 31dffb4eb9a..de802c60524 100644 --- a/Samples/iOS-ObjectiveC/iOS-ObjectiveC.xcconfig +++ b/Samples/iOS-ObjectiveC/iOS-ObjectiveC.xcconfig @@ -25,5 +25,3 @@ PRODUCT_BUNDLE_IDENTIFIER = io.sentry.iOS-ObjectiveC INFOPLIST_FILE = iOS-ObjectiveC/Info.plist SUPPORTED_PLATFORMS = iphoneos iphonesimulator - -IPHONEOS_DEPLOYMENT_TARGET = 11.0 diff --git a/Samples/iOS-ObjectiveC/iOS-ObjectiveC.yml b/Samples/iOS-ObjectiveC/iOS-ObjectiveC.yml index 506085a1a5e..0c398a74283 100644 --- a/Samples/iOS-ObjectiveC/iOS-ObjectiveC.yml +++ b/Samples/iOS-ObjectiveC/iOS-ObjectiveC.yml @@ -1,4 +1,6 @@ name: iOS-ObjectiveC +include: + - ../Shared/feature-flags.yml createIntermediateGroups: true generateEmptyDirectories: true configs: @@ -9,6 +11,8 @@ configs: projectReferences: Sentry: path: ../../Sentry.xcodeproj + SentrySampleShared: + path: ../SentrySampleShared/SentrySampleShared.xcodeproj fileGroups: - ../Shared/Config - iOS-ObjectiveC.yml @@ -20,18 +24,17 @@ targets: type: application platform: auto sources: + - ../Shared/SampleAssets.xcassets - path: iOS-ObjectiveC excludes: - NoARCCrash.m - path: iOS-ObjectiveC/NoARCCrash.m compilerFlags: - "-fno-objc-arc" - - ../Shared/GitInjections.swift - - ../Shared/screenshot.png - - ../Shared/SampleAssets.xcassets dependencies: - target: Sentry/Sentry - target: Sentry/SentrySwiftUI + - target: SentrySampleShared/SentrySampleShared configFiles: Debug: iOS-ObjectiveC.xcconfig Release: iOS-ObjectiveC.xcconfig @@ -57,6 +60,8 @@ targets: TestCI: iOS-ObjectiveC-UITests.xcconfig schemes: iOS-ObjectiveC: + templates: + - SampleAppScheme build: targets: iOS-ObjectiveC: all diff --git a/Samples/iOS-ObjectiveC/iOS-ObjectiveC/AppDelegate.m b/Samples/iOS-ObjectiveC/iOS-ObjectiveC/AppDelegate.m index e9126dee85a..ef55536c6c0 100644 --- a/Samples/iOS-ObjectiveC/iOS-ObjectiveC/AppDelegate.m +++ b/Samples/iOS-ObjectiveC/iOS-ObjectiveC/AppDelegate.m @@ -3,7 +3,7 @@ @import Sentry; #import -#import "iOS_ObjectiveC-Swift.h" +@import SentrySampleShared; @interface AppDelegate () @end @@ -82,7 +82,7 @@ - (BOOL)application:(UIApplication *)application options.initialScope = ^(SentryScope *scope) { [scope setTagValue:@"" forKey:@""]; - [scope injectGitInformation]; + [GitInjector objc_injectGitInformationInto:scope]; return scope; }; diff --git a/Samples/iOS-ObjectiveC/iOS-ObjectiveC/ViewController.m b/Samples/iOS-ObjectiveC/iOS-ObjectiveC/ViewController.m index fafceca4ab2..5af8997106d 100644 --- a/Samples/iOS-ObjectiveC/iOS-ObjectiveC/ViewController.m +++ b/Samples/iOS-ObjectiveC/iOS-ObjectiveC/ViewController.m @@ -2,6 +2,7 @@ #import "NoARCCrash.h" @import Sentry; +@import SentrySampleShared; @interface ViewController () @@ -80,8 +81,7 @@ - (IBAction)captureUserFeedback:(id)sender - (IBAction)captureUserFeedbackV2:(id)sender { - NSData *data = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"screenshot" - withExtension:@"png"]]; + NSData *data = [NSData dataWithContentsOfURL:BundleResourceProvider.screenshotURL]; NSArray *attachments = nil; if (data != nil) { attachments = @[ data ]; diff --git a/Samples/iOS-Swift/iOS-Benchmarking/SentrySDKPerformanceBenchmarkTests.m b/Samples/iOS-Swift/iOS-Benchmarking/SentrySDKPerformanceBenchmarkTests.m index 10bdba89d49..a3c07fdc2c6 100644 --- a/Samples/iOS-Swift/iOS-Benchmarking/SentrySDKPerformanceBenchmarkTests.m +++ b/Samples/iOS-Swift/iOS-Benchmarking/SentrySDKPerformanceBenchmarkTests.m @@ -37,15 +37,7 @@ - (void)testProfilerCPUUsage [app.buttons[@"startTransactionMainThread"] tap]; - // On iPhone the tab "Benchmarking" is in the collapsed "More" tab bar item. - // On iPad it is directly in the tab bar. - // The test needs to be universal, therefore we just check where it exists. - if ([app.tabBars[@"Tab Bar"].buttons[@"Benchmarking"] waitForExistenceWithTimeout:1.0]) { - [app.tabBars[@"Tab Bar"].buttons[@"Benchmarking"] tap]; - } else { - [app.tabBars[@"Tab Bar"].buttons[@"More"] tap]; - [app.staticTexts[@"Benchmarking"] tap]; - } + [app.tabBars[@"Tab Bar"].buttons[@"Benchmarking"] tap]; [app.buttons[@"Benchmark start"] tap]; diff --git a/Samples/iOS-Swift/iOS-Swift.yml b/Samples/iOS-Swift/iOS-Swift.yml index b67f6dc6f8a..b35a0756838 100644 --- a/Samples/iOS-Swift/iOS-Swift.yml +++ b/Samples/iOS-Swift/iOS-Swift.yml @@ -1,4 +1,6 @@ name: iOS-Swift +include: + - ../Shared/feature-flags.yml createIntermediateGroups: true generateEmptyDirectories: true configs: @@ -9,6 +11,8 @@ configs: projectReferences: Sentry: path: ../../Sentry.xcodeproj + SentrySampleShared: + path: ../SentrySampleShared/SentrySampleShared.xcodeproj fileGroups: - ../Shared/Config - iOS-Swift.yml @@ -22,20 +26,11 @@ targets: platform: auto sources: - iOS-Swift - - ../Shared/GitInjections.swift - - ../Shared/DSNStorage.swift - - ../Shared/SpanExtension.swift - - ../Shared/UIViewControllerExtension.swift - - ../Shared/UIViewExtension.swift - - ../Shared/RandomErrors.swift - - ../Shared/SpanObserver.swift - - ../Shared/UIAssert.swift - - ../Shared/AssertView.swift - - ../Shared/screenshot.png - ../Shared/SampleAssets.xcassets dependencies: - target: Sentry/Sentry - target: iOS-SwiftClip + - target: SentrySampleShared/SentrySampleShared configFiles: Debug: iOS-Swift.xcconfig Release: iOS-Swift.xcconfig @@ -54,11 +49,10 @@ targets: platform: auto sources: - iOS-SwiftClip - - ../Shared/GitInjections.swift - - ../Shared/DSNStorage.swift - - ../Shared/screenshot.png + - ../Shared/SampleAssets.xcassets dependencies: - target: Sentry/Sentry + - target: SentrySampleShared/SentrySampleShared configFiles: Debug: iOS-SwiftClip.xcconfig Release: iOS-SwiftClip.xcconfig @@ -87,6 +81,7 @@ targets: platform: auto sources: - iOS-Benchmarking + - ../Shared/SampleAssets.xcassets configFiles: Debug: iOS-Benchmarking.xcconfig Release: iOS-Benchmarking.xcconfig @@ -94,87 +89,11 @@ targets: TestCI: iOS-Benchmarking.xcconfig schemes: iOS-Swift: + templates: + - SampleAppScheme build: targets: iOS-Swift: all - run: - commandLineArguments: - "--io.sentry.disable-everything": false - "--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 - "--io.sentry.feedback.no-animations": false - "--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 - "--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-watchdog-tracking": false - "--disable-tracing": false - "--io.sentry.disable-app-start-profiling": false - "--disable-crash-handler": false - "--disable-swizzling": false - "--disable-network-breadcrumbs": false - "--disable-core-data-tracing": false - "--disable-network-tracking": false - "--disable-uiviewcontroller-tracing": false - "--disable-automatic-breadcrumbs": false - "--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 - environmentVariables: - - variable: "--io.sentry.tracesSampleRate" - value: - isEnabled: false - - variable: "--io.sentry.profilesSampleRate" - value: - isEnabled: false - - variable: "--io.sentry.tracesSamplerValue" - value: - isEnabled: false - - variable: "--io.sentry.profilesSamplerValue" - value: - isEnabled: false - - variable: "--io.sentry.sdk-environment" - value: - isEnabled: false - - variable: "--io.sentry.dsn" - value: - isEnabled: false - - variable: "--io.sentry.user.username" - value: - isEnabled: false - - variable: "--io.sentry.user.email" - value: - isEnabled: false - - variable: "--io.sentry.sessionTrackingIntervalMillis" - value: - isEnabled: false - - variable: "--io.sentry.user.name" - value: - isEnabled: false - - variable: "--io.sentry.profile-session-sample-rate" - value: - isEnabled: false test: config: Test testPlans: diff --git a/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift b/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift index 2a68fb41113..f4e66b592bb 100644 --- a/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift +++ b/Samples/iOS-Swift/iOS-Swift/AppDelegate.swift @@ -1,3 +1,4 @@ +import SentrySampleShared import UIKit @UIApplicationMain @@ -15,10 +16,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { if args.contains("--io.sentry.wipe-data") { removeAppData() } - if !args.contains("--skip-sentry-init") { - SentrySDKWrapper.shared.startSentry() - } - + + SentrySDKWrapper.shared.startSentry() + if #available(iOS 15.0, *) { metricKit.receiveReports() } diff --git a/Samples/iOS-Swift/iOS-Swift/Base.lproj/Main.storyboard b/Samples/iOS-Swift/iOS-Swift/Base.lproj/Main.storyboard index 6a33c314531..f1c07584f57 100644 --- a/Samples/iOS-Swift/iOS-Swift/Base.lproj/Main.storyboard +++ b/Samples/iOS-Swift/iOS-Swift/Base.lproj/Main.storyboard @@ -1,9 +1,9 @@ - + - + @@ -186,25 +186,6 @@ - - - - - - - - - - - - - - - - - - - @@ -891,7 +872,6 @@ - @@ -917,13 +897,13 @@ - + - + - + + - + - @@ -1583,13 +1572,13 @@ - + - + diff --git a/Samples/iOS-Swift/iOS-Swift/ErrorsViewController.swift b/Samples/iOS-Swift/iOS-Swift/ErrorsViewController.swift index 630e6858f32..3d328f8ff5a 100644 --- a/Samples/iOS-Swift/iOS-Swift/ErrorsViewController.swift +++ b/Samples/iOS-Swift/iOS-Swift/ErrorsViewController.swift @@ -1,5 +1,6 @@ import Foundation import Sentry +import SentrySampleShared import UIKit class ErrorsViewController: UIViewController { diff --git a/Samples/iOS-Swift/iOS-Swift/ExtraViewController.swift b/Samples/iOS-Swift/iOS-Swift/ExtraViewController.swift index e84f9265128..534d24d0057 100644 --- a/Samples/iOS-Swift/iOS-Swift/ExtraViewController.swift +++ b/Samples/iOS-Swift/iOS-Swift/ExtraViewController.swift @@ -1,5 +1,6 @@ import Foundation import Sentry +import SentrySampleShared import UIKit class ExtraViewController: UIViewController { @@ -143,7 +144,7 @@ class ExtraViewController: UIViewController { @IBAction func captureUserFeedbackV2(_ sender: UIButton) { highlightButton(sender) var attachments: [Data]? - if let url = Bundle.main.url(forResource: "screenshot", withExtension: "png"), let data = try? Data(contentsOf: url) { + if let url = BundleResourceProvider.screenshotURL, let data = try? Data(contentsOf: url) { attachments = [data] } let errorEventID = SentrySDK.capture(error: NSError(domain: "test-error.user-feedback.iOS-Swift", code: 1)) @@ -199,6 +200,10 @@ class ExtraViewController: UIViewController { } } + @IBAction func featureFlags(_ sender: Any) { + navigationController?.pushViewController(FeaturesViewController(style: .plain), animated: true) + } + private func calcPi() -> Double { var denominator = 1.0 var pi = 0.0 diff --git a/Samples/iOS-Swift/iOS-Swift/Profiling/ProfilingViewController.swift b/Samples/iOS-Swift/iOS-Swift/Profiling/ProfilingViewController.swift index 549bbbf88c2..18078233aa9 100644 --- a/Samples/iOS-Swift/iOS-Swift/Profiling/ProfilingViewController.swift +++ b/Samples/iOS-Swift/iOS-Swift/Profiling/ProfilingViewController.swift @@ -1,4 +1,5 @@ import Sentry +import SentrySampleShared import UIKit class ProfilingViewController: UIViewController, UITextFieldDelegate { diff --git a/Samples/iOS-Swift/iOS-Swift/Tools/DSNDisplayViewController.swift b/Samples/iOS-Swift/iOS-Swift/Tools/DSNDisplayViewController.swift index adeec5511e5..6e6a6f09601 100644 --- a/Samples/iOS-Swift/iOS-Swift/Tools/DSNDisplayViewController.swift +++ b/Samples/iOS-Swift/iOS-Swift/Tools/DSNDisplayViewController.swift @@ -1,3 +1,4 @@ +import SentrySampleShared import UIKit let fontSize: CGFloat = 12 diff --git a/Samples/iOS-Swift/iOS-Swift/TransactionsViewController.swift b/Samples/iOS-Swift/iOS-Swift/TransactionsViewController.swift index dfb4a833fac..4430081c11c 100644 --- a/Samples/iOS-Swift/iOS-Swift/TransactionsViewController.swift +++ b/Samples/iOS-Swift/iOS-Swift/TransactionsViewController.swift @@ -1,4 +1,5 @@ import Sentry +import SentrySampleShared import UIKit class TransactionsViewController: UIViewController { @@ -47,7 +48,7 @@ class TransactionsViewController: UIViewController { private func readLoremIpsumFile() { dispatchQueue.async { - if let path = Bundle.main.path(forResource: "LoremIpsum", ofType: "txt") { + if let path = BundleResourceProvider.loremIpsumTextFilePath { _ = FileManager.default.contents(atPath: path) } } diff --git a/Samples/iOS-Swift/iOS-Swift/ViewControllers/LoremIpsumViewController.swift b/Samples/iOS-Swift/iOS-Swift/ViewControllers/LoremIpsumViewController.swift index d74750bd347..44fd34863ee 100644 --- a/Samples/iOS-Swift/iOS-Swift/ViewControllers/LoremIpsumViewController.swift +++ b/Samples/iOS-Swift/iOS-Swift/ViewControllers/LoremIpsumViewController.swift @@ -1,4 +1,5 @@ import Sentry +import SentrySampleShared import UIKit class LoremIpsumViewController: UIViewController { @@ -10,7 +11,7 @@ class LoremIpsumViewController: UIViewController { let dispatchQueue = DispatchQueue(label: "LoremIpsumViewController") dispatchQueue.async { - if let path = Bundle.main.path(forResource: "LoremIpsum", ofType: "txt") { + if let path = BundleResourceProvider.loremIpsumTextFilePath { if let contents = FileManager.default.contents(atPath: path) { DispatchQueue.main.async { self.textView.text = String(data: contents, encoding: .utf8) diff --git a/Samples/iOS-Swift/iOS-Swift/ViewControllers/NibViewController.swift b/Samples/iOS-Swift/iOS-Swift/ViewControllers/NibViewController.swift index 108cf52745a..b2ab7c6cc67 100644 --- a/Samples/iOS-Swift/iOS-Swift/ViewControllers/NibViewController.swift +++ b/Samples/iOS-Swift/iOS-Swift/ViewControllers/NibViewController.swift @@ -1,5 +1,6 @@ import Foundation import Sentry +import SentrySampleShared import UIKit class NibViewController: UIViewController { diff --git a/Samples/iOS-Swift/iOS-Swift/ViewControllers/SplitViewController.swift b/Samples/iOS-Swift/iOS-Swift/ViewControllers/SplitViewController.swift index 1356fcca4f3..6cde974ff05 100644 --- a/Samples/iOS-Swift/iOS-Swift/ViewControllers/SplitViewController.swift +++ b/Samples/iOS-Swift/iOS-Swift/ViewControllers/SplitViewController.swift @@ -1,5 +1,6 @@ import Foundation import Sentry +import SentrySampleShared import UIKit class SplitViewController: UISplitViewController { diff --git a/Samples/iOS-Swift/iOS-Swift/ViewControllers/TableViewController.swift b/Samples/iOS-Swift/iOS-Swift/ViewControllers/TableViewController.swift index 2df83233c43..b1be9f900b3 100644 --- a/Samples/iOS-Swift/iOS-Swift/ViewControllers/TableViewController.swift +++ b/Samples/iOS-Swift/iOS-Swift/ViewControllers/TableViewController.swift @@ -1,5 +1,6 @@ import Foundation import Sentry +import SentrySampleShared import UIKit class TableViewController: UITableViewController { diff --git a/Samples/iOS-Swift/iOS-Swift/ViewControllers/TraceTestViewController.swift b/Samples/iOS-Swift/iOS-Swift/ViewControllers/TraceTestViewController.swift index 1aabf5974e2..67a95d92318 100644 --- a/Samples/iOS-Swift/iOS-Swift/ViewControllers/TraceTestViewController.swift +++ b/Samples/iOS-Swift/iOS-Swift/ViewControllers/TraceTestViewController.swift @@ -1,5 +1,6 @@ import Foundation import Sentry +import SentrySampleShared import UIKit class TraceTestViewController: UIViewController { diff --git a/Samples/iOS-Swift/iOS-SwiftClip/AppDelegate.swift b/Samples/iOS-Swift/iOS-SwiftClip/AppDelegate.swift index 8bb7bd8453e..630fb6a0e93 100644 --- a/Samples/iOS-Swift/iOS-SwiftClip/AppDelegate.swift +++ b/Samples/iOS-Swift/iOS-SwiftClip/AppDelegate.swift @@ -1,33 +1,11 @@ import Sentry +import SentrySampleShared import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - - SentrySDK.start { options in - options.dsn = "https://6cc9bae94def43cab8444a99e0031c28@o447951.ingest.sentry.io/5428557" - options.beforeSend = { event in - return event - } - options.debug = true - // Sampling 100% - In Production you probably want to adjust this - options.tracesSampleRate = 1.0 - options.sessionTrackingIntervalMillis = 5_000 - options.initialScope = { scope in - scope.injectGitInformation() - return scope - } - - 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 - - // Experimental features - options.experimental.enableFileManagerSwizzling = true - } - + SentrySDKWrapper.shared.startSentry() return true } @@ -38,11 +16,4 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Use this method to select a configuration to create the new scene with. return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } - - func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { - // Called when the user discards a scene session. - // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. - // Use this method to release any resources that were specific to the discarded scenes, as they will not return. - } - } diff --git a/Samples/iOS-Swift6/iOS-Swift6.xcconfig b/Samples/iOS-Swift6/iOS-Swift6.xcconfig index 1e6fc0636e8..c6bcc990906 100644 --- a/Samples/iOS-Swift6/iOS-Swift6.xcconfig +++ b/Samples/iOS-Swift6/iOS-Swift6.xcconfig @@ -25,3 +25,4 @@ PRODUCT_BUNDLE_IDENTIFIER = io.sentry.sample.iOS-Swift6 INFOPLIST_FILE = iOS-Swift6/Info.plist SUPPORTED_PLATFORMS = iphoneos iphonesimulator SWIFT_VERSION = 6 +IPHONEOS_DEPLOYMENT_TARGET = 13.0 diff --git a/Samples/iOS-Swift6/iOS-Swift6.yml b/Samples/iOS-Swift6/iOS-Swift6.yml index e2de1af6817..d9488c197d3 100644 --- a/Samples/iOS-Swift6/iOS-Swift6.yml +++ b/Samples/iOS-Swift6/iOS-Swift6.yml @@ -1,4 +1,6 @@ name: iOS-Swift6 +include: + - ../Shared/feature-flags.yml createIntermediateGroups: true generateEmptyDirectories: true configs: @@ -9,6 +11,8 @@ configs: projectReferences: Sentry: path: ../../Sentry.xcodeproj + SentrySampleShared: + path: ../SentrySampleShared/SentrySampleShared.xcodeproj fileGroups: - ../Shared/Config - iOS-Swift6.yml @@ -21,11 +25,10 @@ targets: platform: auto sources: - iOS-Swift6 - - ../Shared/GitInjections.swift - - ../Shared/screenshot.png - ../Shared/SampleAssets.xcassets dependencies: - target: Sentry/Sentry + - target: SentrySampleShared/SentrySampleShared configFiles: Debug: iOS-Swift6.xcconfig Release: iOS-Swift6.xcconfig @@ -51,6 +54,8 @@ targets: TestCI: iOS-Swift6-UITests.xcconfig schemes: iOS-Swift6: + templates: + - SampleAppScheme build: targets: iOS-Swift6: all diff --git a/Samples/iOS-Swift6/iOS-Swift6/AppDelegate.swift b/Samples/iOS-Swift6/iOS-Swift6/AppDelegate.swift index 48ede8b61d9..7741a44a0d4 100644 --- a/Samples/iOS-Swift6/iOS-Swift6/AppDelegate.swift +++ b/Samples/iOS-Swift6/iOS-Swift6/AppDelegate.swift @@ -1,55 +1,17 @@ import Sentry +import SentrySampleShared import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { - - func startSentry() { - SentrySDK.start(configureOptions: { options in - options.dsn = "https://6cc9bae94def43cab8444a99e0031c28@o447951.ingest.sentry.io/5428557" - options.beforeSend = { event in - print("Sentry: beforeSend called") - return event - } - options.beforeSendSpan = { span in - print("Sentry: beforeSendSpan called") - return span - } - options.beforeCaptureScreenshot = { _ in - print("Sentry: beforeCaptureScreenshot called") - return true - } - options.beforeCaptureViewHierarchy = { _ in - print("Sentry: beforeCaptureViewHierarchy called") - return true - } - options.attachScreenshot = true - options.attachViewHierarchy = true - options.debug = true - options.sampleRate = 1 - options.tracesSampleRate = 1 - - 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 - - // Experimental features - options.experimental.enableFileManagerSwizzling = true - }) - - } - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - startSentry() + SentrySDKWrapper.shared.startSentry() return true } // MARK: UISceneSession Lifecycle + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } - - func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { - } - } diff --git a/Samples/iOS-SwiftUI/iOS-SwiftUI-UITests.xcconfig b/Samples/iOS-SwiftUI/iOS-SwiftUI-UITests.xcconfig index 9f1038a415b..f264e53fb73 100644 --- a/Samples/iOS-SwiftUI/iOS-SwiftUI-UITests.xcconfig +++ b/Samples/iOS-SwiftUI/iOS-SwiftUI-UITests.xcconfig @@ -22,9 +22,5 @@ #include "../Shared/Config/Metal.xcconfig" PRODUCT_BUNDLE_IDENTIFIER = io.sentry.iOS-Swift-UITests - -CODE_SIGN_STYLE = Manual - SUPPORTED_PLATFORMS = iphoneos iphonesimulator - TEST_TARGET_NAME = iOS-SwiftUI diff --git a/Samples/iOS-SwiftUI/iOS-SwiftUI.yml b/Samples/iOS-SwiftUI/iOS-SwiftUI.yml index 79ada5aed7a..565dd54887d 100644 --- a/Samples/iOS-SwiftUI/iOS-SwiftUI.yml +++ b/Samples/iOS-SwiftUI/iOS-SwiftUI.yml @@ -1,4 +1,6 @@ name: iOS-SwiftUI +include: + - ../Shared/feature-flags.yml createIntermediateGroups: true generateEmptyDirectories: true configs: @@ -9,6 +11,8 @@ configs: projectReferences: Sentry: path: ../../Sentry.xcodeproj + SentrySampleShared: + path: ../SentrySampleShared/SentrySampleShared.xcodeproj fileGroups: - ../Shared/Config - iOS-SwiftUI.yml @@ -21,8 +25,6 @@ targets: platform: auto sources: - iOS-SwiftUI - - ../Shared/GitInjections.swift - - ../Shared/screenshot.png - ../Shared/SampleAssets.xcassets - ../../Sources/Sentry/include/SentryTracer.h - ../../Sources/Sentry/include/SentryPerformanceTracker.h @@ -30,6 +32,7 @@ targets: dependencies: - target: Sentry/Sentry - target: Sentry/SentrySwiftUI + - target: SentrySampleShared/SentrySampleShared configFiles: Debug: iOS-SwiftUI.xcconfig Release: iOS-SwiftUI.xcconfig @@ -55,6 +58,8 @@ targets: TestCI: iOS-SwiftUI-UITests.xcconfig schemes: iOS-SwiftUI: + templates: + - SampleAppScheme build: targets: iOS-SwiftUI: all diff --git a/Samples/iOS-SwiftUI/iOS-SwiftUI/LoremIpsumView.swift b/Samples/iOS-SwiftUI/iOS-SwiftUI/LoremIpsumView.swift index bfa9b700b7c..113437768e1 100644 --- a/Samples/iOS-SwiftUI/iOS-SwiftUI/LoremIpsumView.swift +++ b/Samples/iOS-SwiftUI/iOS-SwiftUI/LoremIpsumView.swift @@ -1,4 +1,5 @@ import Foundation +import SentrySampleShared import SentrySwiftUI import SwiftUI @@ -25,7 +26,7 @@ class LoremIpsumViewModel: ObservableObject { private func fetchLoremIpsum() { let dispatchQueue = DispatchQueue(label: "LoremIpsumViewModel") dispatchQueue.async { - if let path = Bundle.main.path(forResource: "LoremIpsum", ofType: "txt") { + if let path = BundleResourceProvider.loremIpsumTextFilePath { if let contents = FileManager.default.contents(atPath: path) { DispatchQueue.main.async { self.text = String(data: contents, encoding: .utf8) ?? "" diff --git a/Samples/iOS-SwiftUI/iOS-SwiftUI/SwiftUIApp.swift b/Samples/iOS-SwiftUI/iOS-SwiftUI/SwiftUIApp.swift index 51c9edede06..4dec6424f20 100644 --- a/Samples/iOS-SwiftUI/iOS-SwiftUI/SwiftUIApp.swift +++ b/Samples/iOS-SwiftUI/iOS-SwiftUI/SwiftUIApp.swift @@ -1,28 +1,12 @@ import Foundation import Sentry +import SentrySampleShared import SwiftUI @main struct SwiftUIApp: App { init() { - SentrySDK.start { options in - options.dsn = "https://6cc9bae94def43cab8444a99e0031c28@o447951.ingest.sentry.io/5428557" - options.debug = true - options.tracesSampleRate = 1.0 - options.profilesSampleRate = 1.0 - options.sessionReplay.sessionSampleRate = 1.0 - options.initialScope = { scope in - scope.injectGitInformation() - return scope - } - - 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 - - // Experimental features - options.experimental.enableFileManagerSwizzling = true - } + SentrySDKWrapper.shared.startSentry() } var body: some Scene { diff --git a/Samples/iOS13-Swift/iOS13-Swift.xcconfig b/Samples/iOS13-Swift/iOS13-Swift.xcconfig index 10c84098ec9..40a7b362084 100644 --- a/Samples/iOS13-Swift/iOS13-Swift.xcconfig +++ b/Samples/iOS13-Swift/iOS13-Swift.xcconfig @@ -24,3 +24,4 @@ PRODUCT_BUNDLE_IDENTIFIER = io.sentry.sample.iOS13-Swift INFOPLIST_FILE = iOS-Swift/iOS13-Swift/Info.plist SUPPORTED_PLATFORMS = iphoneos iphonesimulator +IPHONEOS_DEPLOYMENT_TARGET = 13.0 diff --git a/Samples/iOS13-Swift/iOS13-Swift.yml b/Samples/iOS13-Swift/iOS13-Swift.yml index 737b5b7bba2..1720e2d3980 100644 --- a/Samples/iOS13-Swift/iOS13-Swift.yml +++ b/Samples/iOS13-Swift/iOS13-Swift.yml @@ -1,4 +1,6 @@ name: iOS13-Swift +include: + - ../Shared/feature-flags.yml createIntermediateGroups: true generateEmptyDirectories: true configs: @@ -9,6 +11,8 @@ configs: projectReferences: Sentry: path: ../../Sentry.xcodeproj + SentrySampleShared: + path: ../SentrySampleShared/SentrySampleShared.xcodeproj fileGroups: - ../Shared/Config - iOS13-Swift.yml @@ -21,20 +25,11 @@ targets: platform: auto sources: - iOS13-Swift - - ../Shared/GitInjections.swift - - ../Shared/DSNStorage.swift - - ../Shared/SpanExtension.swift - - ../Shared/UIViewControllerExtension.swift - - ../Shared/UIViewExtension.swift - - ../Shared/RandomErrors.swift - - ../Shared/SpanObserver.swift - - ../Shared/UIAssert.swift - - ../Shared/AssertView.swift - - ../Shared/screenshot.png - ../Shared/SampleAssets.xcassets dependencies: - target: Sentry/Sentry - target: Sentry/SentrySwiftUI + - target: SentrySampleShared/SentrySampleShared configFiles: Debug: iOS13-Swift.xcconfig Release: iOS13-Swift.xcconfig @@ -60,6 +55,8 @@ targets: TestCI: iOS13-Swift-UITests.xcconfig schemes: iOS13-Swift: + templates: + - SampleAppScheme build: targets: iOS13-Swift: all diff --git a/Samples/iOS13-Swift/iOS13-Swift/AppDelegate.swift b/Samples/iOS13-Swift/iOS13-Swift/AppDelegate.swift index 8ff919f9a8f..588925ae319 100644 --- a/Samples/iOS13-Swift/iOS13-Swift/AppDelegate.swift +++ b/Samples/iOS13-Swift/iOS13-Swift/AppDelegate.swift @@ -1,68 +1,17 @@ import Sentry +import SentrySampleShared import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { - - static let defaultDSN = "https://6cc9bae94def43cab8444a99e0031c28@o447951.ingest.sentry.io/5428557" - - func startSentry() { - // 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 storedDsn: String? - do { - storedDsn = try DSNStorage.shared.getDSN() - try DSNStorage.shared.saveDSN(dsn: storedDsn ?? Self.defaultDSN) - } catch { - print("[iOS-Swift] Failed to read/write DSN: \(error)") - } - - let dsn = storedDsn ?? Self.defaultDSN - - SentrySDK.start { options in - options.dsn = dsn - options.debug = true - if #available(iOS 15.0, *) { - options.enableMetricKit = true - } - // Sampling 100% - In Production you probably want to adjust this - options.tracesSampleRate = 1.0 - options.sessionTrackingIntervalMillis = 5_000 - options.profilesSampleRate = 1.0 - options.attachScreenshot = true - options.attachViewHierarchy = true - options.environment = "test-app" - options.enableTimeToFullDisplayTracing = true - options.initialScope = { scope in - scope.injectGitInformation() - return scope - } - - 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 - - // Experimental features - options.experimental.enableFileManagerSwizzling = true - } - } - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - startSentry() + SentrySDKWrapper.shared.startSentry() return true } // MARK: UISceneSession Lifecycle func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { - // Called when a new scene session is being created. - // Use this method to select a configuration to create the new scene with. return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } - - func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { - // Called when the user discards a scene session. - // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. - // Use this method to release any resources that were specific to the discarded scenes, as they will not return. - } - } diff --git a/Samples/iOS13-Swift/iOS13-Swift/MainViewController.swift b/Samples/iOS13-Swift/iOS13-Swift/MainViewController.swift index 8d27be3422d..f5c8f21ff63 100644 --- a/Samples/iOS13-Swift/iOS13-Swift/MainViewController.swift +++ b/Samples/iOS13-Swift/iOS13-Swift/MainViewController.swift @@ -1,4 +1,5 @@ import Sentry +import SentrySampleShared import UIKit class MainViewController: UIViewController { diff --git a/Samples/iOS15-SwiftUI/iOS15-SwiftUI.yml b/Samples/iOS15-SwiftUI/iOS15-SwiftUI.yml index c2bfd56699b..aea773cf6c4 100644 --- a/Samples/iOS15-SwiftUI/iOS15-SwiftUI.yml +++ b/Samples/iOS15-SwiftUI/iOS15-SwiftUI.yml @@ -1,4 +1,6 @@ name: iOS15-SwiftUI +include: + - ../Shared/feature-flags.yml createIntermediateGroups: true generateEmptyDirectories: true configs: @@ -9,6 +11,8 @@ configs: projectReferences: Sentry: path: ../../Sentry.xcodeproj + SentrySampleShared: + path: ../SentrySampleShared/SentrySampleShared.xcodeproj fileGroups: - ../Shared/Config - iOS15-SwiftUI.yml @@ -20,11 +24,10 @@ targets: platform: auto sources: - iOS15-SwiftUI - - ../Shared/GitInjections.swift - - ../Shared/screenshot.png - ../Shared/SampleAssets.xcassets dependencies: - target: Sentry/Sentry + - target: SentrySampleShared/SentrySampleShared configFiles: Debug: iOS15-SwiftUI.xcconfig Release: iOS15-SwiftUI.xcconfig @@ -40,6 +43,8 @@ targets: basedOnDependencyAnalysis: false schemes: iOS15-SwiftUI: + templates: + - SampleAppScheme build: targets: iOS15-SwiftUI: all diff --git a/Samples/iOS15-SwiftUI/iOS15-SwiftUI/App.swift b/Samples/iOS15-SwiftUI/iOS15-SwiftUI/App.swift index 3c3cef8a411..58f633bec8d 100644 --- a/Samples/iOS15-SwiftUI/iOS15-SwiftUI/App.swift +++ b/Samples/iOS15-SwiftUI/iOS15-SwiftUI/App.swift @@ -1,22 +1,11 @@ import Sentry +import SentrySampleShared import SwiftUI @main struct SwiftUIApp: App { init() { - SentrySDK.start { options in - options.dsn = "https://6cc9bae94def43cab8444a99e0031c28@o447951.ingest.sentry.io/5428557" - options.debug = true - options.tracesSampleRate = 1.0 - options.profilesSampleRate = 1.0 - - 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 - - // Experimental features - options.experimental.enableFileManagerSwizzling = true - } + SentrySDKWrapper.shared.startSentry() } var body: some Scene { diff --git a/Samples/macOS-Swift/Shared/AppDelegate.swift b/Samples/macOS-Swift/Shared/AppDelegate.swift index be8ded76bb0..63c55a1d39c 100644 --- a/Samples/macOS-Swift/Shared/AppDelegate.swift +++ b/Samples/macOS-Swift/Shared/AppDelegate.swift @@ -1,53 +1,10 @@ import Cocoa import Sentry +import SentrySampleShared @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { - func applicationDidFinishLaunching(_ aNotification: Notification) { - // Insert code here to initialize your application - SentrySDK.start { options in - options.dsn = "https://6cc9bae94def43cab8444a99e0031c28@o447951.ingest.sentry.io/5428557" - options.debug = true - options.tracesSampleRate = 1.0 - - let args = ProcessInfo.processInfo.arguments - - // Set the profilesSampleRate to `nil` to enable the configureProfiling block - options.profilesSampleRate = nil - options.configureProfiling = { - $0.profileAppStarts = ProcessInfo.processInfo.arguments.contains("--io.sentry.enable-profile-app-starts") - $0.sessionSampleRate = 1 - } - - if args.contains("--disable-auto-performance-tracing") { - options.enableAutoPerformanceTracing = false - } - - if #available(macOS 12.0, *), !args.contains("--disable-metrickit-integration") { - options.enableMetricKit = true - options.enableMetricKitRawPayload = true - } - - options.initialScope = { scope in - if let path = Bundle.main.path(forResource: "Tongariro", ofType: "jpg") { - scope.addAttachment(Attachment(path: path, filename: "Tongariro.jpg", contentType: "image/jpeg")) - } - - let data = Data("hello".utf8) - scope.addAttachment(Attachment(data: data, filename: "log.txt")) - - scope.injectGitInformation() - - return scope - } - - options.experimental.enableFileManagerSwizzling = !args.contains("--disable-filemanager-swizzling") - } - } - - func applicationWillTerminate(_ aNotification: Notification) { - // Insert code here to tear down your application + SentrySDKWrapper.shared.startSentry() } - } diff --git a/Samples/macOS-Swift/macOS-Swift.yml b/Samples/macOS-Swift/macOS-Swift.yml index 8b1bef8f7d8..c12982f4a45 100644 --- a/Samples/macOS-Swift/macOS-Swift.yml +++ b/Samples/macOS-Swift/macOS-Swift.yml @@ -1,4 +1,6 @@ name: macOS-Swift +include: + - ../Shared/feature-flags.yml createIntermediateGroups: true generateEmptyDirectories: true configs: @@ -9,6 +11,8 @@ configs: projectReferences: Sentry: path: ../../Sentry.xcodeproj + SentrySampleShared: + path: ../SentrySampleShared/SentrySampleShared.xcodeproj fileGroups: - ../Shared/Config - macOS-Swift.yml @@ -20,12 +24,11 @@ targets: platform: auto sources: - Shared - - ../Shared/GitInjections.swift - - ../Shared/screenshot.png - ../Shared/SampleAssets.xcassets dependencies: - target: Sentry/Sentry - target: Sentry/SentrySwiftUI + - target: SentrySampleShared/SentrySampleShared configFiles: Debug: macOS-Swift.xcconfig Release: macOS-Swift.xcconfig @@ -62,12 +65,11 @@ targets: platform: auto sources: - Shared - - ../Shared/GitInjections.swift - - ../Shared/screenshot.png - ../Shared/SampleAssets.xcassets dependencies: - target: Sentry/Sentry - target: Sentry/SentrySwiftUI + - target: SentrySampleShared/SentrySampleShared configFiles: Debug: macOS-Swift-Other.xcconfig Release: macOS-Swift-Other.xcconfig @@ -86,12 +88,11 @@ targets: platform: auto sources: - Shared - - ../Shared/GitInjections.swift - - ../Shared/screenshot.png - ../Shared/SampleAssets.xcassets dependencies: - target: Sentry/Sentry - target: Sentry/SentrySwiftUI + - target: SentrySampleShared/SentrySampleShared configFiles: Debug: macOS-Swift-Sandboxed.xcconfig Release: macOS-Swift-Sandboxed.xcconfig @@ -114,12 +115,11 @@ targets: platform: auto sources: - Shared - - ../Shared/GitInjections.swift - - ../Shared/screenshot.png - ../Shared/SampleAssets.xcassets dependencies: - target: Sentry/Sentry - target: Sentry/SentrySwiftUI + - target: SentrySampleShared/SentrySampleShared configFiles: Debug: macOS-Swift-Sandboxed-Other.xcconfig Release: macOS-Swift-Sandboxed-Other.xcconfig @@ -139,6 +139,8 @@ targets: basedOnDependencyAnalysis: false schemes: macOS-Swift: + templates: + - SampleAppScheme build: targets: macOS-Swift: all @@ -146,3 +148,21 @@ schemes: config: Test testPlans: - path: ../../Plans/macOS-Swift_Base.xctestplan + macOS-Swift-Other: + templates: + - SampleAppScheme + build: + targets: + macOS-Swift: all + macOS-Swift-Sandboxed: + templates: + - SampleAppScheme + build: + targets: + macOS-Swift: all + macOS-Swift-Sandboxed-Other: + templates: + - SampleAppScheme + build: + targets: + macOS-Swift: all diff --git a/Samples/macOS-SwiftUI/macOS-SwiftUI.yml b/Samples/macOS-SwiftUI/macOS-SwiftUI.yml index d53dcf33962..1ac363d60d4 100644 --- a/Samples/macOS-SwiftUI/macOS-SwiftUI.yml +++ b/Samples/macOS-SwiftUI/macOS-SwiftUI.yml @@ -1,4 +1,6 @@ name: macOS-SwiftUI +include: + - ../Shared/feature-flags.yml createIntermediateGroups: true generateEmptyDirectories: true configs: @@ -9,6 +11,8 @@ configs: projectReferences: Sentry: path: ../../Sentry.xcodeproj + SentrySampleShared: + path: ../SentrySampleShared/SentrySampleShared.xcodeproj fileGroups: - ../Shared/Config - macOS-SwiftUI.yml @@ -20,12 +24,11 @@ targets: platform: auto sources: - macOS-SwiftUI - - ../Shared/GitInjections.swift - - ../Shared/screenshot.png - ../Shared/SampleAssets.xcassets dependencies: - target: Sentry/Sentry - target: Sentry/SentrySwiftUI + - target: SentrySampleShared/SentrySampleShared configFiles: Debug: macOS-SwiftUI.xcconfig Release: macOS-SwiftUI.xcconfig @@ -41,6 +44,8 @@ targets: basedOnDependencyAnalysis: false schemes: macOS-SwiftUI: + templates: + - SampleAppScheme build: targets: macOS-SwiftUI: all diff --git a/Samples/macOS-SwiftUI/macOS-SwiftUI/macOS_SwiftUIApp.swift b/Samples/macOS-SwiftUI/macOS-SwiftUI/macOS_SwiftUIApp.swift index 2bab7d24af5..4137e32ddfa 100644 --- a/Samples/macOS-SwiftUI/macOS-SwiftUI/macOS_SwiftUIApp.swift +++ b/Samples/macOS-SwiftUI/macOS-SwiftUI/macOS_SwiftUIApp.swift @@ -1,9 +1,9 @@ import Sentry +import SentrySampleShared import SwiftUI @main struct MacOSSwiftUIApp: App { - @NSApplicationDelegateAdaptor private var appDelegate: MyAppDelegate var body: some Scene { @@ -14,16 +14,7 @@ struct MacOSSwiftUIApp: App { } class MyAppDelegate: NSObject, NSApplicationDelegate, ObservableObject { - func applicationDidFinishLaunching(_ aNotification: Notification) { - SentrySDK.start { options in - options.dsn = "https://6cc9bae94def43cab8444a99e0031c28@o447951.ingest.sentry.io/5428557" - options.debug = true - options.tracesSampleRate = 1.0 - options.profilesSampleRate = 1.0 - options.enableUncaughtNSExceptionReporting = true - options.experimental.enableFileManagerSwizzling = true - } + SentrySDKWrapper.shared.startSentry() } - } diff --git a/Samples/tvOS-Swift/tvOS-SBSwift/AppDelegate.swift b/Samples/tvOS-Swift/tvOS-SBSwift/AppDelegate.swift index eb64f38ecd1..af019a1bac8 100644 --- a/Samples/tvOS-Swift/tvOS-SBSwift/AppDelegate.swift +++ b/Samples/tvOS-Swift/tvOS-SBSwift/AppDelegate.swift @@ -1,40 +1,13 @@ import Sentry +import SentrySampleShared import UIKit + @main class AppDelegate: UIResponder, UIApplicationDelegate { - var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - SentrySDK.start { options in - options.dsn = "https://6cc9bae94def43cab8444a99e0031c28@o447951.ingest.sentry.io/5428557" - options.debug = true - options.sessionTrackingIntervalMillis = 5_000 - // Sampling 100% - In Production you probably want to adjust this - options.tracesSampleRate = 1.0 - - // Experimental features - options.experimental.enableFileManagerSwizzling = true - } - + SentrySDKWrapper.shared.startSentry() return true } - - func applicationWillResignActive(_ application: UIApplication) { - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. - } - - func applicationDidEnterBackground(_ application: UIApplication) { - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - } - - func applicationWillEnterForeground(_ application: UIApplication) { - // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. - } - - func applicationDidBecomeActive(_ application: UIApplication) { - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. - } - } diff --git a/Samples/tvOS-Swift/tvOS-Swift.xcconfig b/Samples/tvOS-Swift/tvOS-Swift.xcconfig index c927fe1aabd..3aa1bf308c5 100644 --- a/Samples/tvOS-Swift/tvOS-Swift.xcconfig +++ b/Samples/tvOS-Swift/tvOS-Swift.xcconfig @@ -25,3 +25,4 @@ PRODUCT_BUNDLE_IDENTIFIER = io.sentry.tvOS-Swift INFOPLIST_FILE = tvOS-Swift/Info.plist SUPPORTED_PLATFORMS = appletvos appletvsimulator TARGETED_DEVICE_FAMILY = 3 +TVOS_DEPLOYMENT_TARGET = 13.0 // UIHostingController is only available on tvOS 13 or later diff --git a/Samples/tvOS-Swift/tvOS-Swift.yml b/Samples/tvOS-Swift/tvOS-Swift.yml index c7ce6cd72a0..6f7238afe05 100644 --- a/Samples/tvOS-Swift/tvOS-Swift.yml +++ b/Samples/tvOS-Swift/tvOS-Swift.yml @@ -1,4 +1,6 @@ name: tvOS-Swift +include: + - ../Shared/feature-flags.yml createIntermediateGroups: true generateEmptyDirectories: true configs: @@ -9,6 +11,8 @@ configs: projectReferences: Sentry: path: ../../Sentry.xcodeproj + SentrySampleShared: + path: ../SentrySampleShared/SentrySampleShared.xcodeproj fileGroups: - ../Shared/Config - tvOS-Swift.yml @@ -21,12 +25,11 @@ targets: platform: auto sources: - tvOS-Swift - - ../Shared/GitInjections.swift - - ../Shared/screenshot.png - ../Shared/SampleAssets.xcassets dependencies: - target: Sentry/Sentry - target: Sentry/SentrySwiftUI + - target: SentrySampleShared/SentrySampleShared configFiles: Debug: tvOS-Swift.xcconfig Release: tvOS-Swift.xcconfig @@ -45,12 +48,11 @@ targets: platform: auto sources: - tvOS-SBSwift - - ../Shared/GitInjections.swift - - ../Shared/screenshot.png - ../Shared/SampleAssets.xcassets dependencies: - target: Sentry/Sentry - target: Sentry/SentrySwiftUI + - target: SentrySampleShared/SentrySampleShared configFiles: Debug: tvOS-SBSwift.xcconfig Release: tvOS-SBSwift.xcconfig @@ -86,6 +88,8 @@ targets: TestCI: tvOS-SBSwift-UITests.xcconfig schemes: tvOS-Swift: + templates: + - SampleAppScheme build: targets: tvOS-Swift: all @@ -95,6 +99,8 @@ schemes: testPlans: - path: ../../Plans/tvOS-Swift_Base.xctestplan tvOS-SBSwift: + templates: + - SampleAppScheme build: targets: tvOS-Swift: all diff --git a/Samples/tvOS-Swift/tvOS-Swift/AppDelegate.swift b/Samples/tvOS-Swift/tvOS-Swift/AppDelegate.swift index bd9156841de..14fa9293a95 100644 --- a/Samples/tvOS-Swift/tvOS-Swift/AppDelegate.swift +++ b/Samples/tvOS-Swift/tvOS-Swift/AppDelegate.swift @@ -1,4 +1,5 @@ import Sentry +import SentrySampleShared import SwiftUI import UIKit @@ -8,29 +9,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - - SentrySDK.start { options in - options.dsn = "https://6cc9bae94def43cab8444a99e0031c28@o447951.ingest.sentry.io/5428557" - options.debug = true - options.sessionTrackingIntervalMillis = 5_000 - // Sampling 100% - In Production you probably want to adjust this - options.tracesSampleRate = 1.0 - options.enableAppHangTracking = true - options.experimental.enableFileManagerSwizzling = true - - options.initialScope = { scope in - if let path = Bundle.main.path(forResource: "Tongariro", ofType: "jpg") { - scope.addAttachment(Attachment(path: path, filename: "Tongariro.jpg", contentType: "image/jpeg")) - } - - let data = Data("hello".utf8) - scope.addAttachment(Attachment(data: data, filename: "log.txt")) - - scope.injectGitInformation() - - return scope - } - } + SentrySDKWrapper.shared.startSentry() // Create the SwiftUI view that provides the window contents. let contentView = ContentView() diff --git a/Samples/visionOS-Swift/visionOS-Swift.xcconfig b/Samples/visionOS-Swift/visionOS-Swift.xcconfig index 6e32f8f44d5..96f10357fef 100644 --- a/Samples/visionOS-Swift/visionOS-Swift.xcconfig +++ b/Samples/visionOS-Swift/visionOS-Swift.xcconfig @@ -23,9 +23,6 @@ PRODUCT_BUNDLE_IDENTIFIER = io.sentry.VisionOS-Swift INFOPLIST_FILE = visionOS-Swift/Info.plist - SUPPORTED_PLATFORMS = xros xrsimulator - XROS_DEPLOYMENT_TARGET = 1.0 - DEVELOPMENT_ASSET_PATHS = "visionOS-Swift/Preview Content" diff --git a/Samples/visionOS-Swift/visionOS-Swift.yml b/Samples/visionOS-Swift/visionOS-Swift.yml index d40cdebb98b..d6e64e1fdaa 100644 --- a/Samples/visionOS-Swift/visionOS-Swift.yml +++ b/Samples/visionOS-Swift/visionOS-Swift.yml @@ -1,4 +1,6 @@ name: visionOS-Swift +include: + - ../Shared/feature-flags.yml createIntermediateGroups: true generateEmptyDirectories: true configs: @@ -9,6 +11,8 @@ configs: projectReferences: Sentry: path: ../../Sentry.xcodeproj + SentrySampleShared: + path: ../SentrySampleShared/SentrySampleShared.xcodeproj fileGroups: - ../Shared/Config - visionOS-Swift.yml @@ -20,13 +24,11 @@ targets: platform: auto sources: - visionOS-Swift - - ../Shared/GitInjections.swift - - ../Shared/screenshot.png - ../Shared/SampleAssets.xcassets - - ../Shared/LoremIpsum.txt dependencies: - target: Sentry/Sentry - target: Sentry/SentrySwiftUI + - target: SentrySampleShared/SentrySampleShared configFiles: Debug: visionOS-Swift.xcconfig Release: visionOS-Swift.xcconfig @@ -42,6 +44,8 @@ targets: basedOnDependencyAnalysis: false schemes: visionOS-Swift: + templates: + - SampleAppScheme build: targets: visionOS-Swift: all diff --git a/Samples/visionOS-Swift/visionOS-Swift/LoremIpsumView.swift b/Samples/visionOS-Swift/visionOS-Swift/LoremIpsumView.swift index bfa9b700b7c..113437768e1 100644 --- a/Samples/visionOS-Swift/visionOS-Swift/LoremIpsumView.swift +++ b/Samples/visionOS-Swift/visionOS-Swift/LoremIpsumView.swift @@ -1,4 +1,5 @@ import Foundation +import SentrySampleShared import SentrySwiftUI import SwiftUI @@ -25,7 +26,7 @@ class LoremIpsumViewModel: ObservableObject { private func fetchLoremIpsum() { let dispatchQueue = DispatchQueue(label: "LoremIpsumViewModel") dispatchQueue.async { - if let path = Bundle.main.path(forResource: "LoremIpsum", ofType: "txt") { + if let path = BundleResourceProvider.loremIpsumTextFilePath { if let contents = FileManager.default.contents(atPath: path) { DispatchQueue.main.async { self.text = String(data: contents, encoding: .utf8) ?? "" diff --git a/Samples/visionOS-Swift/visionOS-Swift/VisionOSSwiftApp.swift b/Samples/visionOS-Swift/visionOS-Swift/VisionOSSwiftApp.swift index d899d2bfd22..3727168cc7d 100644 --- a/Samples/visionOS-Swift/visionOS-Swift/VisionOSSwiftApp.swift +++ b/Samples/visionOS-Swift/visionOS-Swift/VisionOSSwiftApp.swift @@ -1,20 +1,11 @@ import Sentry +import SentrySampleShared import SwiftUI @main struct VisionOSSwiftApp: App { - init() { - SentrySDK.start { options in - options.dsn = "https://6cc9bae94def43cab8444a99e0031c28@o447951.ingest.sentry.io/5428557" - options.debug = true - options.tracesSampleRate = 1.0 - options.attachScreenshot = true - options.attachViewHierarchy = true - - // Experimental features - options.experimental.enableFileManagerSwizzling = true - } + SentrySDKWrapper.shared.startSentry() } var body: some Scene { diff --git a/Samples/watchOS-Swift/watchOS-Swift WatchKit Extension/ExtensionDelegate.swift b/Samples/watchOS-Swift/watchOS-Swift WatchKit Extension/ExtensionDelegate.swift index 9dc6beb49ba..e07584fad7e 100644 --- a/Samples/watchOS-Swift/watchOS-Swift WatchKit Extension/ExtensionDelegate.swift +++ b/Samples/watchOS-Swift/watchOS-Swift WatchKit Extension/ExtensionDelegate.swift @@ -1,38 +1,11 @@ import Sentry +import SentrySampleShared import WatchKit class ExtensionDelegate: NSObject, WKExtensionDelegate { func applicationDidFinishLaunching() { - // Perform any final initialization of your application. - - SentrySDK.start { options in - options.dsn = "https://6cc9bae94def43cab8444a99e0031c28@o447951.ingest.sentry.io/5428557" - options.debug = true - - // Experimental features - options.experimental.enableFileManagerSwizzling = true - } - - SentrySDK.configureScope { scope in - if let path = Bundle.main.path(forResource: "Tongariro", ofType: "jpg") { - scope.addAttachment(Attachment(path: path, filename: "Tongariro.jpg", contentType: "image/jpeg")) - } - - scope.injectGitInformation() - - let data = Data("hello".utf8) - scope.addAttachment(Attachment(data: data, filename: "log.txt")) - } - } - - func applicationDidBecomeActive() { - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. - } - - func applicationWillResignActive() { - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, etc. + SentrySDKWrapper.shared.startSentry() } func handle(_ backgroundTasks: Set) { diff --git a/Samples/watchOS-Swift/watchOS-Swift-App.xcconfig b/Samples/watchOS-Swift/watchOS-Swift-App.xcconfig index fa027052c53..de02103948a 100644 --- a/Samples/watchOS-Swift/watchOS-Swift-App.xcconfig +++ b/Samples/watchOS-Swift/watchOS-Swift-App.xcconfig @@ -26,3 +26,6 @@ INFOPLIST_FILE = "watchOS=Swift WatchKit App/Info.plist" SUPPORTED_PLATFORMS = watchos watchsimulator SDKROOT = watchos TARGETED_DEVICE_FAMILY = 4 + +// WKWatchOnly in the WatchKit app extension's Info.plist is only valid for apps with a MinimumOSVersion of watchOS 6.0 and later. +WATCHOS_DEPLOYMENT_TARGET = 6.0 diff --git a/Samples/watchOS-Swift/watchOS-Swift-Extension.xcconfig b/Samples/watchOS-Swift/watchOS-Swift-Extension.xcconfig index 6b7a9edeb03..3b6604b0b07 100644 --- a/Samples/watchOS-Swift/watchOS-Swift-Extension.xcconfig +++ b/Samples/watchOS-Swift/watchOS-Swift-Extension.xcconfig @@ -27,3 +27,6 @@ SUPPORTED_PLATFORMS = watchos watchsimulator SDKROOT = watchos TARGETED_DEVICE_FAMILY = 4 DEVELOPMENT_ASSET_PATHS = "watchOS-Swift WatchKit Extension/Preview Content" + +// The watch extension's source code uses lots of 6.0+ API, it's easier to set this here than adding availability checks/annotations throughout the code +WATCHOS_DEPLOYMENT_TARGET = 6.0 diff --git a/Samples/watchOS-Swift/watchOS-Swift.yml b/Samples/watchOS-Swift/watchOS-Swift.yml index 78561db7514..87fa31c6373 100644 --- a/Samples/watchOS-Swift/watchOS-Swift.yml +++ b/Samples/watchOS-Swift/watchOS-Swift.yml @@ -1,4 +1,6 @@ name: watchOS-Swift +include: + - ../Shared/feature-flags.yml createIntermediateGroups: true generateEmptyDirectories: true configs: @@ -9,6 +11,8 @@ configs: projectReferences: Sentry: path: ../../Sentry.xcodeproj + SentrySampleShared: + path: ../SentrySampleShared/SentrySampleShared.xcodeproj fileGroups: - ../Shared/Config - watchOS-Swift.yml @@ -24,6 +28,7 @@ targets: - ../Shared/SampleAssets.xcassets dependencies: - target: "watchOS-Swift WatchKit Extension" + - target: SentrySampleShared/SentrySampleShared configFiles: Debug: watchOS-Swift-App.xcconfig Release: watchOS-Swift-App.xcconfig @@ -34,10 +39,10 @@ targets: platform: auto sources: - "watchOS-Swift WatchKit Extension" - - ../Shared/GitInjections.swift - ../Shared/SampleAssets.xcassets dependencies: - target: Sentry/Sentry + - target: SentrySampleShared/SentrySampleShared configFiles: Debug: watchOS-Swift-Extension.xcconfig Release: watchOS-Swift-Extension.xcconfig @@ -53,8 +58,8 @@ targets: basedOnDependencyAnalysis: false schemes: "watchOS-Swift WatchKit App": + templates: + - SampleAppScheme build: targets: "watchOS-Swift WatchKit App": all - run: - executable: "watchOS-Swift WatchKit App" diff --git a/Sources/Swift/Integrations/SessionReplay/SentryReplayOptions.swift b/Sources/Swift/Integrations/SessionReplay/SentryReplayOptions.swift index 41baa6e93dd..e53e4823598 100644 --- a/Sources/Swift/Integrations/SessionReplay/SentryReplayOptions.swift +++ b/Sources/Swift/Integrations/SessionReplay/SentryReplayOptions.swift @@ -40,7 +40,7 @@ public class SentryReplayOptions: NSObject, SentryRedactOptions { /** * Used by Hybrid SDKs. */ - static func fromName(_ name: String) -> SentryReplayOptions.SentryReplayQuality { + public static func fromName(_ name: String) -> SentryReplayOptions.SentryReplayQuality { switch name { case "low": return .low case "medium": return .medium diff --git a/Utils/VersionBump/main.swift b/Utils/VersionBump/main.swift index f0bcdf749bd..345eb1ed78e 100644 --- a/Utils/VersionBump/main.swift +++ b/Utils/VersionBump/main.swift @@ -19,8 +19,7 @@ let restrictFiles = [ "./Sources/Configuration/SDK.xcconfig", "./Sources/Configuration/Versioning.xcconfig", "./Sources/Configuration/SentrySwiftUI.xcconfig", - "./Samples/Shared/Config/Versioning.xcconfig", - "./Samples/iOS-Swift/iOS-Swift/Sample.xcconfig" + "./Samples/Shared/Config/Versioning.xcconfig" ] let args = CommandLine.arguments diff --git a/scripts/build-sample-apps.sh b/scripts/build-sample-apps.sh new file mode 100755 index 00000000000..525a5f583f0 --- /dev/null +++ b/scripts/build-sample-apps.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash + +set -x + +# Function to build a sample app +build_app() { + local xcodeproj=$1 + local scheme=$2 + local destination=$3 + local log_file="/tmp/${scheme}.log" + local derived_data_path="/tmp/${scheme}" + cmd="xcodebuild build -project \"${xcodeproj}\" -scheme \"${scheme}\" -destination \"${destination}\" -configuration Debug -derivedDataPath \"${derived_data_path}\"" + echo "$cmd" + if eval "${cmd}" > "${log_file}" 2>&1; then + echo "${xcodeproj}::${scheme}: success" + else + echo "${xcodeproj}::${scheme}: failed (see ${log_file})" + fi +} + +# Define an associative array with xcodeproj paths as keys and schemes as values +declare -A projects_and_schemes=( + ["iOS13-Swift"]="./Samples/iOS13-Swift/iOS13-Swift.xcodeproj" + ["iOS-SwiftUI"]="./Samples/iOS-SwiftUI/iOS-SwiftUI.xcodeproj" + ["iOS-Swift6"]="./Samples/iOS-Swift6/iOS-Swift6.xcodeproj" + ["iOS-Swift"]="./Samples/iOS-Swift/iOS-Swift.xcodeproj" + ["watchOS-Swift WatchKit App"]="./Samples/watchOS-Swift/watchOS-Swift.xcodeproj" + ["visionOS-Swift"]="./Samples/visionOS-Swift/visionOS-Swift.xcodeproj" + ["tvOS-Swift"]="./Samples/tvOS-Swift/tvOS-Swift.xcodeproj" + ["macOS-SwiftUI"]="./Samples/macOS-SwiftUI/macOS-SwiftUI.xcodeproj" + ["macOS-Swift"]="./Samples/macOS-Swift/macOS-Swift.xcodeproj" + ["macOS-Swift-Other"]="./Samples/macOS-Swift/macOS-Swift.xcodeproj" + ["macOS-Swift-Sandboxed"]="./Samples/macOS-Swift/macOS-Swift.xcodeproj" + ["macOS-Swift-Sandboxed-Other"]="./Samples/macOS-Swift/macOS-Swift.xcodeproj" + ["iOS15-SwiftUI"]="./Samples/iOS15-SwiftUI/iOS15-SwiftUI.xcodeproj" + ["iOS-ObjectiveC"]="./Samples/iOS-ObjectiveC/iOS-ObjectiveC.xcodeproj" +) + +resolve_destination_for_watch_pair() { + local sim_data + sim_data=$(xcrun simctl list -j) + + # Use jq to extract the latest available device pair + local pair_info + pair_info=$(echo "$sim_data" | jq -r ' + .pairs | to_entries[0].value | "\(.watch.udid) \(.phone.udid)"' | head -n1) + + local watch_udid + local phone_udid + read -r watch_udid phone_udid <<< "$pair_info" + + if [[ -z "$watch_udid" || -z "$phone_udid" ]]; then + echo "❌ Could not find matching simulators for a watch and phone pair." + return 1 + fi + + echo "platform=watchOS Simulator,id=$watch_udid,pairedWith=$phone_udid" +} + +watch_destination=$(resolve_destination_for_watch_pair) + +declare -A projects_and_destinations=( + ["iOS13-Swift"]="platform=iOS Simulator,OS=latest,name=iPhone 16" + ["iOS-SwiftUI"]="platform=iOS Simulator,OS=latest,name=iPhone 16" + ["iOS-Swift6"]="platform=iOS Simulator,OS=latest,name=iPhone 16" + ["iOS-Swift"]="platform=iOS Simulator,OS=latest,name=iPhone 16" + ["watchOS-Swift WatchKit App"]="$watch_destination" + ["visionOS-Swift"]="platform=visionOS Simulator,OS=latest,name=Apple Vision Pro" + ["tvOS-Swift"]="platform=tvOS Simulator,OS=latest,name=Apple TV" + ["macOS-SwiftUI"]="platform=macOS" + ["macOS-Swift"]="platform=macOS" + ["macOS-Swift-Other"]="platform=macOS" + ["macOS-Swift-Sandboxed"]="platform=macOS" + ["macOS-Swift-Sandboxed-Other"]="platform=macOS" + ["iOS15-SwiftUI"]="platform=iOS Simulator,OS=latest,name=iPhone 16" + ["iOS-ObjectiveC"]="platform=iOS Simulator,OS=latest,name=iPhone 16" +) + +# Iterate over the associative array and build each project with its scheme +for scheme in "${!projects_and_schemes[@]}"; do + xcodeproj="${projects_and_schemes[$scheme]}" + destination="${projects_and_destinations[$scheme]}" + build_app "$xcodeproj" "$scheme" "$destination" & +done + +# Wait for all background jobs to finish +wait