diff --git a/.buildkite/pipeline.full.yml b/.buildkite/pipeline.full.yml index f344f80ee..4ba9bbf33 100644 --- a/.buildkite/pipeline.full.yml +++ b/.buildkite/pipeline.full.yml @@ -311,3 +311,22 @@ steps: - xcodebuild -scheme swift-package-manager -configuration Release -destination generic/platform=iOS -derivedDataPath DerivedData -quiet build GCC_TREAT_WARNINGS_AS_ERRORS=YES - echo "+++ Build Debug iOS Simulator" - xcodebuild -scheme swift-package-manager -configuration Debug -destination generic/platform=iOS\ Simulator -derivedDataPath DerivedData -quiet build GCC_TREAT_WARNINGS_AS_ERRORS=YES + + - label: 'examples/swiftui' + agents: + queue: opensource-mac-cocoa-11 + commands: + - cd examples/swiftui + - echo "--- Resolve Swift Package Dependencies" + - sed -i '' -e 's/kind = branch/kind = revision/' -e "s/branch = master/revision = ${BUILDKITE_COMMIT}/" swiftui.xcodeproj/project.pbxproj + - xcodebuild -scheme "swiftui (iOS)" -derivedDataPath DerivedData -resolvePackageDependencies + - echo "+++ Build Release iOS" + - xcodebuild -scheme "swiftui (iOS)" -configuration Release -destination generic/platform=iOS -derivedDataPath DerivedData -quiet build GCC_TREAT_WARNINGS_AS_ERRORS=YES + - echo "+++ Build Debug iOS Simulator" + - xcodebuild -scheme "swiftui (iOS)" -configuration Debug -destination generic/platform=iOS\ Simulator -derivedDataPath DerivedData -quiet build GCC_TREAT_WARNINGS_AS_ERRORS=YES + - echo "+++ Build Release macOS" + - xcodebuild -scheme "swiftui (macOS)" -configuration Release -derivedDataPath DerivedData -quiet build GCC_TREAT_WARNINGS_AS_ERRORS=YES + - echo "+++ Build Release tvOS" + - xcodebuild -scheme "swiftui (tvOS)" -configuration Release -destination generic/platform=tvOS -derivedDataPath DerivedData -allowProvisioningUpdates -quiet build GCC_TREAT_WARNINGS_AS_ERRORS=YES + - echo "+++ Build Debug tvOS Simulator" + - xcodebuild -scheme "swiftui (tvOS)" -configuration Debug -destination generic/platform=tvOS\ Simulator -derivedDataPath DerivedData -allowProvisioningUpdates -quiet build GCC_TREAT_WARNINGS_AS_ERRORS=YES diff --git a/.jazzy.yaml b/.jazzy.yaml index 62b6556f0..a1aed84c9 100644 --- a/.jazzy.yaml +++ b/.jazzy.yaml @@ -2,11 +2,11 @@ author_url: "https://www.bugsnag.com" author: "Bugsnag Inc" clean: false # avoid deleting docs/.git framework_root: "Bugsnag" -github_file_prefix: "https://github.com/bugsnag/bugsnag-cocoa/tree/v6.10.4/Bugsnag" +github_file_prefix: "https://github.com/bugsnag/bugsnag-cocoa/tree/v6.11.0/Bugsnag" github_url: "https://github.com/bugsnag/bugsnag-cocoa" hide_documentation_coverage: true module: "Bugsnag" -module_version: "6.10.4" +module_version: "6.11.0" objc: true output: "docs" readme: "README.md" diff --git a/Bugsnag.podspec.json b/Bugsnag.podspec.json index 40389587d..47909655f 100644 --- a/Bugsnag.podspec.json +++ b/Bugsnag.podspec.json @@ -1,6 +1,6 @@ { "name": "Bugsnag", - "version": "6.10.4", + "version": "6.11.0", "summary": "The Bugsnag crash reporting framework for Apple platforms.", "homepage": "https://bugsnag.com", "license": "MIT", @@ -9,7 +9,7 @@ }, "source": { "git": "https://github.com/bugsnag/bugsnag-cocoa.git", - "tag": "v6.10.4" + "tag": "v6.11.0" }, "frameworks": [ "Foundation", diff --git a/Bugsnag.xcodeproj/project.pbxproj b/Bugsnag.xcodeproj/project.pbxproj index 0f3db2043..438751c6c 100644 --- a/Bugsnag.xcodeproj/project.pbxproj +++ b/Bugsnag.xcodeproj/project.pbxproj @@ -633,6 +633,11 @@ 0126F7BF25DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0126F7BA25DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.m */; }; 0126F7C025DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0126F7BA25DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.m */; }; 0126F7C125DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0126F7BA25DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.m */; }; + 013D9CD126C5262F0077F0AD /* UISceneStub.h in Headers */ = {isa = PBXBuildFile; fileRef = 013D9CCF26C5262F0077F0AD /* UISceneStub.h */; }; + 013D9CD226C5262F0077F0AD /* UISceneStub.h in Headers */ = {isa = PBXBuildFile; fileRef = 013D9CCF26C5262F0077F0AD /* UISceneStub.h */; }; + 013D9CD326C5262F0077F0AD /* UISceneStub.h in Headers */ = {isa = PBXBuildFile; fileRef = 013D9CCF26C5262F0077F0AD /* UISceneStub.h */; }; + 013D9CD426C5262F0077F0AD /* UISceneStub.m in Sources */ = {isa = PBXBuildFile; fileRef = 013D9CD026C5262F0077F0AD /* UISceneStub.m */; }; + 013D9CD626C5262F0077F0AD /* UISceneStub.m in Sources */ = {isa = PBXBuildFile; fileRef = 013D9CD026C5262F0077F0AD /* UISceneStub.m */; }; 0140D29A25767C9A00FD0306 /* BugsnagApiClientTest.m in Sources */ = {isa = PBXBuildFile; fileRef = CB9103632502320A00E9D1E2 /* BugsnagApiClientTest.m */; }; 01447605256684500018AB94 /* BugsnagApiClientTest.m in Sources */ = {isa = PBXBuildFile; fileRef = CB9103632502320A00E9D1E2 /* BugsnagApiClientTest.m */; }; 01468F5225876DC1002B0519 /* BSGNotificationBreadcrumbs.h in Headers */ = {isa = PBXBuildFile; fileRef = 01468F5025876DC1002B0519 /* BSGNotificationBreadcrumbs.h */; }; @@ -646,9 +651,9 @@ 015F528525C15BB7000D1915 /* MRCCanary.m in Sources */ = {isa = PBXBuildFile; fileRef = 015F528325C15BB7000D1915 /* MRCCanary.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 015F528625C15BB7000D1915 /* MRCCanary.m in Sources */ = {isa = PBXBuildFile; fileRef = 015F528325C15BB7000D1915 /* MRCCanary.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 015F528725C15BB7000D1915 /* MRCCanary.m in Sources */ = {isa = PBXBuildFile; fileRef = 015F528325C15BB7000D1915 /* MRCCanary.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 0163BF5925823D8D008DC28B /* NotificationBreadcrumbTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0163BF5825823D8D008DC28B /* NotificationBreadcrumbTests.m */; }; - 0163BF5A25823D8D008DC28B /* NotificationBreadcrumbTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0163BF5825823D8D008DC28B /* NotificationBreadcrumbTests.m */; }; - 0163BF5B25823D8D008DC28B /* NotificationBreadcrumbTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0163BF5825823D8D008DC28B /* NotificationBreadcrumbTests.m */; }; + 0163BF5925823D8D008DC28B /* BSGNotificationBreadcrumbsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0163BF5825823D8D008DC28B /* BSGNotificationBreadcrumbsTests.m */; }; + 0163BF5A25823D8D008DC28B /* BSGNotificationBreadcrumbsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0163BF5825823D8D008DC28B /* BSGNotificationBreadcrumbsTests.m */; }; + 0163BF5B25823D8D008DC28B /* BSGNotificationBreadcrumbsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0163BF5825823D8D008DC28B /* BSGNotificationBreadcrumbsTests.m */; }; 016875C6258D003200DFFF69 /* NSUserDefaultsStub.m in Sources */ = {isa = PBXBuildFile; fileRef = 016875C5258D003200DFFF69 /* NSUserDefaultsStub.m */; }; 016875C7258D003200DFFF69 /* NSUserDefaultsStub.m in Sources */ = {isa = PBXBuildFile; fileRef = 016875C5258D003200DFFF69 /* NSUserDefaultsStub.m */; }; 016875C8258D003200DFFF69 /* NSUserDefaultsStub.m in Sources */ = {isa = PBXBuildFile; fileRef = 016875C5258D003200DFFF69 /* NSUserDefaultsStub.m */; }; @@ -1301,11 +1306,13 @@ 0126F7BA25DD512B008483C2 /* BSGEventUploadKSCrashReportOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSGEventUploadKSCrashReportOperation.m; sourceTree = ""; }; 0134524A256BCF7C0088C548 /* BugsnagError+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BugsnagError+Private.h"; sourceTree = ""; }; 0134524B256BD00A0088C548 /* BugsnagThread+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "BugsnagThread+Private.h"; sourceTree = ""; }; + 013D9CCF26C5262F0077F0AD /* UISceneStub.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UISceneStub.h; sourceTree = ""; }; + 013D9CD026C5262F0077F0AD /* UISceneStub.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UISceneStub.m; sourceTree = ""; }; 0140D24725765F8F00FD0306 /* BSGUIKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BSGUIKit.h; sourceTree = ""; }; 01468F5025876DC1002B0519 /* BSGNotificationBreadcrumbs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSGNotificationBreadcrumbs.h; sourceTree = ""; }; 01468F5125876DC1002B0519 /* BSGNotificationBreadcrumbs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BSGNotificationBreadcrumbs.m; sourceTree = ""; }; 015F528325C15BB7000D1915 /* MRCCanary.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MRCCanary.m; sourceTree = ""; }; - 0163BF5825823D8D008DC28B /* NotificationBreadcrumbTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NotificationBreadcrumbTests.m; sourceTree = ""; }; + 0163BF5825823D8D008DC28B /* BSGNotificationBreadcrumbsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BSGNotificationBreadcrumbsTests.m; sourceTree = ""; }; 016875C4258D003200DFFF69 /* NSUserDefaultsStub.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSUserDefaultsStub.h; sourceTree = ""; }; 016875C5258D003200DFFF69 /* NSUserDefaultsStub.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NSUserDefaultsStub.m; sourceTree = ""; }; 017824BD262D65A000D18AFA /* Bugsnag.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Bugsnag.xcconfig; sourceTree = ""; }; @@ -1704,6 +1711,7 @@ 01BDB1CE25DEBF4600A91FAF /* BSGEventUploadKSCrashReportOperationTests.m */, 01847DAB26441A5E00ADA4C7 /* BSGInternalErrorReporterTests.m */, CBCF77AA250142E0004AF22A /* BSGJSONSerializationTests.m */, + 0163BF5825823D8D008DC28B /* BSGNotificationBreadcrumbsTests.m */, 008966C82486D43600DC48C2 /* BSGOutOfMemoryTests.m */, CB6419AA25A73E8C00613D25 /* BSGStorageMigratorTests.m */, CB9103632502320A00E9D1E2 /* BugsnagApiClientTest.m */, @@ -1751,13 +1759,14 @@ 008966D32486D43700DC48C2 /* KSCrash */, 016875C4258D003200DFFF69 /* NSUserDefaultsStub.h */, 016875C5258D003200DFFF69 /* NSUserDefaultsStub.m */, - 0163BF5825823D8D008DC28B /* NotificationBreadcrumbTests.m */, 01B14C55251CE55F00118748 /* report-react-native-promise-rejection.json */, 008966B72486D43500DC48C2 /* report.json */, CB6419B325A7419400613D25 /* v0_files */, 008966AE2486D43500DC48C2 /* Swift Tests */, CBA22499251E429C00B87416 /* TestSupport.h */, CBA2249A251E429C00B87416 /* TestSupport.m */, + 013D9CCF26C5262F0077F0AD /* UISceneStub.h */, + 013D9CD026C5262F0077F0AD /* UISceneStub.m */, 01E8765C256684E700F4B70A /* URLSessionMock.h */, 01E8765D256684E700F4B70A /* URLSessionMock.m */, 012482A225627B51003F7243 /* UIKitTests.m */, @@ -2056,6 +2065,7 @@ 008968F42486DAB800DC48C2 /* BugsnagSessionFileStore.h in Headers */, 008968882486DA9600DC48C2 /* BugsnagHandledState.h in Headers */, CBCF77A325010648004AF22A /* BSGJSONSerialization.h in Headers */, + 013D9CD126C5262F0077F0AD /* UISceneStub.h in Headers */, 00896A082486DAD100DC48C2 /* BSG_KSCrashSentry_Private.h in Headers */, 008969722486DAD000DC48C2 /* BSG_KSSignalInfo.h in Headers */, 008967C22486DA1900DC48C2 /* BugsnagClient+Private.h in Headers */, @@ -2159,6 +2169,7 @@ 0126F79C25DD510E008483C2 /* BSGEventUploadObjectOperation.h in Headers */, 008968892486DA9600DC48C2 /* BugsnagHandledState.h in Headers */, 00896A092486DAD100DC48C2 /* BSG_KSCrashSentry_Private.h in Headers */, + 013D9CD226C5262F0077F0AD /* UISceneStub.h in Headers */, CBCF77A425010648004AF22A /* BSGJSONSerialization.h in Headers */, 008969732486DAD000DC48C2 /* BSG_KSSignalInfo.h in Headers */, 008967C32486DA1900DC48C2 /* BugsnagClient+Private.h in Headers */, @@ -2262,6 +2273,7 @@ 0126F79D25DD510E008483C2 /* BSGEventUploadObjectOperation.h in Headers */, 0089688A2486DA9600DC48C2 /* BugsnagHandledState.h in Headers */, 00896A0A2486DAD100DC48C2 /* BSG_KSCrashSentry_Private.h in Headers */, + 013D9CD326C5262F0077F0AD /* UISceneStub.h in Headers */, CBCF77A525010648004AF22A /* BSGJSONSerialization.h in Headers */, 008969742486DAD100DC48C2 /* BSG_KSSignalInfo.h in Headers */, 008967C42486DA1900DC48C2 /* BugsnagClient+Private.h in Headers */, @@ -2624,6 +2636,7 @@ CBAB4DD82510D2460092CBAA /* BugsnagKVStoreObjC.m in Sources */, 008968A72486DA9600DC48C2 /* BugsnagSession.m in Sources */, 0089683A2486DA6C00DC48C2 /* BugsnagMetadata.m in Sources */, + 013D9CD426C5262F0077F0AD /* UISceneStub.m in Sources */, 008969F62486DAD100DC48C2 /* BSG_KSCrash.m in Sources */, 0089695D2486DAD000DC48C2 /* BSG_KSMach_x86_32.c in Sources */, 00896A1A2486DAD100DC48C2 /* BSG_KSCrashSentry_MachException.c in Sources */, @@ -2707,7 +2720,7 @@ 008967AB2486D43700DC48C2 /* BSG_KSMachTests.m in Sources */, 0089672A2486D43700DC48C2 /* BugsnagStacktraceTest.m in Sources */, 01847DAC26441A5E00ADA4C7 /* BSGInternalErrorReporterTests.m in Sources */, - 0163BF5925823D8D008DC28B /* NotificationBreadcrumbTests.m in Sources */, + 0163BF5925823D8D008DC28B /* BSGNotificationBreadcrumbsTests.m in Sources */, 008967392486D43700DC48C2 /* BugsnagEventFromKSCrashReportTest.m in Sources */, 008967182486D43700DC48C2 /* BugsnagErrorTest.m in Sources */, 004E35352487AFDC007FBAE4 /* BugsnagHandledStateTest.m in Sources */, @@ -2859,7 +2872,7 @@ 008967702486D43700DC48C2 /* NSError+SimpleConstructor_Tests.m in Sources */, 0089671C2486D43700DC48C2 /* BugsnagSessionTest.m in Sources */, 008967AC2486D43700DC48C2 /* BSG_KSMachTests.m in Sources */, - 0163BF5A25823D8D008DC28B /* NotificationBreadcrumbTests.m in Sources */, + 0163BF5A25823D8D008DC28B /* BSGNotificationBreadcrumbsTests.m in Sources */, 01BDB1FD25DEBFB300A91FAF /* BSGEventUploadKSCrashReportOperationTests.m in Sources */, 00896A452486DBF000DC48C2 /* BugsnagConfigurationTests.m in Sources */, 008967492486D43700DC48C2 /* BugsnagUserTest.m in Sources */, @@ -2961,6 +2974,7 @@ CBAB4DDA2510D2460092CBAA /* BugsnagKVStoreObjC.m in Sources */, 008968A92486DA9600DC48C2 /* BugsnagSession.m in Sources */, 0089683C2486DA6C00DC48C2 /* BugsnagMetadata.m in Sources */, + 013D9CD626C5262F0077F0AD /* UISceneStub.m in Sources */, 008969F82486DAD100DC48C2 /* BSG_KSCrash.m in Sources */, 0089695F2486DAD000DC48C2 /* BSG_KSMach_x86_32.c in Sources */, 00896A1C2486DAD100DC48C2 /* BSG_KSCrashSentry_MachException.c in Sources */, @@ -3039,7 +3053,7 @@ E701FAB12490EFE8008D842F /* ConfigurationApiValidationTest.m in Sources */, 0089677D2486D43700DC48C2 /* RFC3339DateTool_Tests.m in Sources */, CBCF77AD250142E0004AF22A /* BSGJSONSerializationTests.m in Sources */, - 0163BF5B25823D8D008DC28B /* NotificationBreadcrumbTests.m in Sources */, + 0163BF5B25823D8D008DC28B /* BSGNotificationBreadcrumbsTests.m in Sources */, 008967562486D43700DC48C2 /* BugsnagOnCrashTest.m in Sources */, 008967A12486D43700DC48C2 /* KSCrashSentry_Tests.m in Sources */, 008967442486D43700DC48C2 /* BugsnagSessionTrackerStopTest.m in Sources */, diff --git a/Bugsnag/Breadcrumbs/BSGNotificationBreadcrumbs.m b/Bugsnag/Breadcrumbs/BSGNotificationBreadcrumbs.m index 57065f8ce..cb77faf61 100644 --- a/Bugsnag/Breadcrumbs/BSGNotificationBreadcrumbs.m +++ b/Bugsnag/Breadcrumbs/BSGNotificationBreadcrumbs.m @@ -41,18 +41,13 @@ - (instancetype)initWithConfiguration:(BugsnagConfiguration *)configuration #endif _breadcrumbSink = breadcrumbSink; _notificationNameMap = @{ -#if TARGET_OS_TV NSUndoManagerDidRedoChangeNotification : @"Redo Operation", NSUndoManagerDidUndoChangeNotification : @"Undo Operation", +#if TARGET_OS_TV UIScreenBrightnessDidChangeNotification : @"Screen Brightness Changed", - UITableViewSelectionDidChangeNotification : @"TableView Select Change", - UIWindowDidBecomeHiddenNotification : @"Window Became Hidden", UIWindowDidBecomeKeyNotification : @"Window Became Key", - UIWindowDidBecomeVisibleNotification : @"Window Became Visible", UIWindowDidResignKeyNotification : @"Window Resigned Key", #elif TARGET_OS_IOS - NSUndoManagerDidRedoChangeNotification : @"Redo Operation", - NSUndoManagerDidUndoChangeNotification : @"Undo Operation", UIApplicationDidEnterBackgroundNotification : @"App Did Enter Background", UIApplicationDidReceiveMemoryWarningNotification : @"Memory Warning", UIApplicationUserDidTakeScreenshotNotification : @"Took Screenshot", @@ -65,16 +60,11 @@ - (instancetype)initWithConfiguration:(BugsnagConfiguration *)configuration UIKeyboardDidShowNotification : @"Keyboard Became Visible", UIMenuControllerDidHideMenuNotification : @"Did Hide Menu", UIMenuControllerDidShowMenuNotification : @"Did Show Menu", - UITableViewSelectionDidChangeNotification : @"TableView Select Change", UITextFieldTextDidBeginEditingNotification : @"Began Editing Text", UITextFieldTextDidEndEditingNotification : @"Stopped Editing Text", UITextViewTextDidBeginEditingNotification : @"Began Editing Text", UITextViewTextDidEndEditingNotification : @"Stopped Editing Text", - UIWindowDidBecomeHiddenNotification : @"Window Became Hidden", - UIWindowDidBecomeVisibleNotification : @"Window Became Visible", #elif TARGET_OS_OSX - NSUndoManagerDidRedoChangeNotification : @"Redo Operation", - NSUndoManagerDidUndoChangeNotification : @"Undo Operation", NSApplicationDidBecomeActiveNotification : @"App Became Active", NSApplicationDidHideNotification : @"App Did Hide", NSApplicationDidResignActiveNotification : @"App Resigned Active", @@ -91,6 +81,17 @@ - (instancetype)initWithConfiguration:(BugsnagConfiguration *)configuration NSWindowWillMiniaturizeNotification : @"Window Will Miniaturize", NSWorkspaceScreensDidSleepNotification : @"Workspace Screen Slept", NSWorkspaceScreensDidWakeNotification : @"Workspace Screen Awoke", +#endif +#if TARGET_OS_IOS || TARGET_OS_TV + UISceneWillConnectNotification : @"Scene Will Connect", + UISceneDidDisconnectNotification : @"Scene Disconnected", + UISceneDidActivateNotification : @"Scene Activated", + UISceneWillDeactivateNotification : @"Scene Will Deactivate", + UISceneWillEnterForegroundNotification : @"Scene Will Enter Foreground", + UISceneDidEnterBackgroundNotification : @"Scene Entered Background", + UITableViewSelectionDidChangeNotification : @"TableView Select Change", + UIWindowDidBecomeHiddenNotification : @"Window Became Hidden", + UIWindowDidBecomeVisibleNotification : @"Window Became Visible", #endif }; } @@ -107,20 +108,14 @@ - (instancetype)initWithConfiguration:(BugsnagConfiguration *)configuration #endif - (NSArray *)automaticBreadcrumbStateEvents { -#if TARGET_OS_TV return @[ NSUndoManagerDidRedoChangeNotification, NSUndoManagerDidUndoChangeNotification, +#if TARGET_OS_TV UIScreenBrightnessDidChangeNotification, - UIWindowDidBecomeHiddenNotification, UIWindowDidBecomeKeyNotification, - UIWindowDidBecomeVisibleNotification, UIWindowDidResignKeyNotification, - ]; #elif TARGET_OS_IOS - return @[ - NSUndoManagerDidRedoChangeNotification, - NSUndoManagerDidUndoChangeNotification, UIApplicationDidEnterBackgroundNotification, UIApplicationDidReceiveMemoryWarningNotification, UIApplicationUserDidTakeScreenshotNotification, @@ -130,11 +125,7 @@ - (instancetype)initWithConfiguration:(BugsnagConfiguration *)configuration UIKeyboardDidShowNotification, UIMenuControllerDidHideMenuNotification, UIMenuControllerDidShowMenuNotification, - UIWindowDidBecomeHiddenNotification, - UIWindowDidBecomeVisibleNotification, - ]; #elif TARGET_OS_OSX - return @[ NSApplicationDidBecomeActiveNotification, NSApplicationDidResignActiveNotification, NSApplicationDidHideNotification, @@ -146,8 +137,18 @@ - (instancetype)initWithConfiguration:(BugsnagConfiguration *)configuration NSWindowDidExitFullScreenNotification, NSWindowWillCloseNotification, NSWindowWillMiniaturizeNotification, - ]; #endif +#if TARGET_OS_IOS || TARGET_OS_TV + UISceneWillConnectNotification, + UISceneDidDisconnectNotification, + UISceneDidActivateNotification, + UISceneWillDeactivateNotification, + UISceneWillEnterForegroundNotification, + UISceneDidEnterBackgroundNotification, + UIWindowDidBecomeHiddenNotification, + UIWindowDidBecomeVisibleNotification, +#endif + ]; } - (NSArray *)automaticBreadcrumbControlEvents { @@ -258,6 +259,23 @@ - (void)startListeningForStateChangeNotification:(NSNotificationName)notificatio } - (void)addBreadcrumbForNotification:(NSNotification *)notification { +#if (defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0) || \ + (defined(__TVOS_13_0) && __TV_OS_VERSION_MAX_ALLOWED >= __TVOS_13_0) + if (@available(iOS 13.0, tvOS 13.0, *)) { + if ([notification.name hasPrefix:@"UIScene"] && [notification.object isKindOfClass:UISCENE]) { +#define BSG_STRING_FROM_CLASS(__CLASS__) __CLASS__ ? NSStringFromClass((Class _Nonnull)__CLASS__) : nil + UIScene *scene = notification.object; + NSMutableDictionary *metadata = [NSMutableDictionary dictionary]; + metadata[@"configuration"] = scene.session.configuration.name; + metadata[@"delegateClass"] = BSG_STRING_FROM_CLASS(scene.session.configuration.delegateClass); + metadata[@"role"] = scene.session.role; + metadata[@"sceneClass"] = BSG_STRING_FROM_CLASS(scene.session.configuration.sceneClass); + metadata[@"title"] = scene.title.length ? scene.title : nil; + [self addBreadcrumbWithType:BSGBreadcrumbTypeState forNotificationName:notification.name metadata:metadata]; + return; + } + } +#endif [self addBreadcrumbWithType:BSGBreadcrumbTypeState forNotificationName:notification.name]; } diff --git a/Bugsnag/Helpers/BSGUIKit.h b/Bugsnag/Helpers/BSGUIKit.h index d05f39acb..6f72f72e9 100644 --- a/Bugsnag/Helpers/BSGUIKit.h +++ b/Bugsnag/Helpers/BSGUIKit.h @@ -14,6 +14,7 @@ // Calling code should be prepared for classes to not be found when UIKit is not linked. #define UIAPPLICATION NSClassFromString(@"UIApplication") #define UIDEVICE NSClassFromString(@"UIDevice") +#define UISCENE NSClassFromString(@"UIScene") #define UIApplicationDidBecomeActiveNotification @"UIApplicationDidBecomeActiveNotification" #define UIApplicationDidEnterBackgroundNotification @"UIApplicationDidEnterBackgroundNotification" @@ -29,6 +30,12 @@ #define UIKeyboardDidShowNotification @"UIKeyboardDidShowNotification" #define UIMenuControllerDidHideMenuNotification @"UIMenuControllerDidHideMenuNotification" #define UIMenuControllerDidShowMenuNotification @"UIMenuControllerDidShowMenuNotification" +#define UISceneWillConnectNotification @"UISceneWillConnectNotification" +#define UISceneDidDisconnectNotification @"UISceneDidDisconnectNotification" +#define UISceneDidActivateNotification @"UISceneDidActivateNotification" +#define UISceneWillDeactivateNotification @"UISceneWillDeactivateNotification" +#define UISceneWillEnterForegroundNotification @"UISceneWillEnterForegroundNotification" +#define UISceneDidEnterBackgroundNotification @"UISceneDidEnterBackgroundNotification" #define UIScreenBrightnessDidChangeNotification @"UIScreenBrightnessDidChangeNotification" #define UITableViewSelectionDidChangeNotification @"UITableViewSelectionDidChangeNotification" #define UITextFieldTextDidBeginEditingNotification @"UITextFieldTextDidBeginEditingNotification" diff --git a/Bugsnag/Payload/BugsnagNotifier.m b/Bugsnag/Payload/BugsnagNotifier.m index 4eabeb72b..a45a8a743 100644 --- a/Bugsnag/Payload/BugsnagNotifier.m +++ b/Bugsnag/Payload/BugsnagNotifier.m @@ -23,7 +23,7 @@ - (instancetype)init { #else self.name = @"Bugsnag Objective-C"; #endif - self.version = @"6.10.4"; + self.version = @"6.11.0"; self.url = @"https://github.com/bugsnag/bugsnag-cocoa"; self.dependencies = [NSMutableArray new]; } diff --git a/CHANGELOG.md b/CHANGELOG.md index aeea0bdf3..9fe8bca02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ Changelog ========= +## 6.11.0 (2021-08-18) + +### Enhancements + +* Add breadcrumbs for `UIScene` notifications. + [#1165](https://github.com/bugsnag/bugsnag-cocoa/pull/1165) + ## 6.10.4 (2021-08-11) ### Bug fixes diff --git a/Framework/Info.plist b/Framework/Info.plist index b19f8f469..f02eb3959 100644 --- a/Framework/Info.plist +++ b/Framework/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 6.10.4 + 6.11.0 CFBundleVersion 1 diff --git a/Tests/NotificationBreadcrumbTests.m b/Tests/BSGNotificationBreadcrumbsTests.m similarity index 78% rename from Tests/NotificationBreadcrumbTests.m rename to Tests/BSGNotificationBreadcrumbsTests.m index 12cdcc6e9..62481d3ea 100644 --- a/Tests/NotificationBreadcrumbTests.m +++ b/Tests/BSGNotificationBreadcrumbsTests.m @@ -1,5 +1,5 @@ // -// NotificationBreadcrumbTests.m +// BSGNotificationBreadcrumbsTests.m // Bugsnag // // Created by Nick Dowell on 10/12/2020. @@ -13,8 +13,12 @@ #import "BSGNotificationBreadcrumbs.h" #import "BugsnagBreadcrumb+Private.h" +#if TARGET_OS_IOS || TARGET_OS_TV +#import "UISceneStub.h" +#endif -@interface NotificationBreadcrumbTests : XCTestCase + +@interface BSGNotificationBreadcrumbsTests : XCTestCase @property NSNotificationCenter *notificationCenter; @property id notificationObject; @@ -28,7 +32,7 @@ @interface NotificationBreadcrumbTests : XCTestCase #pragma mark - -@implementation NotificationBreadcrumbTests +@implementation BSGNotificationBreadcrumbsTests #pragma mark Setup @@ -61,7 +65,7 @@ - (void)leaveBreadcrumbWithMessage:(NSString *)message metadata:(NSDictionary *) #define TEST(__NAME__, __TYPE__, __MESSAGE__, __METADATA__) do { \ BugsnagBreadcrumb *breadcrumb = [self breadcrumbForNotificationWithName:__NAME__]; \ - XCTAssertNotNil(breadcrumb); \ + XCTAssert([NSJSONSerialization isValidJSONObject:breadcrumb.metadata]); \ if (breadcrumb) { \ XCTAssertEqual(breadcrumb.type, __TYPE__); \ XCTAssertEqualObjects(breadcrumb.message, __MESSAGE__); \ @@ -69,6 +73,13 @@ - (void)leaveBreadcrumbWithMessage:(NSString *)message metadata:(NSDictionary *) } \ } while (0) +#pragma mark Tests + +- (void)testNSUndoManagerNotifications { + TEST(NSUndoManagerDidRedoChangeNotification, BSGBreadcrumbTypeState, @"Redo Operation", @{}); + TEST(NSUndoManagerDidUndoChangeNotification, BSGBreadcrumbTypeState, @"Undo Operation", @{}); +} + #pragma mark iOS Tests #if TARGET_OS_IOS @@ -112,12 +123,35 @@ - (void)testUIWindowNotifications { #if TARGET_OS_IOS || TARGET_OS_TV -// This should be on macOS too! -- (void)testNSUndoManagerNotifications { - TEST(NSUndoManagerDidRedoChangeNotification, BSGBreadcrumbTypeState, @"Redo Operation", @{}); - TEST(NSUndoManagerDidUndoChangeNotification, BSGBreadcrumbTypeState, @"Undo Operation", @{}); +#if (defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0) || \ + (defined(__TVOS_13_0) && __TV_OS_VERSION_MAX_ALLOWED >= __TVOS_13_0) + +- (void)testUISceneNotifications { + if (@available(iOS 13.0, tvOS 13.0, *)) { + self.notificationObject = [[UISceneStub alloc] initWithConfiguration:@"Default Configuration" + delegateClass:[BSGNotificationBreadcrumbsTests class] + role:UIWindowSceneSessionRoleApplication + sceneClass:[UISceneStub class] + title:@"Home"]; + + TEST(UISceneWillConnectNotification, BSGBreadcrumbTypeState, @"Scene Will Connect", + (@{@"configuration": @"Default Configuration", + @"delegateClass": @"BSGNotificationBreadcrumbsTests", + @"role": @"UIWindowSceneSessionRoleApplication", + @"sceneClass": @"UISceneStub", + @"title": @"Home"})); + + self.notificationObject = nil; + TEST(UISceneDidDisconnectNotification, BSGBreadcrumbTypeState, @"Scene Disconnected", @{}); + TEST(UISceneDidActivateNotification, BSGBreadcrumbTypeState, @"Scene Activated", @{}); + TEST(UISceneWillDeactivateNotification, BSGBreadcrumbTypeState, @"Scene Will Deactivate", @{}); + TEST(UISceneWillEnterForegroundNotification, BSGBreadcrumbTypeState, @"Scene Will Enter Foreground", @{}); + TEST(UISceneDidEnterBackgroundNotification, BSGBreadcrumbTypeState, @"Scene Entered Background", @{}); + } } +#endif + - (void)testUITableViewNotifications { TEST(UITableViewSelectionDidChangeNotification, BSGBreadcrumbTypeNavigation, @"TableView Select Change", @{}); } diff --git a/Tests/BugsnagBreadcrumbsTest.m b/Tests/BugsnagBreadcrumbsTest.m index 52abe2ca8..8a828e592 100644 --- a/Tests/BugsnagBreadcrumbsTest.m +++ b/Tests/BugsnagBreadcrumbsTest.m @@ -503,7 +503,14 @@ - (void)testCrashReportWriterConcurrency { NSError *error = nil; NSData *data = [NSData dataWithBytesNoCopy:buffer.buffer length:buffer.length freeWhenDone:NO]; - XCTAssert([NSJSONSerialization JSONObjectWithData:data options:0 error:&error], @"%@", error); + if (![NSJSONSerialization JSONObjectWithData:data options:0 error:&error]) { + [self addAttachment:[XCTAttachment attachmentWithUniformTypeIdentifier:@"public.plain-text" + name:@"breadcrumbs.json" + payload:data + userInfo:nil]]; + XCTFail(@"Breadcrumbs JSON could not be parsed: %@", error); + break; + } } free(buffer.buffer); diff --git a/Tests/Info.plist b/Tests/Info.plist index ddfc429f7..a8b4e3342 100644 --- a/Tests/Info.plist +++ b/Tests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 6.10.4 + 6.11.0 CFBundleVersion 1 diff --git a/Tests/UIKitTests.m b/Tests/UIKitTests.m index 740dddf48..43f27ffcf 100644 --- a/Tests/UIKitTests.m +++ b/Tests/UIKitTests.m @@ -43,6 +43,18 @@ - (void)testNotificationNames { ASSERT_NOTIFICATION_NAME(UIWindowDidBecomeKeyNotification); ASSERT_NOTIFICATION_NAME(UIWindowDidBecomeVisibleNotification); ASSERT_NOTIFICATION_NAME(UIWindowDidResignKeyNotification); + +#if (defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0) || \ + (defined(__TVOS_13_0) && __TV_OS_VERSION_MAX_ALLOWED >= __TVOS_13_0) + if (@available(iOS 13.0, tvOS 13.0, *)) { + ASSERT_NOTIFICATION_NAME(UISceneWillConnectNotification); + ASSERT_NOTIFICATION_NAME(UISceneDidDisconnectNotification); + ASSERT_NOTIFICATION_NAME(UISceneDidActivateNotification); + ASSERT_NOTIFICATION_NAME(UISceneWillDeactivateNotification); + ASSERT_NOTIFICATION_NAME(UISceneWillEnterForegroundNotification); + ASSERT_NOTIFICATION_NAME(UISceneDidEnterBackgroundNotification); + } +#endif } @end diff --git a/Tests/UISceneStub.h b/Tests/UISceneStub.h new file mode 100644 index 000000000..0b6854320 --- /dev/null +++ b/Tests/UISceneStub.h @@ -0,0 +1,29 @@ +// +// UISceneStub.h +// BugsnagTests +// +// Created by Nick Dowell on 12/08/2021. +// Copyright © 2021 Bugsnag Inc. All rights reserved. +// + +#import + +#if (defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0) || \ + (defined(__TVOS_13_0) && __TV_OS_VERSION_MAX_ALLOWED >= __TVOS_13_0) + +NS_ASSUME_NONNULL_BEGIN + +API_AVAILABLE(ios(13.0), tvos(13.0)) +@interface UISceneStub : NSObject + +- (instancetype)initWithConfiguration:(NSString *)configuration + delegateClass:(Class)delegateClass + role:(UISceneSessionRole)role + sceneClass:(Class)sceneClass + title:(NSString *)title; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Tests/UISceneStub.m b/Tests/UISceneStub.m new file mode 100644 index 000000000..9df8160ce --- /dev/null +++ b/Tests/UISceneStub.m @@ -0,0 +1,59 @@ +// +// UISceneStub.m +// BugsnagTests +// +// Created by Nick Dowell on 12/08/2021. +// Copyright © 2021 Bugsnag Inc. All rights reserved. +// + +#import "UISceneStub.h" + +#if (defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0) || \ + (defined(__TVOS_13_0) && __TV_OS_VERSION_MAX_ALLOWED >= __TVOS_13_0) + +@interface UISceneStub () + +@property (nonatomic) NSString *configurationName; +@property (weak, nonatomic) Class delegateClass; +@property (nonatomic) UISceneSessionRole role; +@property (nonatomic) Class sceneClass; +@property (nonatomic) NSString *title; + +@end + +@implementation UISceneStub + +- (instancetype)initWithConfiguration:(NSString *)configuration + delegateClass:(Class)delegateClass + role:(UISceneSessionRole)role + sceneClass:(Class)sceneClass + title:(NSString *)title { + if ((self = [super init])) { + _configurationName = configuration; + _delegateClass = delegateClass; + _role = role; + _sceneClass = sceneClass; + _title = title; + } + return self; +} + +- (id)session { + return self; +} + +- (id)configuration { + return self; +} + +- (NSString *)name { + return self.configurationName; +} + +- (BOOL)isKindOfClass:(Class)aClass { + return [NSStringFromClass(aClass) isEqualToString:@"UIScene"] || [super isKindOfClass:aClass]; +} + +@end + +#endif diff --git a/VERSION b/VERSION index 1678f3d43..1de66e5ff 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.10.4 +6.11.0 diff --git a/examples/swiftui/Shared/AnObjCClass.h b/examples/swiftui/Shared/AnObjCClass.h new file mode 100644 index 000000000..2c7c8dc7b --- /dev/null +++ b/examples/swiftui/Shared/AnObjCClass.h @@ -0,0 +1,30 @@ +// Copyright (c) 2016 Bugsnag, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall remain in place +// in this source code. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +@interface AnObjCClass : NSObject + +- (void)trap; +- (void)corruptSomeMemory; +- (void)accessInvalidMemoryAddress; +- (void)throwCxxException; + +@end diff --git a/examples/swiftui/Shared/AnObjCClass.mm b/examples/swiftui/Shared/AnObjCClass.mm new file mode 100644 index 000000000..153dd3356 --- /dev/null +++ b/examples/swiftui/Shared/AnObjCClass.mm @@ -0,0 +1,67 @@ +// Copyright (c) 2016 Bugsnag, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall remain in place +// in this source code. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "AnObjCClass.h" + +#import + +@implementation AnObjCClass + +- (void)trap { + __builtin_trap(); +} + +- (void)corruptSomeMemory { + /* Some random data */ + void *cache[] = { + NULL, NULL, NULL + }; + + void *displayStrings[6] = { + (void *)"This little piggy went to the meerket", + (void *)"This little piggy stayed at home", + cache, + (void *)"This little piggy had roast beef.", + (void *)"This little piggy had none.", + (void *)"And this little piggy went 'Wee! Wee! Wee!' all the way home", + }; + + /* A corrupted/under-retained/re-used piece of memory */ + struct { + void *isa; + } corruptObj; + corruptObj.isa = displayStrings; + + /* Message an invalid/corrupt object. This will deadlock crash reporters + * using Objective-C. */ + [(__bridge id)&corruptObj class]; +} + +- (void)accessInvalidMemoryAddress { + // This should result in an EXC_BAD_ACCESS mach exception with code = KERN_INVALID_ADDRESS and subcode = 0xDEADBEEF + void (* ptr)(void) = (void (*)(void))0xDEADBEEF; + ptr(); +} + +- (void)throwCxxException { + throw std::runtime_error("This is a C++ exception"); +} + +@end diff --git a/examples/swiftui/Shared/AppMain.swift b/examples/swiftui/Shared/AppMain.swift new file mode 100644 index 000000000..82f3958ff --- /dev/null +++ b/examples/swiftui/Shared/AppMain.swift @@ -0,0 +1,108 @@ +// Copyright (c) 2021 Bugsnag, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall remain in place +// in this source code. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Bugsnag +import SwiftUI + +@main +struct AppMain: App { + + init() { + /** + This is the minimum amount of setup required for Bugsnag to work. Simply add your API key to the app's .plist (Supporting Files/Info.plist) and the application will deliver all error and session notifications to the appropriate dashboard. + + You can find your API key in your Bugsnag dashboard under the settings menu. + */ + Bugsnag.start() + + /** + Bugsnag behavior can be configured through the plist and/or further extended in code by creating a BugsnagConfiguration object and passing it to [Bugsnag startWithConfiguration]. + + All subsequent setup is optional, and will configure your Bugsnag setup in different ways. A few common examples are included here, for more detailed explanations please look at the documented configuration options at https://docs.bugsnag.com/platforms/ios/configuration-options/ + */ + + // Create config object from the application plist +// let config = BugsnagConfiguration.loadConfig() + + // ... or construct an empty object +// let config = BugsnagConfiguration("YOUR-API-KEY") + + /** + This sets some user information that will be attached to each error. + */ +// config.setUser("DefaultUser", withEmail:"Not@real.fake", andName:"Default User") + + /** + The appVersion will let you see what release an error is present in. This will be picked up automatically from your build settings, but can be manually overwritten as well. + */ +// config.appVersion = "1.5.0" + + /** + When persisting a user you won't need to set the user information everytime the app opens, instead it will be persisted between each app session. + */ +// config.persistUser = true + + /** + This option allows you to send more or less detail about errors to Bugsnag. Setting it to Always or Unhandled means you'll have detailed stacktraces of all app threads available when debugging unexpected errors. + */ +// config.sendThreads = .always + + /** + Enabled error types allow you to customize exactly what errors are automatically captured and delivered to your Bugsnag dashboard. A detailed breakdown of each error type can be found in the configuration option documentation. + */ +// config.enabledErrorTypes.ooms = false +// config.enabledErrorTypes.unhandledExceptions = true +// config.enabledErrorTypes.machExceptions = true + + /** + If there's information that you do not wish sent to your Bugsnag dashboard, such as passwords or user information, you can set the keys as redacted. When a notification is sent to Bugsnag all keys matching your set filters will be redacted before they leave your application. + All automatically captured data can be found here: https://docs.bugsnag.com/platforms/ios/automatically-captured-data/. + */ +// config.redactedKeys = ["password", "credit_card_number"] + + /** + Finally, start Bugsnag with the specified configuration: + */ +// Bugsnag.start(with: config) + + #if os(macOS) + /** + Mac exceptions on the main thread are caught by Cocoa and don’t reach Bugsnag by default. + To report these exceptions from a SwiftUI app, it is necessary to override `-[NSApplication reportException:]` + */ + let selector = #selector(NSApplication.reportException(_:)) + if let method = class_getInstanceMethod(NSApplication.self, selector) { + typealias FunctionType = @convention(c) (NSApplication, Selector, NSException) -> Void + let originalFunc = unsafeBitCast(method_getImplementation(method), to: FunctionType.self) + let replacementBlock: @convention(block) (NSApplication, NSException) -> Void = { + Bugsnag.notify($1) + originalFunc($0, selector, $1) + } + method_setImplementation(method, imp_implementationWithBlock(replacementBlock)) + } + #endif + } + + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/examples/swiftui/Shared/Assets.xcassets/AccentColor.colorset/Contents.json b/examples/swiftui/Shared/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/examples/swiftui/Shared/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Contents.json b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..045f085ff --- /dev/null +++ b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,172 @@ +{ + "images" : [ + { + "filename" : "Icon-App-20x20@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "Icon-App-20x20@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "filename" : "Icon-App-29x29@1x.png", + "idiom" : "iphone", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "Icon-App-29x29@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "Icon-App-29x29@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "filename" : "Icon-App-40x40@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "Icon-App-40x40@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "filename" : "Icon-App-60x60@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "filename" : "Icon-App-60x60@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "filename" : "Icon-App-20x20@1x.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "filename" : "Icon-App-20x20@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "Icon-App-29x29@1x.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "Icon-App-29x29@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "Icon-App-40x40@1x.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "filename" : "Icon-App-40x40@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "Icon-App-76x76@1x.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "filename" : "Icon-App-76x76@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "filename" : "Icon-App-83.5x83.5@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "filename" : "ItunesArtwork@2x.png", + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 000000000..5efca42d8 Binary files /dev/null and b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 000000000..84734e387 Binary files /dev/null and b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 000000000..5f0a59cd8 Binary files /dev/null and b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 000000000..4cb8ebb38 Binary files /dev/null and b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 000000000..18943e04d Binary files /dev/null and b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 000000000..c39ee6b13 Binary files /dev/null and b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 000000000..84734e387 Binary files /dev/null and b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 000000000..8fc9ada0e Binary files /dev/null and b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 000000000..bd4e92180 Binary files /dev/null and b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 000000000..bd4e92180 Binary files /dev/null and b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 000000000..2901a7af6 Binary files /dev/null and b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 000000000..5c499e6d6 Binary files /dev/null and b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 000000000..97b4556a6 Binary files /dev/null and b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 000000000..be50c84da Binary files /dev/null and b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png new file mode 100644 index 000000000..e5aa5674b Binary files /dev/null and b/examples/swiftui/Shared/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png differ diff --git a/examples/swiftui/Shared/Assets.xcassets/Contents.json b/examples/swiftui/Shared/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/examples/swiftui/Shared/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/examples/swiftui/Shared/Bridging-Header.h b/examples/swiftui/Shared/Bridging-Header.h new file mode 100644 index 000000000..a50906e2a --- /dev/null +++ b/examples/swiftui/Shared/Bridging-Header.h @@ -0,0 +1,25 @@ +// Copyright (c) 2021 Bugsnag, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall remain in place +// in this source code. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "AnObjCClass.h" + +#if TARGET_OS_IOS +#import "OutOfMemoryController.h" +#endif diff --git a/examples/swiftui/Shared/ContentView.swift b/examples/swiftui/Shared/ContentView.swift new file mode 100644 index 000000000..c8c2a7d62 --- /dev/null +++ b/examples/swiftui/Shared/ContentView.swift @@ -0,0 +1,41 @@ +// Copyright (c) 2021 Bugsnag, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall remain in place +// in this source code. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Bugsnag +import SwiftUI + +struct ContentView: View { + var body: some View { + #if os(iOS) + NavigationView { + Examples() + .listStyle(InsetGroupedListStyle()) + } + #else + Examples() + #endif + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } +} diff --git a/examples/swiftui/Shared/Examples.swift b/examples/swiftui/Shared/Examples.swift new file mode 100644 index 000000000..e2e87ccb8 --- /dev/null +++ b/examples/swiftui/Shared/Examples.swift @@ -0,0 +1,185 @@ +// Copyright (c) 2021 Bugsnag, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall remain in place +// in this source code. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Bugsnag +import SwiftUI + +struct Examples: View { + var body: some View { + List { + Section(header: Text("Crashes"), footer: Text("Events which terminate the app are sent to Bugsnag automatically. Reopen the app after a crash to send reports.")) { + Button("Uncaught Objective-C Exception", action: generateUncaughtException) + Button("POSIX Signal", action: generatePOSIXSignal) + Button("Mach exception", action: generateMachException) + Button("Memory Corruption", action: generateMemoryCorruption) + Button("Stack Overflow", action: generateStackOverflow) + Button("Swift Precondition Failure", action: generateAssertionFailure) + #if os(iOS) + NavigationLink("Out Of Memory", destination: OutOfMemoryPresenter()) + #endif + Button("Uncaught C++ Exception", action: generateCxxException) + Button("Fatal App Hang", action: generateFatalAppHang) + } + Section(header: Text("Handled Errors"), footer: Text("Events which can be handled gracefully can also be reported to Bugsnag.")) { + Button("Send error with notifyError()", action: sendAnError) + } + Section(header: Text("Metadata, breadcrumbs, and callbacks"), footer: Text("Adding diagnostic data and modifying how the event shows on the Bugsnag dashboard.")) { + Button("Add client metadata", action: addClientMetadata) + Button("Add filtered metadata", action: addFilteredMetadata) + Button("Clear metadata", action: clearMetadata) + Button("Add custom breadcrumb", action: addCustomBreadcrumb) + Button("Add breadcrumb with callback", action: addBreadcrumbWithCallback) + Button("Set user", action: setUser) + } + Section(header: Text("Sessions"), footer: Text("Demonstrates the methods of manually determining when sessions are created and expire.")) { + Button("Start new session", action: startNewSession) + Button("Pause current session", action: pauseCurrentSession) + Button("Resume current session", action: resumeCurrentSession) + } + } + .navigationTitle("Bugsnag Examples") + } +} + +// MARK: - Crashes + +func generateUncaughtException() { + let someJson : Dictionary = ["foo": Color.green] + do { + let data = try JSONSerialization.data(withJSONObject: someJson, options: .prettyPrinted) + print("Received data: %@", data) + } catch { + // Why does this crash the app? A very good question. + } +} + +func generatePOSIXSignal() { + AnObjCClass().trap() +} + +func generateMachException() { + AnObjCClass().accessInvalidMemoryAddress() +} + +func generateStackOverflow() { + let items = ["Something!"] + // Use if statement to remove warning about calling self through any path + if (items[0] == "Something!") { + generateStackOverflow() + } + print("items: %@", items) +} + +func generateMemoryCorruption() { + AnObjCClass().corruptSomeMemory() +} + +func generateAssertionFailure() { + preconditionFailure("This should NEVER happen") +} + +func generateCxxException() { + AnObjCClass().throwCxxException() +} + +func generateFatalAppHang() { + Thread.sleep(forTimeInterval: 3) + _exit(1) +} + +// MARK: - Handled Errors + +func sendAnError() { + do { + try FileManager.default.removeItem(atPath:"//invalid/file") + } catch { + Bugsnag.notifyError(error) { event in + // modify report properties in the (optional) block + event.severity = .info + return true + } + } +} + +// MARK: - Metadata, breadcrumbs, and callbacks + +private enum MetadataSection: String { + case extras +} + +func addClientMetadata() { + // This method adds some metadata to your application client, that will be included in all subsequent error reports, and visible on the "extras" tab on the Bugsnag dashboard. + Bugsnag.addMetadata("metadata!", key: "client", section: MetadataSection.extras.rawValue) +} + +func addFilteredMetadata() { + // This method adds some metadata that will be redacted on the Bugsnag dashboard. + // It will only work if the optional configuration is uncommented in the `AppDelegate.swift` file. + Bugsnag.addMetadata("secret123", key: "password", section: MetadataSection.extras.rawValue) +} + +func clearMetadata() { + // This method clears all metadata in the "extras" tab that would be attached to the error reports. + // It won't clear data that hasn't been added yet, like data attached through a callback. + Bugsnag.clearMetadata(section: MetadataSection.extras.rawValue) +} + +func addCustomBreadcrumb() { + // This is the simplest example of leaving a custom breadcrumb. + // This will show up under the "breadcrumbs" tab of your error on the Bugsnag dashboard. + Bugsnag.leaveBreadcrumb(withMessage: "This is our custom breadcrumb!") +} + +func addBreadcrumbWithCallback() { + // This adds a callback to the breadcrumb process, setting a different breadcrumb type if a specific message is present. + // It leaves a slightly more detailed breadcrumb than before, with a message, metadata, and type all specified. + Bugsnag.addOnBreadcrumb { + if $0.message == "Custom breadcrumb name" { + $0.type = .process + } + return true + } + Bugsnag.leaveBreadcrumb("Custom breadcrumb name", metadata: ["metadata": "here!"], type: .manual) +} + +func setUser() { + // This sets a user on the client, similar to setting one on the configuration. + // It will also set the user in a session payload. + Bugsnag.setUser("user123", withEmail: "TestUser@example.com", andName: "Test Userson") +} + +// MARK: - Sessions + +func startNewSession() { + // This starts a new session within Bugsnag. + // While sessions are generally configured to work automatically, this allows you to define when a session begins. + Bugsnag.startSession() +} + +func pauseCurrentSession() { + // This pauses the current session. + // If an error occurs when a session is paused it will not be included in the session statistics for the project. + Bugsnag.pauseSession() +} + +func resumeCurrentSession() { + // This allows you to resume the previous session, keeping a record of any errors that previously occurred within a single session intact. + Bugsnag.resumeSession(); +} diff --git a/examples/swiftui/iOS/Info.plist b/examples/swiftui/iOS/Info.plist new file mode 100644 index 000000000..84208078b --- /dev/null +++ b/examples/swiftui/iOS/Info.plist @@ -0,0 +1,55 @@ + + + + + bugsnag + + apiKey + YOUR-API-KEY + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + + UIApplicationSupportsIndirectInputEvents + + UILaunchScreen + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/examples/swiftui/iOS/OutOfMemoryController.h b/examples/swiftui/iOS/OutOfMemoryController.h new file mode 100644 index 000000000..44161c8a7 --- /dev/null +++ b/examples/swiftui/iOS/OutOfMemoryController.h @@ -0,0 +1,25 @@ +// Copyright (c) 2016 Bugsnag, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall remain in place +// in this source code. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +@interface OutOfMemoryController : UIViewController + +@end diff --git a/examples/swiftui/iOS/OutOfMemoryController.m b/examples/swiftui/iOS/OutOfMemoryController.m new file mode 100644 index 000000000..4e68af9bb --- /dev/null +++ b/examples/swiftui/iOS/OutOfMemoryController.m @@ -0,0 +1,131 @@ +// Copyright (c) 2016 Bugsnag, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall remain in place +// in this source code. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import "OutOfMemoryController.h" + +#include +#include +#include +#include +#include + +#define PRINT_STATS 1 + +#define MEGABYTE 0x100000 + +@implementation OutOfMemoryController + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.view.backgroundColor = UIColor.systemGroupedBackgroundColor; +} + +- (void)didReceiveMemoryWarning { + NSLog(@"--> Received a low memory warning"); +} + +- (void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + + struct utsname system = {0}; + uname(&system); + NSLog(@"*** Device = %s", system.machine); + + NSUInteger physicalMemory = (NSUInteger)NSProcessInfo.processInfo.physicalMemory; + NSUInteger megabytes = physicalMemory / MEGABYTE; + NSLog(@"*** Physical memory = %lu MB", (unsigned long)megabytes); + + // + // The ActiveHard limit and point at which a memory warning is sent varies by device + // Some data from http://www.chenxiyao.com/article/10/read + // + // Device Total Warn Limit + // ======================================================= + // iPad3,1 iPad 3rd gen 987 560 700 (70%) + // iPhone5,1 iPhone 5 650 + // iPhone6,2 iPhone 5s 1000 600 650 (65%) + // iPhone7,1 iPhone 6 Plus 650 + // iPhone7,2 iPhone 6 650 + // iPhone8,1 iPhone 6s 1380 + // iPhone8,2 iPhone 6s Plus 1380 + // iPhone8,4 iPhone SE 1380 + // iPhone9,1 iPhone 7 1380 + // iPhone9,2 iPhone 7 Plus 2050 + // iPhone10,1 iPhone 8 1380 + // iPhone10,2 iPhone 8 Plus 2050 + // iPhone10,3 iPhone X 1400 + // iPhone11,2 iPhone XS 2050 + // iPhone11,4 iPhone XS Max 2050 + // iPhone11,8 iPhone XR 1400 + // iPhone12,1 iPhone 11 3859 2098 (54%) + // iPhone12,8 iPhone SE (2) 2965 1994 2098 (70%) + // iPhone13,1 iPhone 12 mini 3718 1546 2098 (57%) + // + NSUInteger limit = 0; + task_vm_info_data_t vm_info = {0}; + mach_msg_type_number_t count = TASK_VM_INFO_COUNT; + kern_return_t result = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t)&vm_info, &count); + if (result == KERN_SUCCESS && count >= TASK_VM_INFO_REV4_COUNT) { + limit = (NSUInteger)(vm_info.phys_footprint + vm_info.limit_bytes_remaining) / MEGABYTE; + NSLog(@"*** Memory limit = %lu MB", (unsigned long)limit); + } else if (!strcmp(system.machine, "iPhone6,2")) { + limit = 650; + NSLog(@"*** Memory limit = %lu MB", (unsigned long)limit); + } else { + limit = MIN(2098, megabytes * 70 / 100); + NSLog(@"*** Memory limit = %lu MB (estimated)", (unsigned long)limit); + } + + // The size of the initial block needs to be under the memory warning limit in order for one to be reliably sent. + NSUInteger initial = limit * 70 / 100; + NSLog(@"*** Dirtying an initial block of %lu MB", (unsigned long)initial); + [self consumeMegabytes:initial]; + + NSLog(@"*** Dirtying remaining memory in ~2 seconds"); + NSTimeInterval timeInterval = 2.0 / (limit - initial); + [NSTimer scheduledTimerWithTimeInterval:timeInterval target:self selector:@selector(timerFired) userInfo:nil repeats:YES]; +} + +- (void)timerFired { + [self consumeMegabytes:1]; +} + +- (void)consumeMegabytes:(NSUInteger)megabytes { + for (NSUInteger i = 0; i < megabytes; i++) { + volatile char *ptr = malloc(MEGABYTE); + // Originally used NSPageSize() but on iPhone 5s iOS 12 that didn't result in dirtying all the memory allocated. + const int pagesize = 4096; + const int npages = MEGABYTE / pagesize; + for (int page = 0; page < npages; page++) { + ptr[page * pagesize] = 42; // Dirty each page + } + } +#if PRINT_STATS + task_vm_info_data_t info; + mach_msg_type_number_t count = TASK_VM_INFO_COUNT; + kern_return_t result = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &info, &count); + assert(result == KERN_SUCCESS); + unsigned long long physicalMemory = NSProcessInfo.processInfo.physicalMemory; + NSLog(@"%4llu / %4llu MB (%llu%%)", info.phys_footprint / MEGABYTE, physicalMemory / MEGABYTE, info.phys_footprint * 100 / physicalMemory); +#endif +} + +@end diff --git a/examples/swiftui/iOS/OutOfMemoryPresenter.swift b/examples/swiftui/iOS/OutOfMemoryPresenter.swift new file mode 100644 index 000000000..e2749d41a --- /dev/null +++ b/examples/swiftui/iOS/OutOfMemoryPresenter.swift @@ -0,0 +1,32 @@ +// Copyright (c) 2021 Bugsnag, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall remain in place +// in this source code. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import SwiftUI + +struct OutOfMemoryPresenter: UIViewControllerRepresentable { + typealias UIViewControllerType = OutOfMemoryController + + func makeUIViewController(context: Context) -> OutOfMemoryController { + OutOfMemoryController() + } + + func updateUIViewController(_ uiViewController: OutOfMemoryController, context: Context) { + } +} diff --git a/examples/swiftui/macOS/Info.plist b/examples/swiftui/macOS/Info.plist new file mode 100644 index 000000000..d6344bbc1 --- /dev/null +++ b/examples/swiftui/macOS/Info.plist @@ -0,0 +1,31 @@ + + + + + bugsnag + + apiKey + YOUR-API-KEY + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + + diff --git a/examples/swiftui/macOS/macOS.entitlements b/examples/swiftui/macOS/macOS.entitlements new file mode 100644 index 000000000..625af03d9 --- /dev/null +++ b/examples/swiftui/macOS/macOS.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-only + + com.apple.security.network.client + + + diff --git a/examples/swiftui/swiftui.xcodeproj/project.pbxproj b/examples/swiftui/swiftui.xcodeproj/project.pbxproj new file mode 100644 index 000000000..d071da248 --- /dev/null +++ b/examples/swiftui/swiftui.xcodeproj/project.pbxproj @@ -0,0 +1,661 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 52; + objects = { + +/* Begin PBXBuildFile section */ + 0158BE3626C16E6E002BD41A /* AppMain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E53F8126C13133003AD7BB /* AppMain.swift */; }; + 0158BE3726C16E6E002BD41A /* AnObjCClass.mm in Sources */ = {isa = PBXBuildFile; fileRef = 01E53FAA26C13D77003AD7BB /* AnObjCClass.mm */; }; + 0158BE3826C16E6E002BD41A /* Examples.swift in Sources */ = {isa = PBXBuildFile; fileRef = 018AB2B826C15AA2009CCDC2 /* Examples.swift */; }; + 0158BE3926C16E6E002BD41A /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E53F8226C13133003AD7BB /* ContentView.swift */; }; + 0158BE3B26C16ED3002BD41A /* Bugsnag in Frameworks */ = {isa = PBXBuildFile; productRef = 0158BE3A26C16ED3002BD41A /* Bugsnag */; }; + 018AB2B926C15AA2009CCDC2 /* Examples.swift in Sources */ = {isa = PBXBuildFile; fileRef = 018AB2B826C15AA2009CCDC2 /* Examples.swift */; }; + 018AB2BA26C15AA6009CCDC2 /* Examples.swift in Sources */ = {isa = PBXBuildFile; fileRef = 018AB2B826C15AA2009CCDC2 /* Examples.swift */; }; + 01E53F9426C13135003AD7BB /* AppMain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E53F8126C13133003AD7BB /* AppMain.swift */; }; + 01E53F9526C13135003AD7BB /* AppMain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E53F8126C13133003AD7BB /* AppMain.swift */; }; + 01E53F9626C13135003AD7BB /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E53F8226C13133003AD7BB /* ContentView.swift */; }; + 01E53F9726C13135003AD7BB /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E53F8226C13133003AD7BB /* ContentView.swift */; }; + 01E53F9826C13135003AD7BB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 01E53F8326C13135003AD7BB /* Assets.xcassets */; }; + 01E53F9926C13135003AD7BB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 01E53F8326C13135003AD7BB /* Assets.xcassets */; }; + 01E53FA426C13C20003AD7BB /* Bugsnag in Frameworks */ = {isa = PBXBuildFile; productRef = 01E53FA326C13C20003AD7BB /* Bugsnag */; }; + 01E53FA726C13C35003AD7BB /* Bugsnag in Frameworks */ = {isa = PBXBuildFile; productRef = 01E53FA626C13C35003AD7BB /* Bugsnag */; }; + 01E53FAE26C13D77003AD7BB /* AnObjCClass.mm in Sources */ = {isa = PBXBuildFile; fileRef = 01E53FAA26C13D77003AD7BB /* AnObjCClass.mm */; }; + 01E53FAF26C13D77003AD7BB /* AnObjCClass.mm in Sources */ = {isa = PBXBuildFile; fileRef = 01E53FAA26C13D77003AD7BB /* AnObjCClass.mm */; }; + 01E53FB026C13D77003AD7BB /* OutOfMemoryController.m in Sources */ = {isa = PBXBuildFile; fileRef = 01E53FAC26C13D77003AD7BB /* OutOfMemoryController.m */; }; + 01E53FB626C1419E003AD7BB /* OutOfMemoryPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E53FB526C1419E003AD7BB /* OutOfMemoryPresenter.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 0158BE2726C16E12002BD41A /* swiftui.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = swiftui.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 0158BE3226C16E14002BD41A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 018AB2B826C15AA2009CCDC2 /* Examples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Examples.swift; sourceTree = ""; }; + 01E53F8126C13133003AD7BB /* AppMain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMain.swift; sourceTree = ""; }; + 01E53F8226C13133003AD7BB /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 01E53F8326C13135003AD7BB /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 01E53F8826C13135003AD7BB /* swiftui.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = swiftui.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 01E53F8B26C13135003AD7BB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 01E53F9026C13135003AD7BB /* swiftui.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = swiftui.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 01E53F9226C13135003AD7BB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 01E53F9326C13135003AD7BB /* macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = macOS.entitlements; sourceTree = ""; }; + 01E53FA826C13D77003AD7BB /* Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = ""; }; + 01E53FAA26C13D77003AD7BB /* AnObjCClass.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AnObjCClass.mm; sourceTree = ""; }; + 01E53FAB26C13D77003AD7BB /* OutOfMemoryController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OutOfMemoryController.h; sourceTree = ""; }; + 01E53FAC26C13D77003AD7BB /* OutOfMemoryController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OutOfMemoryController.m; sourceTree = ""; }; + 01E53FAD26C13D77003AD7BB /* AnObjCClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AnObjCClass.h; sourceTree = ""; }; + 01E53FB526C1419E003AD7BB /* OutOfMemoryPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutOfMemoryPresenter.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 0158BE2426C16E12002BD41A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0158BE3B26C16ED3002BD41A /* Bugsnag in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 01E53F8526C13135003AD7BB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 01E53FA426C13C20003AD7BB /* Bugsnag in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 01E53F8D26C13135003AD7BB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 01E53FA726C13C35003AD7BB /* Bugsnag in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0158BE2826C16E12002BD41A /* tvOS */ = { + isa = PBXGroup; + children = ( + 0158BE3226C16E14002BD41A /* Info.plist */, + ); + path = tvOS; + sourceTree = ""; + }; + 01E53F7B26C13133003AD7BB = { + isa = PBXGroup; + children = ( + 01E53F8026C13133003AD7BB /* Shared */, + 01E53F8A26C13135003AD7BB /* iOS */, + 01E53F9126C13135003AD7BB /* macOS */, + 0158BE2826C16E12002BD41A /* tvOS */, + 01E53F8926C13135003AD7BB /* Products */, + 01E53FA526C13C35003AD7BB /* Frameworks */, + ); + sourceTree = ""; + }; + 01E53F8026C13133003AD7BB /* Shared */ = { + isa = PBXGroup; + children = ( + 01E53F8126C13133003AD7BB /* AppMain.swift */, + 01E53F8226C13133003AD7BB /* ContentView.swift */, + 018AB2B826C15AA2009CCDC2 /* Examples.swift */, + 01E53FAD26C13D77003AD7BB /* AnObjCClass.h */, + 01E53FAA26C13D77003AD7BB /* AnObjCClass.mm */, + 01E53F8326C13135003AD7BB /* Assets.xcassets */, + 01E53FA826C13D77003AD7BB /* Bridging-Header.h */, + ); + path = Shared; + sourceTree = ""; + }; + 01E53F8926C13135003AD7BB /* Products */ = { + isa = PBXGroup; + children = ( + 01E53F8826C13135003AD7BB /* swiftui.app */, + 01E53F9026C13135003AD7BB /* swiftui.app */, + 0158BE2726C16E12002BD41A /* swiftui.app */, + ); + name = Products; + sourceTree = ""; + }; + 01E53F8A26C13135003AD7BB /* iOS */ = { + isa = PBXGroup; + children = ( + 01E53F8B26C13135003AD7BB /* Info.plist */, + 01E53FAB26C13D77003AD7BB /* OutOfMemoryController.h */, + 01E53FAC26C13D77003AD7BB /* OutOfMemoryController.m */, + 01E53FB526C1419E003AD7BB /* OutOfMemoryPresenter.swift */, + ); + path = iOS; + sourceTree = ""; + }; + 01E53F9126C13135003AD7BB /* macOS */ = { + isa = PBXGroup; + children = ( + 01E53F9226C13135003AD7BB /* Info.plist */, + 01E53F9326C13135003AD7BB /* macOS.entitlements */, + ); + path = macOS; + sourceTree = ""; + }; + 01E53FA526C13C35003AD7BB /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 0158BE2626C16E12002BD41A /* swiftui (tvOS) */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0158BE3526C16E14002BD41A /* Build configuration list for PBXNativeTarget "swiftui (tvOS)" */; + buildPhases = ( + 0158BE2326C16E12002BD41A /* Sources */, + 0158BE2426C16E12002BD41A /* Frameworks */, + 0158BE2526C16E12002BD41A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "swiftui (tvOS)"; + packageProductDependencies = ( + 0158BE3A26C16ED3002BD41A /* Bugsnag */, + ); + productName = tvOS; + productReference = 0158BE2726C16E12002BD41A /* swiftui.app */; + productType = "com.apple.product-type.application"; + }; + 01E53F8726C13135003AD7BB /* swiftui (iOS) */ = { + isa = PBXNativeTarget; + buildConfigurationList = 01E53F9C26C13135003AD7BB /* Build configuration list for PBXNativeTarget "swiftui (iOS)" */; + buildPhases = ( + 01E53F8426C13135003AD7BB /* Sources */, + 01E53F8526C13135003AD7BB /* Frameworks */, + 01E53F8626C13135003AD7BB /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "swiftui (iOS)"; + packageProductDependencies = ( + 01E53FA326C13C20003AD7BB /* Bugsnag */, + ); + productName = "swiftui-multiplatform (iOS)"; + productReference = 01E53F8826C13135003AD7BB /* swiftui.app */; + productType = "com.apple.product-type.application"; + }; + 01E53F8F26C13135003AD7BB /* swiftui (macOS) */ = { + isa = PBXNativeTarget; + buildConfigurationList = 01E53F9F26C13135003AD7BB /* Build configuration list for PBXNativeTarget "swiftui (macOS)" */; + buildPhases = ( + 01E53F8C26C13135003AD7BB /* Sources */, + 01E53F8D26C13135003AD7BB /* Frameworks */, + 01E53F8E26C13135003AD7BB /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "swiftui (macOS)"; + packageProductDependencies = ( + 01E53FA626C13C35003AD7BB /* Bugsnag */, + ); + productName = "swiftui-multiplatform (macOS)"; + productReference = 01E53F9026C13135003AD7BB /* swiftui.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 01E53F7C26C13133003AD7BB /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1250; + LastUpgradeCheck = 1250; + TargetAttributes = { + 0158BE2626C16E12002BD41A = { + CreatedOnToolsVersion = 12.5.1; + }; + 01E53F8726C13135003AD7BB = { + CreatedOnToolsVersion = 12.5.1; + LastSwiftMigration = 1250; + }; + 01E53F8F26C13135003AD7BB = { + CreatedOnToolsVersion = 12.5.1; + LastSwiftMigration = 1250; + }; + }; + }; + buildConfigurationList = 01E53F7F26C13133003AD7BB /* Build configuration list for PBXProject "swiftui" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 01E53F7B26C13133003AD7BB; + packageReferences = ( + 01E53FA226C13C20003AD7BB /* XCRemoteSwiftPackageReference "bugsnag-cocoa" */, + ); + productRefGroup = 01E53F8926C13135003AD7BB /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 01E53F8726C13135003AD7BB /* swiftui (iOS) */, + 01E53F8F26C13135003AD7BB /* swiftui (macOS) */, + 0158BE2626C16E12002BD41A /* swiftui (tvOS) */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 0158BE2526C16E12002BD41A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 01E53F8626C13135003AD7BB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 01E53F9826C13135003AD7BB /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 01E53F8E26C13135003AD7BB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 01E53F9926C13135003AD7BB /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 0158BE2326C16E12002BD41A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0158BE3626C16E6E002BD41A /* AppMain.swift in Sources */, + 0158BE3826C16E6E002BD41A /* Examples.swift in Sources */, + 0158BE3926C16E6E002BD41A /* ContentView.swift in Sources */, + 0158BE3726C16E6E002BD41A /* AnObjCClass.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 01E53F8426C13135003AD7BB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 01E53FB026C13D77003AD7BB /* OutOfMemoryController.m in Sources */, + 01E53FAE26C13D77003AD7BB /* AnObjCClass.mm in Sources */, + 01E53F9626C13135003AD7BB /* ContentView.swift in Sources */, + 01E53F9426C13135003AD7BB /* AppMain.swift in Sources */, + 018AB2B926C15AA2009CCDC2 /* Examples.swift in Sources */, + 01E53FB626C1419E003AD7BB /* OutOfMemoryPresenter.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 01E53F8C26C13135003AD7BB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 018AB2BA26C15AA6009CCDC2 /* Examples.swift in Sources */, + 01E53FAF26C13D77003AD7BB /* AnObjCClass.mm in Sources */, + 01E53F9726C13135003AD7BB /* ContentView.swift in Sources */, + 01E53F9526C13135003AD7BB /* AppMain.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 0158BE3326C16E14002BD41A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 372ZUL2ZB7; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = tvOS/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.bugsnag.examples.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_MODULE_NAME = swiftuiapp; + PRODUCT_NAME = swiftui; + SDKROOT = appletvos; + SWIFT_OBJC_BRIDGING_HEADER = "Shared/Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 14.5; + }; + name = Debug; + }; + 0158BE3426C16E14002BD41A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 372ZUL2ZB7; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = tvOS/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.bugsnag.examples.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_MODULE_NAME = swiftuiapp; + PRODUCT_NAME = swiftui; + SDKROOT = appletvos; + SWIFT_OBJC_BRIDGING_HEADER = "Shared/Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 14.5; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 01E53F9A26C13135003AD7BB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 01E53F9B26C13135003AD7BB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 01E53F9D26C13135003AD7BB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 372ZUL2ZB7; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = iOS/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.bugsnag.examples.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_MODULE_NAME = swiftuiapp; + PRODUCT_NAME = swiftui; + SDKROOT = iphoneos; + SWIFT_OBJC_BRIDGING_HEADER = "Shared/Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 01E53F9E26C13135003AD7BB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 372ZUL2ZB7; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = iOS/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.bugsnag.examples.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_MODULE_NAME = swiftuiapp; + PRODUCT_NAME = swiftui; + SDKROOT = iphoneos; + SWIFT_OBJC_BRIDGING_HEADER = "Shared/Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 01E53FA026C13135003AD7BB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 372ZUL2ZB7; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = macOS/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 11.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.bugsnag.examples.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_MODULE_NAME = swiftuiapp; + PRODUCT_NAME = swiftui; + SDKROOT = macosx; + SWIFT_OBJC_BRIDGING_HEADER = "Shared/Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 01E53FA126C13135003AD7BB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 372ZUL2ZB7; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = macOS/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 11.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.bugsnag.examples.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_MODULE_NAME = swiftuiapp; + PRODUCT_NAME = swiftui; + SDKROOT = macosx; + SWIFT_OBJC_BRIDGING_HEADER = "Shared/Bridging-Header.h"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 0158BE3526C16E14002BD41A /* Build configuration list for PBXNativeTarget "swiftui (tvOS)" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0158BE3326C16E14002BD41A /* Debug */, + 0158BE3426C16E14002BD41A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 01E53F7F26C13133003AD7BB /* Build configuration list for PBXProject "swiftui" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 01E53F9A26C13135003AD7BB /* Debug */, + 01E53F9B26C13135003AD7BB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 01E53F9C26C13135003AD7BB /* Build configuration list for PBXNativeTarget "swiftui (iOS)" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 01E53F9D26C13135003AD7BB /* Debug */, + 01E53F9E26C13135003AD7BB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 01E53F9F26C13135003AD7BB /* Build configuration list for PBXNativeTarget "swiftui (macOS)" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 01E53FA026C13135003AD7BB /* Debug */, + 01E53FA126C13135003AD7BB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 01E53FA226C13C20003AD7BB /* XCRemoteSwiftPackageReference "bugsnag-cocoa" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/bugsnag/bugsnag-cocoa.git"; + requirement = { + branch = master; + kind = branch; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 0158BE3A26C16ED3002BD41A /* Bugsnag */ = { + isa = XCSwiftPackageProductDependency; + package = 01E53FA226C13C20003AD7BB /* XCRemoteSwiftPackageReference "bugsnag-cocoa" */; + productName = Bugsnag; + }; + 01E53FA326C13C20003AD7BB /* Bugsnag */ = { + isa = XCSwiftPackageProductDependency; + package = 01E53FA226C13C20003AD7BB /* XCRemoteSwiftPackageReference "bugsnag-cocoa" */; + productName = Bugsnag; + }; + 01E53FA626C13C35003AD7BB /* Bugsnag */ = { + isa = XCSwiftPackageProductDependency; + package = 01E53FA226C13C20003AD7BB /* XCRemoteSwiftPackageReference "bugsnag-cocoa" */; + productName = Bugsnag; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 01E53F7C26C13133003AD7BB /* Project object */; +} diff --git a/examples/swiftui/swiftui.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples/swiftui/swiftui.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/examples/swiftui/swiftui.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/examples/swiftui/swiftui.xcodeproj/xcshareddata/xcschemes/swiftui (iOS).xcscheme b/examples/swiftui/swiftui.xcodeproj/xcshareddata/xcschemes/swiftui (iOS).xcscheme new file mode 100644 index 000000000..8cd155159 --- /dev/null +++ b/examples/swiftui/swiftui.xcodeproj/xcshareddata/xcschemes/swiftui (iOS).xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/swiftui/swiftui.xcodeproj/xcshareddata/xcschemes/swiftui (macOS).xcscheme b/examples/swiftui/swiftui.xcodeproj/xcshareddata/xcschemes/swiftui (macOS).xcscheme new file mode 100644 index 000000000..89b105065 --- /dev/null +++ b/examples/swiftui/swiftui.xcodeproj/xcshareddata/xcschemes/swiftui (macOS).xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/swiftui/swiftui.xcodeproj/xcshareddata/xcschemes/swiftui (tvOS).xcscheme b/examples/swiftui/swiftui.xcodeproj/xcshareddata/xcschemes/swiftui (tvOS).xcscheme new file mode 100644 index 000000000..03a2557a1 --- /dev/null +++ b/examples/swiftui/swiftui.xcodeproj/xcshareddata/xcschemes/swiftui (tvOS).xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/swiftui/tvOS/Info.plist b/examples/swiftui/tvOS/Info.plist new file mode 100644 index 000000000..d04480dfa --- /dev/null +++ b/examples/swiftui/tvOS/Info.plist @@ -0,0 +1,37 @@ + + + + + bugsnag + + apiKey + YOUR-API-KEY + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchScreen + + UIRequiredDeviceCapabilities + + arm64 + + UIUserInterfaceStyle + Automatic + + diff --git a/features/breadcrumbs.feature b/features/breadcrumbs.feature index 1474a6d88..13d01c0fd 100644 --- a/features/breadcrumbs.feature +++ b/features/breadcrumbs.feature @@ -39,3 +39,18 @@ Feature: Attaching a series of notable events leading up to errors When I run "ModifyBreadcrumbInNotify" And I wait to receive an error Then the event has a "manual" breadcrumb named "Cache locked" + + @skip_below_os_version_13 + @skip_macos + Scenario: State breadcrumbs + When I configure Bugsnag for "HandledErrorScenario" + And I background the app for 2 seconds + And I click the element "run_scenario" + And I wait to receive an error + Then the event has a "state" breadcrumb named "Bugsnag loaded" + # Bugsnag has been started too late to capture some early notifications + And the event has a "state" breadcrumb named "App Did Enter Background" + And the event has a "state" breadcrumb named "App Will Enter Foreground" + And the event has a "state" breadcrumb named "Scene Entered Background" + And the event has a "state" breadcrumb named "Scene Will Enter Foreground" + And the event has a "state" breadcrumb named "Scene Activated" diff --git a/features/fixtures/macos/macOSTestApp/MainWindowController.m b/features/fixtures/macos/macOSTestApp/MainWindowController.m index a3aa4eb57..5a04b6239 100644 --- a/features/fixtures/macos/macOSTestApp/MainWindowController.m +++ b/features/fixtures/macos/macOSTestApp/MainWindowController.m @@ -51,6 +51,8 @@ - (BugsnagConfiguration *)configuration { } - (IBAction)runScenario:(id)sender { + NSLog(@"%s %@", __PRETTY_FUNCTION__, self.scenarioName); + if (!self.scenario) { self.scenario = [Scenario createScenarioNamed:self.scenarioName withConfig:[self configuration]]; self.scenario.eventMode = self.scenarioMetadata; @@ -70,6 +72,8 @@ - (IBAction)runScenario:(id)sender { } - (IBAction)startBugsnag:(id)sender { + NSLog(@"%s %@", __PRETTY_FUNCTION__, self.scenarioName); + self.scenario = [Scenario createScenarioNamed:self.scenarioName withConfig:[self configuration]]; self.scenario.eventMode = self.scenarioMetadata; diff --git a/features/fixtures/macos/macOSTestApp/main.m b/features/fixtures/macos/macOSTestApp/main.m index 413f1121e..c1d48142f 100644 --- a/features/fixtures/macos/macOSTestApp/main.m +++ b/features/fixtures/macos/macOSTestApp/main.m @@ -8,6 +8,20 @@ #import +void NotificationCallback(CFNotificationCenterRef center, void *observer, CFNotificationName cfName, const void *object, CFDictionaryRef userInfo) { + NSString *name = (__bridge NSString *)cfName; + // Ignore high-frequency notifications + if (name == NSMenuDidAddItemNotification || + name == NSMenuDidChangeItemNotification || + name == NSViewDidUpdateTrackingAreasNotification || + name == NSViewFrameDidChangeNotification || + [name hasSuffix:@"UpdateNotification"] || + false) { + return; + } + NSLog(@"%@", name); +} + int main(int argc, const char * argv[]) { [[NSUserDefaults standardUserDefaults] registerDefaults:@{ // Disable state restoration to prevent the following dialog being shown after crashes @@ -19,5 +33,10 @@ int main(int argc, const char * argv[]) { @"NSApplicationCrashOnExceptions": @YES, }]; + // Log (almost) all notifications to aid in diagnosing Appium test flakes + CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), + NULL, NotificationCallback, NULL, NULL, + CFNotificationSuspensionBehaviorDeliverImmediately); + return NSApplicationMain(argc, argv); } diff --git a/features/support/env.rb b/features/support/env.rb index 0b3d6a965..8d0ff546e 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -10,6 +10,11 @@ Maze.config.receive_requests_wait = 180 unless ENV['STRESS_TEST'].nil? end +# Maze.config.os is not set when running on BrowserStack so cannot implement @skip_below_ios_13 :-( +Before('@skip_below_os_version_13') do |scenario| + skip_this_scenario("Skipping scenario") if Maze.config.os_version < 13 +end + # Skip stress tests unless STRESS_TEST env var is set Before('@stress_test') do |_scenario| skip_this_scenario('Skipping: Run is not configured for stress tests') if ENV['STRESS_TEST'].nil?