diff --git a/Backtrace.xcodeproj/project.pbxproj b/Backtrace.xcodeproj/project.pbxproj index 5e82353f..2410067c 100644 --- a/Backtrace.xcodeproj/project.pbxproj +++ b/Backtrace.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 0B6B4CFD25CD8331002DA15C /* BacktraceOomWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B6B4CFC25CD8331002DA15C /* BacktraceOomWatcher.swift */; }; 0B6B4CFE25CD8331002DA15C /* BacktraceOomWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B6B4CFC25CD8331002DA15C /* BacktraceOomWatcher.swift */; }; 0B6B4CFF25CD8331002DA15C /* BacktraceOomWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B6B4CFC25CD8331002DA15C /* BacktraceOomWatcher.swift */; }; + 17FBEF33B4981AC198E2A5C5 /* Pods_Backtrace_iOSTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE42C17AAA889061766C6313 /* Pods_Backtrace_iOSTests.framework */; }; 2046B45B2C46FA1100A927DB /* Model.xcdatamodeld in Resources */ = {isa = PBXBuildFile; fileRef = F2AB639A22479A3200939BC9 /* Model.xcdatamodeld */; }; 2046B45C2C46FA5600A927DB /* BacktraceResources.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 2046B4552C46F97800A927DB /* BacktraceResources.bundle */; }; 2046B45F2C46FCE500A927DB /* BacktraceResources.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 2046B4552C46F97800A927DB /* BacktraceResources.bundle */; }; @@ -24,7 +25,12 @@ 2062D9C92C457B4500E4CE3C /* Crash+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2062D9C52C457B4500E4CE3C /* Crash+CoreDataProperties.swift */; }; 2062D9CA2C457B4500E4CE3C /* Crash+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2062D9C42C457B4500E4CE3C /* Crash+CoreDataClass.swift */; }; 2062D9CB2C457B4500E4CE3C /* Crash+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2062D9C52C457B4500E4CE3C /* Crash+CoreDataProperties.swift */; }; - 21F19953A5CF4D23657729C9 /* Pods_Backtrace_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F1B333A7206E7D275DC2FF54 /* Pods_Backtrace_iOS.framework */; }; + 20DE4B342D4830D80076B3F6 /* NSManagedObjectContext+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20DE4B332D4830D00076B3F6 /* NSManagedObjectContext+Extensions.swift */; }; + 20DE4B352D4830D80076B3F6 /* NSManagedObjectContext+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20DE4B332D4830D00076B3F6 /* NSManagedObjectContext+Extensions.swift */; }; + 20DE4B362D4830D80076B3F6 /* NSManagedObjectContext+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20DE4B332D4830D00076B3F6 /* NSManagedObjectContext+Extensions.swift */; }; + 20DE4B382D48616A0076B3F6 /* NSManagedObjectContextExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20DE4B372D48615C0076B3F6 /* NSManagedObjectContextExtensionTests.swift */; }; + 20DE4B392D48616A0076B3F6 /* NSManagedObjectContextExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20DE4B372D48615C0076B3F6 /* NSManagedObjectContextExtensionTests.swift */; }; + 20DE4B3A2D48616A0076B3F6 /* NSManagedObjectContextExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20DE4B372D48615C0076B3F6 /* NSManagedObjectContextExtensionTests.swift */; }; 282C85E7223FD8E70014FE75 /* BacktraceCrashExceptionApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 282C85E6223FD8E70014FE75 /* BacktraceCrashExceptionApplication.swift */; }; 2846E1F8222F1DE60035F98C /* NetworkReachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2846E1F7222F1DE50035F98C /* NetworkReachability.swift */; }; 2846E1F9222F1DE60035F98C /* NetworkReachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2846E1F7222F1DE50035F98C /* NetworkReachability.swift */; }; @@ -81,13 +87,12 @@ 28F95BEC225260C9003936E0 /* AttributesStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28966EF92214BBD200E6E891 /* AttributesStorage.swift */; }; 28F95BED225260D3003936E0 /* AttributesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F259E4E12229C29A00F282C7 /* AttributesProvider.swift */; }; 28F95BEE225260D5003936E0 /* NetworkReachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2846E1F7222F1DE50035F98C /* NetworkReachability.swift */; }; - 3D5BF0B52B204A35FD16AF39 /* Pods_Example_macOS_ObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6E4D0D661C7A82F07B73C418 /* Pods_Example_macOS_ObjC.framework */; }; - 43C8BD37D98A6F622AA8954A /* Pods_Backtrace_macOSTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D25AE028FEDA9037076358C1 /* Pods_Backtrace_macOSTests.framework */; }; + 2D1D06FAACD48C84129B2D59 /* Pods_Example_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36ECEC425F8F3327C37C1F59 /* Pods_Example_tvOS.framework */; }; 4B947DBB2A055CA3000FAB59 /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B947DBA2A055CA3000FAB59 /* Queue.swift */; }; 4B947DBC2A055CA3000FAB59 /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B947DBA2A055CA3000FAB59 /* Queue.swift */; }; 4B947DBE2A055D21000FAB59 /* BreadcrumbRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B947DBD2A055D21000FAB59 /* BreadcrumbRecord.swift */; }; 4B947DBF2A055D21000FAB59 /* BreadcrumbRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B947DBD2A055D21000FAB59 /* BreadcrumbRecord.swift */; }; - 6517B7C031DE5BF51224C26E /* Pods_Backtrace_tvOSTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83A8170741E9FCC308A51E2F /* Pods_Backtrace_tvOSTests.framework */; }; + 4C480376384811737377B65F /* Pods_Backtrace_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C1FE4B8FD1AF4C449ED1133 /* Pods_Backtrace_macOS.framework */; }; 6E45A3A7273095E500DB0BAC /* BacktraceMetricsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E45A3A6273095E500DB0BAC /* BacktraceMetricsSettings.swift */; }; 6E45A3A8273095E500DB0BAC /* BacktraceMetricsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E45A3A6273095E500DB0BAC /* BacktraceMetricsSettings.swift */; }; 6E45A3A9273095E500DB0BAC /* BacktraceMetricsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E45A3A6273095E500DB0BAC /* BacktraceMetricsSettings.swift */; }; @@ -121,9 +126,8 @@ 6EB713F8276294160075D1C1 /* MetricsRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EB713F7276294160075D1C1 /* MetricsRequest.swift */; }; 6EB713F9276294160075D1C1 /* MetricsRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EB713F7276294160075D1C1 /* MetricsRequest.swift */; }; 6EB713FA276294160075D1C1 /* MetricsRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EB713F7276294160075D1C1 /* MetricsRequest.swift */; }; - 7BA7F4CF205D424F66AAD87C /* Pods_Backtrace_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D1E049ECDF46B870245C6AD9 /* Pods_Backtrace_tvOS.framework */; }; - 8F713846B4815059C2D7177B /* Pods_Example_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DA36A04E06625654A943E06 /* Pods_Example_iOS.framework */; }; - 95249D0EB6DDCF0B8F057026 /* Pods_Backtrace_iOSTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8F4811D63972E12CF77D5878 /* Pods_Backtrace_iOSTests.framework */; }; + 8556B5D9705836593FC08552 /* Pods_Backtrace_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F61D37E90FC0BDFF5071866E /* Pods_Backtrace_tvOS.framework */; }; + 862E3B67B0F002247C72B451 /* Pods_Example_iOS_ObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD28556196F4A705A6D4FD35 /* Pods_Example_iOS_ObjC.framework */; }; A24A4B5728B595D9004F5052 /* BacktraceMetricsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A24A4B4828B595D8004F5052 /* BacktraceMetricsTest.swift */; }; A24A4B5828B595D9004F5052 /* BacktraceMetricsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A24A4B4828B595D8004F5052 /* BacktraceMetricsTest.swift */; }; A24A4B5928B595D9004F5052 /* BacktraceMetricsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A24A4B4828B595D8004F5052 /* BacktraceMetricsTest.swift */; }; @@ -179,6 +183,7 @@ A24A4B9328B59653004F5052 /* BacktraceNotificationObserverMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = A24A4B9028B59653004F5052 /* BacktraceNotificationObserverMock.swift */; }; A24A4B9428B59768004F5052 /* BacktraceBreadcrumbsLogManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A652EB285C6C1500306631 /* BacktraceBreadcrumbsLogManager.swift */; }; A24A4B9628B59789004F5052 /* BacktraceBreadcrumbFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28A652E9285C6C1400306631 /* BacktraceBreadcrumbFile.swift */; }; + A68D97A397E6F33DE76E74CC /* Pods_Example_macOS_ObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B55842D20F5714BBCAC1CA70 /* Pods_Example_macOS_ObjC.framework */; }; AF5AB03A26261A4E0003698C /* AttachmentsStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF7833BA2613D1B400530A10 /* AttachmentsStorage.swift */; }; AF5AB04726261A760003698C /* AttachmentBookmarkHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCCCEC126260BC400B83A28 /* AttachmentBookmarkHandler.swift */; }; AF5AB05526261BDD0003698C /* AttachmentBookmarkHandlerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF5AB05426261BDD0003698C /* AttachmentBookmarkHandlerMock.swift */; }; @@ -194,9 +199,9 @@ AFCCCE232625392300B83A28 /* ReportMetadataStorageMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCCCE222625392300B83A28 /* ReportMetadataStorageMock.swift */; }; AFCCCE242625392300B83A28 /* ReportMetadataStorageMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCCCE222625392300B83A28 /* ReportMetadataStorageMock.swift */; }; AFCCCE252625392300B83A28 /* ReportMetadataStorageMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFCCCE222625392300B83A28 /* ReportMetadataStorageMock.swift */; }; - B9CC38739B9EB646E94EF149 /* Pods_Example_iOS_ObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CABE8AF9A71100EE38C4878 /* Pods_Example_iOS_ObjC.framework */; }; - D180991E603E5CA298229FFC /* Pods_Backtrace_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A82F2F4AFD7641081D60CE4 /* Pods_Backtrace_macOS.framework */; }; - D5D98F7CBF9BE57C55C488B2 /* Pods_Example_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B9BED4C6BDC89EDDB340B994 /* Pods_Example_tvOS.framework */; }; + C7C674988265A05271D3415F /* Pods_Backtrace_macOSTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DEE9165B520FB586340D398B /* Pods_Backtrace_macOSTests.framework */; }; + C9426615FF5E45E92A61BE6B /* Pods_Backtrace_tvOSTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E0B5F559B3F9C5070DE2DCCF /* Pods_Backtrace_tvOSTests.framework */; }; + DBE3128686636B65F735FEEF /* Pods_Backtrace_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B2894981C2D4DC714898BBB1 /* Pods_Backtrace_iOS.framework */; }; F21211A5222348AC000B3692 /* BacktraceCrashReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F21211A4222348AC000B3692 /* BacktraceCrashReporter.swift */; }; F21211A6222348AC000B3692 /* BacktraceCrashReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F21211A4222348AC000B3692 /* BacktraceCrashReporter.swift */; }; F21211A8222348C2000B3692 /* SignalContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = F21211A7222348C2000B3692 /* SignalContext.swift */; }; @@ -211,6 +216,7 @@ F21DD3B12255EA2A00404CC3 /* test.txt in Resources */ = {isa = PBXBuildFile; fileRef = F21DD3B02255EA2A00404CC3 /* test.txt */; }; F21DD3B22255EA2A00404CC3 /* test.txt in Resources */ = {isa = PBXBuildFile; fileRef = F21DD3B02255EA2A00404CC3 /* test.txt */; }; F21DD3B32255EA2A00404CC3 /* test.txt in Resources */ = {isa = PBXBuildFile; fileRef = F21DD3B02255EA2A00404CC3 /* test.txt */; }; + F225AE8512E50709F6BDDE3B /* Pods_Example_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2CF35D8E7B65EE09F0E3982C /* Pods_Example_iOS.framework */; }; F229D78C223A591F008EC851 /* UrlSessionMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = F229D789223A56ED008EC851 /* UrlSessionMock.swift */; }; F229D78D223A5920008EC851 /* UrlSessionMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = F229D789223A56ED008EC851 /* UrlSessionMock.swift */; }; F22EB87721BBD36800DEE94E /* BacktraceClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = F22EB87621BBD36800DEE94E /* BacktraceClient.swift */; }; @@ -410,14 +416,18 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 037D6550B4724EB4F05F0774 /* Pods-Example-iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-iOS.release.xcconfig"; path = "Target Support Files/Pods-Example-iOS/Pods-Example-iOS.release.xcconfig"; sourceTree = ""; }; + 04C55AC90DC913E1DB190B57 /* Pods-Example-iOS-ObjC.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-iOS-ObjC.debug.xcconfig"; path = "Target Support Files/Pods-Example-iOS-ObjC/Pods-Example-iOS-ObjC.debug.xcconfig"; sourceTree = ""; }; 0B6B4CFC25CD8331002DA15C /* BacktraceOomWatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BacktraceOomWatcher.swift; sourceTree = ""; }; - 18191424AF4450698FAE32F1 /* Pods-Backtrace-macOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-macOSTests.debug.xcconfig"; path = "Target Support Files/Pods-Backtrace-macOSTests/Pods-Backtrace-macOSTests.debug.xcconfig"; sourceTree = ""; }; + 0E78E925890604A046A416BA /* Pods-Backtrace-macOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-macOS.release.xcconfig"; path = "Target Support Files/Pods-Backtrace-macOS/Pods-Backtrace-macOS.release.xcconfig"; sourceTree = ""; }; 2046B4552C46F97800A927DB /* BacktraceResources.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BacktraceResources.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 2050DB9C2C61A09D00C6CCA9 /* Example-iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Example-iOS.entitlements"; sourceTree = ""; }; 2050DBBE2C66D98500C6CCA9 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 2062D9C42C457B4500E4CE3C /* Crash+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Crash+CoreDataClass.swift"; sourceTree = ""; }; 2062D9C52C457B4500E4CE3C /* Crash+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Crash+CoreDataProperties.swift"; sourceTree = ""; }; 20D4E5F32CB46A41000C92BF /* BacktraceResources-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "BacktraceResources-Info.plist"; sourceTree = ""; }; + 20DE4B332D4830D00076B3F6 /* NSManagedObjectContext+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+Extensions.swift"; sourceTree = ""; }; + 20DE4B372D48615C0076B3F6 /* NSManagedObjectContextExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSManagedObjectContextExtensionTests.swift; sourceTree = ""; }; 282C85E6223FD8E70014FE75 /* BacktraceCrashExceptionApplication.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BacktraceCrashExceptionApplication.swift; sourceTree = ""; }; 2846E1F7222F1DE50035F98C /* NetworkReachability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkReachability.swift; sourceTree = ""; }; 2846E1FD223070CB0035F98C /* Attachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Attachment.swift; sourceTree = ""; }; @@ -435,38 +445,35 @@ 28F95BB822525DCC003936E0 /* Backtrace-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Backtrace-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 28F95BBD22525DCC003936E0 /* Backtrace_tvOSTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Backtrace_tvOSTests.swift; sourceTree = ""; }; 28F95BBF22525DCC003936E0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 2A9A82981A60FB9C85995A27 /* Pods-Backtrace-iOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-iOSTests.debug.xcconfig"; path = "Target Support Files/Pods-Backtrace-iOSTests/Pods-Backtrace-iOSTests.debug.xcconfig"; sourceTree = ""; }; - 2C23DC140069E88D55FC58BA /* Pods-Backtrace-tvOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-tvOSTests.release.xcconfig"; path = "Target Support Files/Pods-Backtrace-tvOSTests/Pods-Backtrace-tvOSTests.release.xcconfig"; sourceTree = ""; }; - 2F917A9C453E6BE70215EF73 /* Pods-Backtrace-tvOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-tvOSTests.debug.xcconfig"; path = "Target Support Files/Pods-Backtrace-tvOSTests/Pods-Backtrace-tvOSTests.debug.xcconfig"; sourceTree = ""; }; - 39033D1F127DF12C49394199 /* Pods-Backtrace-iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-iOS.release.xcconfig"; path = "Target Support Files/Pods-Backtrace-iOS/Pods-Backtrace-iOS.release.xcconfig"; sourceTree = ""; }; - 3CABE8AF9A71100EE38C4878 /* Pods_Example_iOS_ObjC.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example_iOS_ObjC.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 41EA3E9725F179DCE2943E9A /* Pods-Example-iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-iOS.release.xcconfig"; path = "Target Support Files/Pods-Example-iOS/Pods-Example-iOS.release.xcconfig"; sourceTree = ""; }; - 4ABE61E98666C88E7FA37383 /* Pods-Example-macOS-ObjC.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-macOS-ObjC.debug.xcconfig"; path = "Target Support Files/Pods-Example-macOS-ObjC/Pods-Example-macOS-ObjC.debug.xcconfig"; sourceTree = ""; }; + 2CF35D8E7B65EE09F0E3982C /* Pods_Example_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 36ECEC425F8F3327C37C1F59 /* Pods_Example_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 3D6DBD2958FC7C95A508A2CF /* Pods-Example-macOS-ObjC.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-macOS-ObjC.release.xcconfig"; path = "Target Support Files/Pods-Example-macOS-ObjC/Pods-Example-macOS-ObjC.release.xcconfig"; sourceTree = ""; }; + 400175344F10D1AB39E97CBC /* Pods-Backtrace-iOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-iOSTests.debug.xcconfig"; path = "Target Support Files/Pods-Backtrace-iOSTests/Pods-Backtrace-iOSTests.debug.xcconfig"; sourceTree = ""; }; + 4014972346CDB9143F7B57C9 /* Pods-Backtrace-macOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-macOSTests.debug.xcconfig"; path = "Target Support Files/Pods-Backtrace-macOSTests/Pods-Backtrace-macOSTests.debug.xcconfig"; sourceTree = ""; }; 4B947DBA2A055CA3000FAB59 /* Queue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = ""; }; 4B947DBD2A055D21000FAB59 /* BreadcrumbRecord.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BreadcrumbRecord.swift; sourceTree = ""; }; - 5422833473F329A89A9CBB7E /* Pods-Example-tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-tvOS.release.xcconfig"; path = "Target Support Files/Pods-Example-tvOS/Pods-Example-tvOS.release.xcconfig"; sourceTree = ""; }; - 57E502C99174B3461F217EB3 /* Pods-Backtrace-macOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-macOS.release.xcconfig"; path = "Target Support Files/Pods-Backtrace-macOS/Pods-Backtrace-macOS.release.xcconfig"; sourceTree = ""; }; - 5FBA9FF525151F710FD636E7 /* Pods-Example-iOS-ObjC.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-iOS-ObjC.release.xcconfig"; path = "Target Support Files/Pods-Example-iOS-ObjC/Pods-Example-iOS-ObjC.release.xcconfig"; sourceTree = ""; }; - 618416E87BF2C128F4260B9B /* Pods-Backtrace-tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-tvOS.release.xcconfig"; path = "Target Support Files/Pods-Backtrace-tvOS/Pods-Backtrace-tvOS.release.xcconfig"; sourceTree = ""; }; + 4C1FE4B8FD1AF4C449ED1133 /* Pods_Backtrace_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Backtrace_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 557C73F314ED9BC68CD99710 /* Pods-Backtrace-macOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-macOSTests.release.xcconfig"; path = "Target Support Files/Pods-Backtrace-macOSTests/Pods-Backtrace-macOSTests.release.xcconfig"; sourceTree = ""; }; + 559635099C43B2A8C8FB3E2E /* Pods-Example-iOS-ObjC.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-iOS-ObjC.release.xcconfig"; path = "Target Support Files/Pods-Example-iOS-ObjC/Pods-Example-iOS-ObjC.release.xcconfig"; sourceTree = ""; }; + 5B2E658DD4FA5828A7383339 /* Pods-Backtrace-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-tvOS.debug.xcconfig"; path = "Target Support Files/Pods-Backtrace-tvOS/Pods-Backtrace-tvOS.debug.xcconfig"; sourceTree = ""; }; + 5E37DA3F86EDF46AE4A6FD55 /* Pods-Backtrace-iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-iOS.debug.xcconfig"; path = "Target Support Files/Pods-Backtrace-iOS/Pods-Backtrace-iOS.debug.xcconfig"; sourceTree = ""; }; + 638A1CADF7E8A6563C78C31C /* Pods-Example-tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-tvOS.release.xcconfig"; path = "Target Support Files/Pods-Example-tvOS/Pods-Example-tvOS.release.xcconfig"; sourceTree = ""; }; 6E45A3A6273095E500DB0BAC /* BacktraceMetricsSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BacktraceMetricsSettings.swift; sourceTree = ""; }; - 6E4D0D661C7A82F07B73C418 /* Pods_Example_macOS_ObjC.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example_macOS_ObjC.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 6E87F5EA2733174C00B90B07 /* Event.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = ""; }; 6E87F5F2273325A800B90B07 /* UniqueEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UniqueEvent.swift; sourceTree = ""; }; 6E87F5F6273332B400B90B07 /* SummedEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SummedEvent.swift; sourceTree = ""; }; 6E87F5FA27347A6E00B90B07 /* UniqueEventsPayload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UniqueEventsPayload.swift; sourceTree = ""; }; 6E896E8927274A190005CDF2 /* BacktraceMetrics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BacktraceMetrics.swift; sourceTree = ""; }; 6E896E97272767080005CDF2 /* Payload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Payload.swift; sourceTree = ""; }; + 6EA442E045C5A9AADE7207D7 /* Pods-Example-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-tvOS.debug.xcconfig"; path = "Target Support Files/Pods-Example-tvOS/Pods-Example-tvOS.debug.xcconfig"; sourceTree = ""; }; 6EB713EB275ED4EF0075D1C1 /* SummedEventsPayload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SummedEventsPayload.swift; sourceTree = ""; }; 6EB713EF276125760075D1C1 /* BacktraceMetricsSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BacktraceMetricsSender.swift; sourceTree = ""; }; 6EB713F327617ED00075D1C1 /* BacktraceMetricsContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BacktraceMetricsContainer.swift; sourceTree = ""; }; 6EB713F7276294160075D1C1 /* MetricsRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetricsRequest.swift; sourceTree = ""; }; - 703EE750CC36088E64716403 /* Pods-Example-iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-iOS.debug.xcconfig"; path = "Target Support Files/Pods-Example-iOS/Pods-Example-iOS.debug.xcconfig"; sourceTree = ""; }; - 7A82F2F4AFD7641081D60CE4 /* Pods_Backtrace_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Backtrace_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 83A8170741E9FCC308A51E2F /* Pods_Backtrace_tvOSTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Backtrace_tvOSTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 8C3B37D82535F1A4A2BC3F3D /* Pods-Backtrace-macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-macOS.debug.xcconfig"; path = "Target Support Files/Pods-Backtrace-macOS/Pods-Backtrace-macOS.debug.xcconfig"; sourceTree = ""; }; - 8DA36A04E06625654A943E06 /* Pods_Example_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 8F4811D63972E12CF77D5878 /* Pods_Backtrace_iOSTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Backtrace_iOSTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - A21BEAA470CD3B272983C2E0 /* Pods-Backtrace-iOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-iOSTests.release.xcconfig"; path = "Target Support Files/Pods-Backtrace-iOSTests/Pods-Backtrace-iOSTests.release.xcconfig"; sourceTree = ""; }; + 7161F22B650B5C3A47D7C4F7 /* Pods-Backtrace-iOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-iOSTests.release.xcconfig"; path = "Target Support Files/Pods-Backtrace-iOSTests/Pods-Backtrace-iOSTests.release.xcconfig"; sourceTree = ""; }; + 7934AD125B26FB9A7FF5225A /* Pods-Backtrace-tvOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-tvOSTests.release.xcconfig"; path = "Target Support Files/Pods-Backtrace-tvOSTests/Pods-Backtrace-tvOSTests.release.xcconfig"; sourceTree = ""; }; + 79DE4E5AA2488ADC73D72F8D /* Pods-Example-macOS-ObjC.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-macOS-ObjC.debug.xcconfig"; path = "Target Support Files/Pods-Example-macOS-ObjC/Pods-Example-macOS-ObjC.debug.xcconfig"; sourceTree = ""; }; + 8AA7D23E1801210162B0B63C /* Pods-Example-iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-iOS.debug.xcconfig"; path = "Target Support Files/Pods-Example-iOS/Pods-Example-iOS.debug.xcconfig"; sourceTree = ""; }; A24A4B4828B595D8004F5052 /* BacktraceMetricsTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BacktraceMetricsTest.swift; sourceTree = ""; }; A24A4B4928B595D8004F5052 /* BacktraceWatcherTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BacktraceWatcherTests.swift; sourceTree = ""; }; A24A4B4A28B595D8004F5052 /* BacktraceDatabaseTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BacktraceDatabaseTests.swift; sourceTree = ""; }; @@ -486,19 +493,20 @@ A24A4B8828B5960E004F5052 /* BacktraceBreadcrumbs.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BacktraceBreadcrumbs.swift; sourceTree = ""; }; A24A4B8C28B5961A004F5052 /* BacktraceBreadcrumbSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BacktraceBreadcrumbSettings.swift; sourceTree = ""; }; A24A4B9028B59653004F5052 /* BacktraceNotificationObserverMock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BacktraceNotificationObserverMock.swift; sourceTree = ""; }; - A6DDC2E22CECF26B64A6EF18 /* Pods-Example-iOS-ObjC.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-iOS-ObjC.debug.xcconfig"; path = "Target Support Files/Pods-Example-iOS-ObjC/Pods-Example-iOS-ObjC.debug.xcconfig"; sourceTree = ""; }; AF5AB05426261BDD0003698C /* AttachmentBookmarkHandlerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentBookmarkHandlerMock.swift; sourceTree = ""; }; AF7477582620C6B200DEE7D1 /* ReportMetadataStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportMetadataStorage.swift; sourceTree = ""; }; AF7833BA2613D1B400530A10 /* AttachmentsStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentsStorage.swift; sourceTree = ""; }; AFCCCE222625392300B83A28 /* ReportMetadataStorageMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportMetadataStorageMock.swift; sourceTree = ""; }; AFCCCEC126260BC400B83A28 /* AttachmentBookmarkHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentBookmarkHandler.swift; sourceTree = ""; }; - B9BED4C6BDC89EDDB340B994 /* Pods_Example_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - C0FCA45216D537DD02EE4C33 /* Pods-Backtrace-iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-iOS.debug.xcconfig"; path = "Target Support Files/Pods-Backtrace-iOS/Pods-Backtrace-iOS.debug.xcconfig"; sourceTree = ""; }; - C95639FF150AFBB8678560CA /* Pods-Example-macOS-ObjC.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-macOS-ObjC.release.xcconfig"; path = "Target Support Files/Pods-Example-macOS-ObjC/Pods-Example-macOS-ObjC.release.xcconfig"; sourceTree = ""; }; - D1E049ECDF46B870245C6AD9 /* Pods_Backtrace_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Backtrace_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D23F1F3BB48686E3741CB262 /* Pods-Example-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Example-tvOS.debug.xcconfig"; path = "Target Support Files/Pods-Example-tvOS/Pods-Example-tvOS.debug.xcconfig"; sourceTree = ""; }; - D25AE028FEDA9037076358C1 /* Pods_Backtrace_macOSTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Backtrace_macOSTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - F1B333A7206E7D275DC2FF54 /* Pods_Backtrace_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Backtrace_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B2894981C2D4DC714898BBB1 /* Pods_Backtrace_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Backtrace_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B55842D20F5714BBCAC1CA70 /* Pods_Example_macOS_ObjC.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example_macOS_ObjC.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C8E3A63330AA85977C3A255E /* Pods-Backtrace-tvOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-tvOSTests.debug.xcconfig"; path = "Target Support Files/Pods-Backtrace-tvOSTests/Pods-Backtrace-tvOSTests.debug.xcconfig"; sourceTree = ""; }; + CD28556196F4A705A6D4FD35 /* Pods_Example_iOS_ObjC.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Example_iOS_ObjC.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + CF0E6D0F08AAE9CB60192052 /* Pods-Backtrace-iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-iOS.release.xcconfig"; path = "Target Support Files/Pods-Backtrace-iOS/Pods-Backtrace-iOS.release.xcconfig"; sourceTree = ""; }; + DEE9165B520FB586340D398B /* Pods_Backtrace_macOSTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Backtrace_macOSTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + E0B5F559B3F9C5070DE2DCCF /* Pods_Backtrace_tvOSTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Backtrace_tvOSTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + EA784170CE2484D66F12845A /* Pods-Backtrace-tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-tvOS.release.xcconfig"; path = "Target Support Files/Pods-Backtrace-tvOS/Pods-Backtrace-tvOS.release.xcconfig"; sourceTree = ""; }; + EE42C17AAA889061766C6313 /* Pods_Backtrace_iOSTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Backtrace_iOSTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; F21211A4222348AC000B3692 /* BacktraceCrashReporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BacktraceCrashReporter.swift; sourceTree = ""; }; F21211A7222348C2000B3692 /* SignalContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignalContext.swift; sourceTree = ""; }; F21D302A224A18D50013B5D7 /* Store.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Store.swift; sourceTree = ""; }; @@ -589,8 +597,8 @@ F2D8BE4F21BDA7D0007CFEFA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; F2D8BE5021BDA7D0007CFEFA /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; F2D8BE5221BDA7D0007CFEFA /* Example_macOS_ObjC.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Example_macOS_ObjC.entitlements; sourceTree = ""; }; - F35BAA1E4C89708143878FAB /* Pods-Backtrace-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-tvOS.debug.xcconfig"; path = "Target Support Files/Pods-Backtrace-tvOS/Pods-Backtrace-tvOS.debug.xcconfig"; sourceTree = ""; }; - F4A16A64F245886C15691E86 /* Pods-Backtrace-macOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-macOSTests.release.xcconfig"; path = "Target Support Files/Pods-Backtrace-macOSTests/Pods-Backtrace-macOSTests.release.xcconfig"; sourceTree = ""; }; + F61D37E90FC0BDFF5071866E /* Pods_Backtrace_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Backtrace_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F972AA30EC889ADCC25E1C1F /* Pods-Backtrace-macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-macOS.debug.xcconfig"; path = "Target Support Files/Pods-Backtrace-macOS/Pods-Backtrace-macOS.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -605,7 +613,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 7BA7F4CF205D424F66AAD87C /* Pods_Backtrace_tvOS.framework in Frameworks */, + 8556B5D9705836593FC08552 /* Pods_Backtrace_tvOS.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -613,7 +621,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 6517B7C031DE5BF51224C26E /* Pods_Backtrace_tvOSTests.framework in Frameworks */, + C9426615FF5E45E92A61BE6B /* Pods_Backtrace_tvOSTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -621,7 +629,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D180991E603E5CA298229FFC /* Pods_Backtrace_macOS.framework in Frameworks */, + 4C480376384811737377B65F /* Pods_Backtrace_macOS.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -629,7 +637,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 43C8BD37D98A6F622AA8954A /* Pods_Backtrace_macOSTests.framework in Frameworks */, + C7C674988265A05271D3415F /* Pods_Backtrace_macOSTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -637,7 +645,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D5D98F7CBF9BE57C55C488B2 /* Pods_Example_tvOS.framework in Frameworks */, + 2D1D06FAACD48C84129B2D59 /* Pods_Example_tvOS.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -645,7 +653,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 21F19953A5CF4D23657729C9 /* Pods_Backtrace_iOS.framework in Frameworks */, + DBE3128686636B65F735FEEF /* Pods_Backtrace_iOS.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -653,7 +661,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 95249D0EB6DDCF0B8F057026 /* Pods_Backtrace_iOSTests.framework in Frameworks */, + 17FBEF33B4981AC198E2A5C5 /* Pods_Backtrace_iOSTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -661,7 +669,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 8F713846B4815059C2D7177B /* Pods_Example_iOS.framework in Frameworks */, + F225AE8512E50709F6BDDE3B /* Pods_Example_iOS.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -669,7 +677,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - B9CC38739B9EB646E94EF149 /* Pods_Example_iOS_ObjC.framework in Frameworks */, + 862E3B67B0F002247C72B451 /* Pods_Example_iOS_ObjC.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -677,7 +685,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 3D5BF0B52B204A35FD16AF39 /* Pods_Example_macOS_ObjC.framework in Frameworks */, + A68D97A397E6F33DE76E74CC /* Pods_Example_macOS_ObjC.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -770,55 +778,55 @@ path = Model; sourceTree = ""; }; - 6EE81FE4CCC45BD2458DD6F8 /* Frameworks */ = { + AF7833B92613D0E300530A10 /* Model */ = { isa = PBXGroup; children = ( - F1B333A7206E7D275DC2FF54 /* Pods_Backtrace_iOS.framework */, - 8F4811D63972E12CF77D5878 /* Pods_Backtrace_iOSTests.framework */, - 7A82F2F4AFD7641081D60CE4 /* Pods_Backtrace_macOS.framework */, - D25AE028FEDA9037076358C1 /* Pods_Backtrace_macOSTests.framework */, - D1E049ECDF46B870245C6AD9 /* Pods_Backtrace_tvOS.framework */, - 83A8170741E9FCC308A51E2F /* Pods_Backtrace_tvOSTests.framework */, - 8DA36A04E06625654A943E06 /* Pods_Example_iOS.framework */, - 3CABE8AF9A71100EE38C4878 /* Pods_Example_iOS_ObjC.framework */, - 6E4D0D661C7A82F07B73C418 /* Pods_Example_macOS_ObjC.framework */, - B9BED4C6BDC89EDDB340B994 /* Pods_Example_tvOS.framework */, + AF7833BA2613D1B400530A10 /* AttachmentsStorage.swift */, + AFCCCEC126260BC400B83A28 /* AttachmentBookmarkHandler.swift */, ); - name = Frameworks; + path = Model; sourceTree = ""; }; - AF7833B92613D0E300530A10 /* Model */ = { + C9AA6D00C0F2E6250F2BF95D /* Frameworks */ = { isa = PBXGroup; children = ( - AF7833BA2613D1B400530A10 /* AttachmentsStorage.swift */, - AFCCCEC126260BC400B83A28 /* AttachmentBookmarkHandler.swift */, + B2894981C2D4DC714898BBB1 /* Pods_Backtrace_iOS.framework */, + EE42C17AAA889061766C6313 /* Pods_Backtrace_iOSTests.framework */, + 4C1FE4B8FD1AF4C449ED1133 /* Pods_Backtrace_macOS.framework */, + DEE9165B520FB586340D398B /* Pods_Backtrace_macOSTests.framework */, + F61D37E90FC0BDFF5071866E /* Pods_Backtrace_tvOS.framework */, + E0B5F559B3F9C5070DE2DCCF /* Pods_Backtrace_tvOSTests.framework */, + 2CF35D8E7B65EE09F0E3982C /* Pods_Example_iOS.framework */, + CD28556196F4A705A6D4FD35 /* Pods_Example_iOS_ObjC.framework */, + B55842D20F5714BBCAC1CA70 /* Pods_Example_macOS_ObjC.framework */, + 36ECEC425F8F3327C37C1F59 /* Pods_Example_tvOS.framework */, ); - path = Model; + name = Frameworks; sourceTree = ""; }; E1CB76ADFD3A1D9326B4E46D /* Pods */ = { isa = PBXGroup; children = ( - C0FCA45216D537DD02EE4C33 /* Pods-Backtrace-iOS.debug.xcconfig */, - 39033D1F127DF12C49394199 /* Pods-Backtrace-iOS.release.xcconfig */, - 2A9A82981A60FB9C85995A27 /* Pods-Backtrace-iOSTests.debug.xcconfig */, - A21BEAA470CD3B272983C2E0 /* Pods-Backtrace-iOSTests.release.xcconfig */, - 8C3B37D82535F1A4A2BC3F3D /* Pods-Backtrace-macOS.debug.xcconfig */, - 57E502C99174B3461F217EB3 /* Pods-Backtrace-macOS.release.xcconfig */, - 18191424AF4450698FAE32F1 /* Pods-Backtrace-macOSTests.debug.xcconfig */, - F4A16A64F245886C15691E86 /* Pods-Backtrace-macOSTests.release.xcconfig */, - F35BAA1E4C89708143878FAB /* Pods-Backtrace-tvOS.debug.xcconfig */, - 618416E87BF2C128F4260B9B /* Pods-Backtrace-tvOS.release.xcconfig */, - 2F917A9C453E6BE70215EF73 /* Pods-Backtrace-tvOSTests.debug.xcconfig */, - 2C23DC140069E88D55FC58BA /* Pods-Backtrace-tvOSTests.release.xcconfig */, - 703EE750CC36088E64716403 /* Pods-Example-iOS.debug.xcconfig */, - 41EA3E9725F179DCE2943E9A /* Pods-Example-iOS.release.xcconfig */, - A6DDC2E22CECF26B64A6EF18 /* Pods-Example-iOS-ObjC.debug.xcconfig */, - 5FBA9FF525151F710FD636E7 /* Pods-Example-iOS-ObjC.release.xcconfig */, - 4ABE61E98666C88E7FA37383 /* Pods-Example-macOS-ObjC.debug.xcconfig */, - C95639FF150AFBB8678560CA /* Pods-Example-macOS-ObjC.release.xcconfig */, - D23F1F3BB48686E3741CB262 /* Pods-Example-tvOS.debug.xcconfig */, - 5422833473F329A89A9CBB7E /* Pods-Example-tvOS.release.xcconfig */, + 5E37DA3F86EDF46AE4A6FD55 /* Pods-Backtrace-iOS.debug.xcconfig */, + CF0E6D0F08AAE9CB60192052 /* Pods-Backtrace-iOS.release.xcconfig */, + 400175344F10D1AB39E97CBC /* Pods-Backtrace-iOSTests.debug.xcconfig */, + 7161F22B650B5C3A47D7C4F7 /* Pods-Backtrace-iOSTests.release.xcconfig */, + F972AA30EC889ADCC25E1C1F /* Pods-Backtrace-macOS.debug.xcconfig */, + 0E78E925890604A046A416BA /* Pods-Backtrace-macOS.release.xcconfig */, + 4014972346CDB9143F7B57C9 /* Pods-Backtrace-macOSTests.debug.xcconfig */, + 557C73F314ED9BC68CD99710 /* Pods-Backtrace-macOSTests.release.xcconfig */, + 5B2E658DD4FA5828A7383339 /* Pods-Backtrace-tvOS.debug.xcconfig */, + EA784170CE2484D66F12845A /* Pods-Backtrace-tvOS.release.xcconfig */, + C8E3A63330AA85977C3A255E /* Pods-Backtrace-tvOSTests.debug.xcconfig */, + 7934AD125B26FB9A7FF5225A /* Pods-Backtrace-tvOSTests.release.xcconfig */, + 8AA7D23E1801210162B0B63C /* Pods-Example-iOS.debug.xcconfig */, + 037D6550B4724EB4F05F0774 /* Pods-Example-iOS.release.xcconfig */, + 04C55AC90DC913E1DB190B57 /* Pods-Example-iOS-ObjC.debug.xcconfig */, + 559635099C43B2A8C8FB3E2E /* Pods-Example-iOS-ObjC.release.xcconfig */, + 79DE4E5AA2488ADC73D72F8D /* Pods-Example-macOS-ObjC.debug.xcconfig */, + 3D6DBD2958FC7C95A508A2CF /* Pods-Example-macOS-ObjC.release.xcconfig */, + 6EA442E045C5A9AADE7207D7 /* Pods-Example-tvOS.debug.xcconfig */, + 638A1CADF7E8A6563C78C31C /* Pods-Example-tvOS.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -876,6 +884,7 @@ F266B85321C77D5D00D14417 /* Tests */ = { isa = PBXGroup; children = ( + 20DE4B372D48615C0076B3F6 /* NSManagedObjectContextExtensionTests.swift */, A24A4B5428B595D8004F5052 /* AttachmentStorageTests.swift */, A24A4B4F28B595D8004F5052 /* AttachmentTests.swift */, A24A4B5328B595D8004F5052 /* AttributesTests.swift */, @@ -951,6 +960,7 @@ F28F165621E2A0BD008E4B96 /* Extensions */ = { isa = PBXGroup; children = ( + 20DE4B332D4830D00076B3F6 /* NSManagedObjectContext+Extensions.swift */, F28F165721E2A0DA008E4B96 /* URLSession+Sync.swift */, F2AFB5902225E5D000AAA1D7 /* Foundation+Extensions.swift */, 28A65305285D1BF700306631 /* Date+Extensions.swift */, @@ -1079,7 +1089,7 @@ 28F95BBC22525DCC003936E0 /* Backtrace-tvOSTests */, F2C2FA5121BBD26300934744 /* Products */, E1CB76ADFD3A1D9326B4E46D /* Pods */, - 6EE81FE4CCC45BD2458DD6F8 /* Frameworks */, + C9AA6D00C0F2E6250F2BF95D /* Frameworks */, ); sourceTree = ""; }; @@ -1234,7 +1244,7 @@ isa = PBXNativeTarget; buildConfigurationList = 28F95BC122525DCC003936E0 /* Build configuration list for PBXNativeTarget "Backtrace-tvOS" */; buildPhases = ( - B9BA8CCC511D342BC8FC1F2C /* [CP] Check Pods Manifest.lock */, + 1AC2F744E60E2BDAA7282B3A /* [CP] Check Pods Manifest.lock */, 28F95BAB22525DCC003936E0 /* Headers */, 28F95BAC22525DCC003936E0 /* Sources */, 28F95BAD22525DCC003936E0 /* Frameworks */, @@ -1255,11 +1265,11 @@ isa = PBXNativeTarget; buildConfigurationList = 28F95BC422525DCC003936E0 /* Build configuration list for PBXNativeTarget "Backtrace-tvOSTests" */; buildPhases = ( - 2E898F26FB45CC019E2FDBC1 /* [CP] Check Pods Manifest.lock */, + AC030A28CDD3537FE77F8ABC /* [CP] Check Pods Manifest.lock */, 28F95BB422525DCC003936E0 /* Sources */, 28F95BB522525DCC003936E0 /* Frameworks */, 28F95BB622525DCC003936E0 /* Resources */, - 39E931229FB5A7D80B8592A9 /* [CP] Embed Pods Frameworks */, + E75364CB7AFE3620C3044A9E /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -1275,7 +1285,7 @@ isa = PBXNativeTarget; buildConfigurationList = F266B82321C77AC800D14417 /* Build configuration list for PBXNativeTarget "Backtrace-macOS" */; buildPhases = ( - C903EF9F1D3D6C51684C5D56 /* [CP] Check Pods Manifest.lock */, + C9FFDFBE9B1DE5C7EABAAAE6 /* [CP] Check Pods Manifest.lock */, F266B80D21C77AC800D14417 /* Headers */, F266B80E21C77AC800D14417 /* Sources */, F266B80F21C77AC800D14417 /* Frameworks */, @@ -1296,11 +1306,11 @@ isa = PBXNativeTarget; buildConfigurationList = F266B82621C77AC800D14417 /* Build configuration list for PBXNativeTarget "Backtrace-macOSTests" */; buildPhases = ( - 52E5C998426A9256FA290724 /* [CP] Check Pods Manifest.lock */, + 377781FC90BC6EA5E862C2BB /* [CP] Check Pods Manifest.lock */, F266B81621C77AC800D14417 /* Sources */, F266B81721C77AC800D14417 /* Frameworks */, F266B81821C77AC800D14417 /* Resources */, - 8C4387D9E334BF08C0AFF2B5 /* [CP] Embed Pods Frameworks */, + C4700F99B4BDF71975FF61B7 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -1316,12 +1326,12 @@ isa = PBXNativeTarget; buildConfigurationList = F2A11C0522553C2A00354640 /* Build configuration list for PBXNativeTarget "Example-tvOS" */; buildPhases = ( - 0F520389BFAA01B92EA98FCC /* [CP] Check Pods Manifest.lock */, + 8EFE07C5A17F22CAA406A797 /* [CP] Check Pods Manifest.lock */, F2A11BF322553C2800354640 /* Sources */, F2A11BF422553C2800354640 /* Frameworks */, F2A11BF522553C2800354640 /* Resources */, 28C74A2F226FBD7700CE713A /* Embed Frameworks */, - 1DF02D0672919D682B0633D3 /* [CP] Copy Pods Resources */, + 6914068597A54B8F1876941E /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -1336,7 +1346,7 @@ isa = PBXNativeTarget; buildConfigurationList = F2C2FA6221BBD26300934744 /* Build configuration list for PBXNativeTarget "Backtrace-iOS" */; buildPhases = ( - 53A4B4AC7CD3757E3F060DD1 /* [CP] Check Pods Manifest.lock */, + 7CE68EC82F38626CAD5A83D5 /* [CP] Check Pods Manifest.lock */, F2C2FA4B21BBD26300934744 /* Headers */, F2C2FA4C21BBD26300934744 /* Sources */, F2C2FA4D21BBD26300934744 /* Frameworks */, @@ -1357,11 +1367,11 @@ isa = PBXNativeTarget; buildConfigurationList = F2C2FA6521BBD26300934744 /* Build configuration list for PBXNativeTarget "Backtrace-iOSTests" */; buildPhases = ( - B494F0B1E1EA9E072B33A33D /* [CP] Check Pods Manifest.lock */, + 050777E81C0FB8EF48CC70CA /* [CP] Check Pods Manifest.lock */, F2C2FA5521BBD26300934744 /* Sources */, F2C2FA5621BBD26300934744 /* Frameworks */, F2C2FA5721BBD26300934744 /* Resources */, - 15001CA7B44739504A04CFFE /* [CP] Embed Pods Frameworks */, + EFABFCA43C47D5179DF3ED74 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -1377,12 +1387,12 @@ isa = PBXNativeTarget; buildConfigurationList = F2D8BE1321BC065F007CFEFA /* Build configuration list for PBXNativeTarget "Example-iOS" */; buildPhases = ( - 39BC40B0242D94CF19E85843 /* [CP] Check Pods Manifest.lock */, + 9CCC130157A6E85E31DC6662 /* [CP] Check Pods Manifest.lock */, F2D8BE0021BC065E007CFEFA /* Sources */, F2D8BE0121BC065E007CFEFA /* Frameworks */, F2D8BE0221BC065E007CFEFA /* Resources */, F2D7122821F11303002D2A26 /* Embed Frameworks */, - 8773E30DB965264750FD758A /* [CP] Copy Pods Resources */, + 9F14FD19D9E32B9F6126D063 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -1397,12 +1407,12 @@ isa = PBXNativeTarget; buildConfigurationList = F2D8BE3221BC5F98007CFEFA /* Build configuration list for PBXNativeTarget "Example-iOS-ObjC" */; buildPhases = ( - A5E309352F8F8F3971B23809 /* [CP] Check Pods Manifest.lock */, + B3A6D9E98B158C3D26810658 /* [CP] Check Pods Manifest.lock */, F2D8BE1B21BC5F97007CFEFA /* Sources */, F2D8BE1C21BC5F97007CFEFA /* Frameworks */, F2D8BE1D21BC5F97007CFEFA /* Resources */, F2D7122B21F115CD002D2A26 /* Embed Frameworks */, - 8A754EABCEF9B7C136630099 /* [CP] Copy Pods Resources */, + 5880228CA13D968E338D21C4 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -1417,12 +1427,12 @@ isa = PBXNativeTarget; buildConfigurationList = F2D8BE5321BDA7D0007CFEFA /* Build configuration list for PBXNativeTarget "Example-macOS-ObjC" */; buildPhases = ( - 84EFBCED9B48EED9A339C13A /* [CP] Check Pods Manifest.lock */, + 329A0ACE1239193B26C9575A /* [CP] Check Pods Manifest.lock */, F2D8BE3E21BDA7CF007CFEFA /* Sources */, F2D8BE3F21BDA7CF007CFEFA /* Frameworks */, F2D8BE4021BDA7CF007CFEFA /* Resources */, F289085621C532D9002B813E /* Embed Frameworks */, - 5311D8B08A71CA27C1C0860A /* [CP] Copy Pods Resources */, + E06E9BE8F447B2B652FCF47E /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -1614,7 +1624,7 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 0F520389BFAA01B92EA98FCC /* [CP] Check Pods Manifest.lock */ = { + 050777E81C0FB8EF48CC70CA /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1629,48 +1639,36 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Example-tvOS-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-Backtrace-iOSTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 15001CA7B44739504A04CFFE /* [CP] Embed Pods Frameworks */ = { + 1AC2F744E60E2BDAA7282B3A /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Backtrace-iOSTests/Pods-Backtrace-iOSTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Backtrace-iOSTests/Pods-Backtrace-iOSTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Backtrace-iOSTests/Pods-Backtrace-iOSTests-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 1DF02D0672919D682B0633D3 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Example-tvOS/Pods-Example-tvOS-resources-${CONFIGURATION}-input-files.xcfilelist", + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", ); - name = "[CP] Copy Pods Resources"; + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Example-tvOS/Pods-Example-tvOS-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Backtrace-tvOS-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Example-tvOS/Pods-Example-tvOS-resources.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 2E898F26FB45CC019E2FDBC1 /* [CP] Check Pods Manifest.lock */ = { + 329A0ACE1239193B26C9575A /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1685,14 +1683,14 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Backtrace-tvOSTests-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-Example-macOS-ObjC-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 39BC40B0242D94CF19E85843 /* [CP] Check Pods Manifest.lock */ = { + 377781FC90BC6EA5E862C2BB /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1707,70 +1705,70 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Example-iOS-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-Backtrace-macOSTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 39E931229FB5A7D80B8592A9 /* [CP] Embed Pods Frameworks */ = { + 5880228CA13D968E338D21C4 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Backtrace-tvOSTests/Pods-Backtrace-tvOSTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-Example-iOS-ObjC/Pods-Example-iOS-ObjC-resources-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Embed Pods Frameworks"; + name = "[CP] Copy Pods Resources"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Backtrace-tvOSTests/Pods-Backtrace-tvOSTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-Example-iOS-ObjC/Pods-Example-iOS-ObjC-resources-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Backtrace-tvOSTests/Pods-Backtrace-tvOSTests-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Example-iOS-ObjC/Pods-Example-iOS-ObjC-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 52E5C998426A9256FA290724 /* [CP] Check Pods Manifest.lock */ = { + 6914068597A54B8F1876941E /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Example-tvOS/Pods-Example-tvOS-resources-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Copy Pods Resources"; outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Backtrace-macOSTests-checkManifestLockResult.txt", + "${PODS_ROOT}/Target Support Files/Pods-Example-tvOS/Pods-Example-tvOS-resources-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Example-tvOS/Pods-Example-tvOS-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 5311D8B08A71CA27C1C0860A /* [CP] Copy Pods Resources */ = { + 7CE68EC82F38626CAD5A83D5 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Example-macOS-ObjC/Pods-Example-macOS-ObjC-resources-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Copy Pods Resources"; + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Example-macOS-ObjC/Pods-Example-macOS-ObjC-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Backtrace-iOS-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Example-macOS-ObjC/Pods-Example-macOS-ObjC-resources.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 53A4B4AC7CD3757E3F060DD1 /* [CP] Check Pods Manifest.lock */ = { + 8EFE07C5A17F22CAA406A797 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1785,14 +1783,14 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Backtrace-iOS-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-Example-tvOS-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 84EFBCED9B48EED9A339C13A /* [CP] Check Pods Manifest.lock */ = { + 9CCC130157A6E85E31DC6662 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1807,14 +1805,14 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Example-macOS-ObjC-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-Example-iOS-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 8773E30DB965264750FD758A /* [CP] Copy Pods Resources */ = { + 9F14FD19D9E32B9F6126D063 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1831,24 +1829,51 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Example-iOS/Pods-Example-iOS-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 8A754EABCEF9B7C136630099 /* [CP] Copy Pods Resources */ = { + AC030A28CDD3537FE77F8ABC /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Example-iOS-ObjC/Pods-Example-iOS-ObjC-resources-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Copy Pods Resources"; + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Example-iOS-ObjC/Pods-Example-iOS-ObjC-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Backtrace-tvOSTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Example-iOS-ObjC/Pods-Example-iOS-ObjC-resources.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 8C4387D9E334BF08C0AFF2B5 /* [CP] Embed Pods Frameworks */ = { + B3A6D9E98B158C3D26810658 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Example-iOS-ObjC-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + C4700F99B4BDF71975FF61B7 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1865,7 +1890,7 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Backtrace-macOSTests/Pods-Backtrace-macOSTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - A5E309352F8F8F3971B23809 /* [CP] Check Pods Manifest.lock */ = { + C9FFDFBE9B1DE5C7EABAAAE6 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -1880,77 +1905,62 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Example-iOS-ObjC-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-Backtrace-macOS-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - B494F0B1E1EA9E072B33A33D /* [CP] Check Pods Manifest.lock */ = { + E06E9BE8F447B2B652FCF47E /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Example-macOS-ObjC/Pods-Example-macOS-ObjC-resources-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Copy Pods Resources"; outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Backtrace-iOSTests-checkManifestLockResult.txt", + "${PODS_ROOT}/Target Support Files/Pods-Example-macOS-ObjC/Pods-Example-macOS-ObjC-resources-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Example-macOS-ObjC/Pods-Example-macOS-ObjC-resources.sh\"\n"; showEnvVarsInLog = 0; }; - B9BA8CCC511D342BC8FC1F2C /* [CP] Check Pods Manifest.lock */ = { + E75364CB7AFE3620C3044A9E /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Backtrace-tvOSTests/Pods-Backtrace-tvOSTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Backtrace-tvOS-checkManifestLockResult.txt", + "${PODS_ROOT}/Target Support Files/Pods-Backtrace-tvOSTests/Pods-Backtrace-tvOSTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Backtrace-tvOSTests/Pods-Backtrace-tvOSTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - C903EF9F1D3D6C51684C5D56 /* [CP] Check Pods Manifest.lock */ = { + EFABFCA43C47D5179DF3ED74 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Backtrace-iOSTests/Pods-Backtrace-iOSTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Backtrace-macOS-checkManifestLockResult.txt", + "${PODS_ROOT}/Target Support Files/Pods-Backtrace-iOSTests/Pods-Backtrace-iOSTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Backtrace-iOSTests/Pods-Backtrace-iOSTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; F2F0628C22B0453C00BCA6D0 /* Lint */ = { @@ -2027,6 +2037,7 @@ 28F95BCF22526061003936E0 /* BacktraceLogger.swift in Sources */, AF5AB0BB262622730003698C /* AttachmentBookmarkHandler.swift in Sources */, 28F95BD022526064003936E0 /* BacktraceClient.swift in Sources */, + 20DE4B352D4830D80076B3F6 /* NSManagedObjectContext+Extensions.swift in Sources */, 28F95BCD2252605A003936E0 /* BacktraceClientDelegate.swift in Sources */, 28F95BC92252602C003936E0 /* Foundation+Extensions.swift in Sources */, 28F95BD622526078003936E0 /* DebuggerChecker.swift in Sources */, @@ -2087,6 +2098,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 20DE4B382D48616A0076B3F6 /* NSManagedObjectContextExtensionTests.swift in Sources */, A24A4B7D28B595D9004F5052 /* AttachmentStorageTests.swift in Sources */, F21DD39D2255666F00404CC3 /* Quick+Throws.swift in Sources */, A24A4B7428B595D9004F5052 /* BacktraceApiTests.swift in Sources */, @@ -2178,6 +2190,7 @@ 6EB713ED275ED4EF0075D1C1 /* SummedEventsPayload.swift in Sources */, F29CD79221FCC25600216C59 /* BacktraceWatcher.swift in Sources */, 282C85E7223FD8E70014FE75 /* BacktraceCrashExceptionApplication.swift in Sources */, + 20DE4B342D4830D80076B3F6 /* NSManagedObjectContext+Extensions.swift in Sources */, 6E87F5F8273332B400B90B07 /* SummedEvent.swift in Sources */, F2AFB59B22274E5400AAA1D7 /* BacktraceClientCustomizing.swift in Sources */, F28F165921E2A0DA008E4B96 /* URLSession+Sync.swift in Sources */, @@ -2205,6 +2218,7 @@ F2AB63762246484100939BC9 /* WatcherRepositoryMock.swift in Sources */, A24A4B6428B595D9004F5052 /* BacktraceReporterTests.swift in Sources */, A24A4B5828B595D9004F5052 /* BacktraceMetricsTest.swift in Sources */, + 20DE4B3A2D48616A0076B3F6 /* NSManagedObjectContextExtensionTests.swift in Sources */, A24A4B6128B595D9004F5052 /* BacktraceFileManagerTests.swift in Sources */, F229D78C223A591F008EC851 /* UrlSessionMock.swift in Sources */, A24A4B7028B595D9004F5052 /* BacktraceCredentialsTests.swift in Sources */, @@ -2241,6 +2255,7 @@ F2AFB59D22274EDA00AAA1D7 /* Dispatching.swift in Sources */, 28A65306285D1BF700306631 /* Date+Extensions.swift in Sources */, 4B947DBE2A055D21000FAB59 /* BreadcrumbRecord.swift in Sources */, + 20DE4B362D4830D80076B3F6 /* NSManagedObjectContext+Extensions.swift in Sources */, 2846E1F8222F1DE60035F98C /* NetworkReachability.swift in Sources */, 6E45A3A7273095E500DB0BAC /* BacktraceMetricsSettings.swift in Sources */, F21211A8222348C2000B3692 /* SignalContext.swift in Sources */, @@ -2318,6 +2333,7 @@ F2AB63752246484100939BC9 /* WatcherRepositoryMock.swift in Sources */, A24A4B6328B595D9004F5052 /* BacktraceReporterTests.swift in Sources */, A24A4B5728B595D9004F5052 /* BacktraceMetricsTest.swift in Sources */, + 20DE4B392D48616A0076B3F6 /* NSManagedObjectContextExtensionTests.swift in Sources */, A24A4B6028B595D9004F5052 /* BacktraceFileManagerTests.swift in Sources */, F229D78D223A5920008EC851 /* UrlSessionMock.swift in Sources */, A24A4B6F28B595D9004F5052 /* BacktraceCredentialsTests.swift in Sources */, @@ -2628,7 +2644,7 @@ }; 28F95BC222525DCC003936E0 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F35BAA1E4C89708143878FAB /* Pods-Backtrace-tvOS.debug.xcconfig */; + baseConfigurationReference = 5B2E658DD4FA5828A7383339 /* Pods-Backtrace-tvOS.debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -2713,7 +2729,7 @@ }; 28F95BC322525DCC003936E0 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 618416E87BF2C128F4260B9B /* Pods-Backtrace-tvOS.release.xcconfig */; + baseConfigurationReference = EA784170CE2484D66F12845A /* Pods-Backtrace-tvOS.release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -2792,7 +2808,7 @@ }; 28F95BC522525DCC003936E0 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 2F917A9C453E6BE70215EF73 /* Pods-Backtrace-tvOSTests.debug.xcconfig */; + baseConfigurationReference = C8E3A63330AA85977C3A255E /* Pods-Backtrace-tvOSTests.debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -2866,7 +2882,7 @@ }; 28F95BC622525DCC003936E0 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 2C23DC140069E88D55FC58BA /* Pods-Backtrace-tvOSTests.release.xcconfig */; + baseConfigurationReference = 7934AD125B26FB9A7FF5225A /* Pods-Backtrace-tvOSTests.release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -2934,7 +2950,7 @@ }; F266B82421C77AC800D14417 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 8C3B37D82535F1A4A2BC3F3D /* Pods-Backtrace-macOS.debug.xcconfig */; + baseConfigurationReference = F972AA30EC889ADCC25E1C1F /* Pods-Backtrace-macOS.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_SEARCH_USER_PATHS = NO; @@ -3023,7 +3039,7 @@ }; F266B82521C77AC800D14417 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 57E502C99174B3461F217EB3 /* Pods-Backtrace-macOS.release.xcconfig */; + baseConfigurationReference = 0E78E925890604A046A416BA /* Pods-Backtrace-macOS.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_SEARCH_USER_PATHS = NO; @@ -3105,7 +3121,7 @@ }; F266B82721C77AC800D14417 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 18191424AF4450698FAE32F1 /* Pods-Backtrace-macOSTests.debug.xcconfig */; + baseConfigurationReference = 4014972346CDB9143F7B57C9 /* Pods-Backtrace-macOSTests.debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -3184,7 +3200,7 @@ }; F266B82821C77AC800D14417 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F4A16A64F245886C15691E86 /* Pods-Backtrace-macOSTests.release.xcconfig */; + baseConfigurationReference = 557C73F314ED9BC68CD99710 /* Pods-Backtrace-macOSTests.release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -3256,7 +3272,7 @@ }; F2A11C0322553C2A00354640 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D23F1F3BB48686E3741CB262 /* Pods-Example-tvOS.debug.xcconfig */; + baseConfigurationReference = 6EA442E045C5A9AADE7207D7 /* Pods-Example-tvOS.debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; @@ -3336,7 +3352,7 @@ }; F2A11C0422553C2A00354640 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5422833473F329A89A9CBB7E /* Pods-Example-tvOS.release.xcconfig */; + baseConfigurationReference = 638A1CADF7E8A6563C78C31C /* Pods-Example-tvOS.release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; @@ -3438,7 +3454,7 @@ }; F2C2FA6321BBD26300934744 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C0FCA45216D537DD02EE4C33 /* Pods-Backtrace-iOS.debug.xcconfig */; + baseConfigurationReference = 5E37DA3F86EDF46AE4A6FD55 /* Pods-Backtrace-iOS.debug.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_SEARCH_USER_PATHS = NO; @@ -3531,7 +3547,7 @@ }; F2C2FA6421BBD26300934744 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 39033D1F127DF12C49394199 /* Pods-Backtrace-iOS.release.xcconfig */; + baseConfigurationReference = CF0E6D0F08AAE9CB60192052 /* Pods-Backtrace-iOS.release.xcconfig */; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_SEARCH_USER_PATHS = NO; @@ -3618,7 +3634,7 @@ }; F2C2FA6621BBD26300934744 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 2A9A82981A60FB9C85995A27 /* Pods-Backtrace-iOSTests.debug.xcconfig */; + baseConfigurationReference = 400175344F10D1AB39E97CBC /* Pods-Backtrace-iOSTests.debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -3700,7 +3716,7 @@ }; F2C2FA6721BBD26300934744 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A21BEAA470CD3B272983C2E0 /* Pods-Backtrace-iOSTests.release.xcconfig */; + baseConfigurationReference = 7161F22B650B5C3A47D7C4F7 /* Pods-Backtrace-iOSTests.release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -3776,7 +3792,7 @@ }; F2D8BE1421BC065F007CFEFA /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 703EE750CC36088E64716403 /* Pods-Example-iOS.debug.xcconfig */; + baseConfigurationReference = 8AA7D23E1801210162B0B63C /* Pods-Example-iOS.debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -3860,7 +3876,7 @@ }; F2D8BE1521BC065F007CFEFA /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 41EA3E9725F179DCE2943E9A /* Pods-Example-iOS.release.xcconfig */; + baseConfigurationReference = 037D6550B4724EB4F05F0774 /* Pods-Example-iOS.release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -3938,7 +3954,7 @@ }; F2D8BE3321BC5F98007CFEFA /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A6DDC2E22CECF26B64A6EF18 /* Pods-Example-iOS-ObjC.debug.xcconfig */; + baseConfigurationReference = 04C55AC90DC913E1DB190B57 /* Pods-Example-iOS-ObjC.debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -4016,7 +4032,7 @@ }; F2D8BE3421BC5F98007CFEFA /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5FBA9FF525151F710FD636E7 /* Pods-Example-iOS-ObjC.release.xcconfig */; + baseConfigurationReference = 559635099C43B2A8C8FB3E2E /* Pods-Example-iOS-ObjC.release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -4088,7 +4104,7 @@ }; F2D8BE5421BDA7D0007CFEFA /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 4ABE61E98666C88E7FA37383 /* Pods-Example-macOS-ObjC.debug.xcconfig */; + baseConfigurationReference = 79DE4E5AA2488ADC73D72F8D /* Pods-Example-macOS-ObjC.debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; @@ -4165,7 +4181,7 @@ }; F2D8BE5521BDA7D0007CFEFA /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C95639FF150AFBB8678560CA /* Pods-Example-macOS-ObjC.release.xcconfig */; + baseConfigurationReference = 3D6DBD2958FC7C95A508A2CF /* Pods-Example-macOS-ObjC.release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; diff --git a/Sources/Features/Extensions/NSManagedObjectContext+Extensions.swift b/Sources/Features/Extensions/NSManagedObjectContext+Extensions.swift new file mode 100644 index 00000000..bf77c4f5 --- /dev/null +++ b/Sources/Features/Extensions/NSManagedObjectContext+Extensions.swift @@ -0,0 +1,44 @@ +import Foundation +import CoreData + +enum PerformAndWaitError: Error { + case blockDidNotRun +} + +extension NSManagedObjectContext { + + /// Runs `block` inside `performAndWait`, captures any thrown error and returns the result. + /// + /// - Parameter block: A closure that either returns `T` or throws an error + /// - Returns: The value returned by `block` + /// - Throws: + /// - Rethrows any error from `block`, or `PerformAndWaitError.blockDidNotRun` if the closure never produced a result + func performAndWaitThrowing(_ block: () throws -> T) throws -> T { + var result: T! + var thrownError: Error? + performAndWait { + do { + result = try block() + } catch { + thrownError = error + } + } + if let thrownError = thrownError { + throw thrownError + } + return result + } + + // Swift 5 approach + func performAndWaitThrowingSwift5(_ block: () throws -> T) throws -> T { + var result: Swift.Result? + performAndWait { + // Captures returned value or throws error + result = Swift.Result(catching: block) + } + guard let outcome = result else { + throw PerformAndWaitError.blockDidNotRun + } + return try outcome.get() + } +} diff --git a/Sources/Features/Repository/PersistentRepository.swift b/Sources/Features/Repository/PersistentRepository.swift index a92ce4a8..3d5511b9 100644 --- a/Sources/Features/Repository/PersistentRepository.swift +++ b/Sources/Features/Repository/PersistentRepository.swift @@ -1,6 +1,7 @@ import Foundation import CoreData +/// Describes `PersistentStorable` Core Data protocol PersistentStorable { associatedtype ManagedObjectType: NSManagedObject @@ -13,18 +14,20 @@ protocol PersistentStorable { init(managedObject: ManagedObjectType) throws } +/// Persists `PersistentStorable` objects using Core Data +/// Manages concurrency by using a private-queue context and `performAndWaitThrowing` for all operations final class PersistentRepository { let backgroundContext: NSManagedObjectContext let settings: BacktraceDatabaseSettings - let url: URL - + + /// Creates a new `PersistentRepository` + /// - Parameter settings: BacktraceDatabaseSettings + /// - Throws: `RepositoryError` init(settings: BacktraceDatabaseSettings) throws { self.settings = settings - let momdName = "Model" - #if SWIFT_PACKAGE guard let modelURL = Bundle.module.url(forResource: momdName, withExtension: "momd") else { throw RepositoryError @@ -81,6 +84,12 @@ final class PersistentRepository { try BacktraceFileManager.excludeFromBackup(url) } + + /// Attempts to migrate the persistent store if the existing store is incompatible with the current `NSManagedObjectModel` + /// - Parameters: + /// - coordinator: NSPersistentStoreCoordinator + /// - storeDir: URL + /// - managedObject: NSManagedObjectModel static func migration(coordinator: NSPersistentStoreCoordinator, storeDir: URL, managedObject: NSManagedObjectModel) throws { @@ -102,124 +111,154 @@ final class PersistentRepository { // MARK: - Repository extension PersistentRepository: Repository { - + + /// Saves a new resource to Core Data + /// - Parameter resource: Resource to save + /// Throws: + /// - `RepositoryError.canNotCreateEntityDescription` if the entity cannot be found + /// - Any Core Data error that occurs during the save func save(_ resource: Resource) throws { - try removeOldestRecordIfNeeded() - - guard let entity = NSEntityDescription.entity(forEntityName: Resource.entityName, in: backgroundContext) else { - throw RepositoryError.canNotCreateEntityDescription - } - let newManagedObject = NSManagedObject(entity: entity, insertInto: backgroundContext) - newManagedObject.setValue(resource.identifier.uuidString, forKey: "hashProperty") - newManagedObject.setValue(resource.reportData, forKey: "reportData") - newManagedObject.setValue(Date(), forKey: "dateAdded") - newManagedObject.setValue(0, forKey: "retryCount") - newManagedObject.setValue(resource.attachmentPaths, forKey: "attachmentPaths") - try backgroundContext.save() + try backgroundContext.performAndWaitThrowing { + try _removeOldestRecordIfNeededLocked() + + guard let entity = NSEntityDescription.entity(forEntityName: Resource.entityName, in: backgroundContext) else { + throw RepositoryError.canNotCreateEntityDescription + } + let newManagedObject = NSManagedObject(entity: entity, insertInto: backgroundContext) + newManagedObject.setValue(resource.identifier.uuidString, forKey: "hashProperty") + newManagedObject.setValue(resource.reportData, forKey: "reportData") + newManagedObject.setValue(Date(), forKey: "dateAdded") + newManagedObject.setValue(0, forKey: "retryCount") + newManagedObject.setValue(resource.attachmentPaths, forKey: "attachmentPaths") + try backgroundContext.save() + } + // File storage outside the Core Data backgroundContext (optional concurrency). + // TODO: Verify AttributesStorage for concurrency try AttributesStorage.store(resource.attributes, fileName: resource.identifier.uuidString) } - + + /// Deletes a resource from Core Data + /// - Parameter resource: Resource to delete + /// - Throws: Any error from fetching or deleting the records func delete(_ resource: Resource) throws { - let predicate = NSPredicate(format: "hashProperty==%@", resource.identifier.uuidString) - let fetchRequestResults = try getResources(predicate: predicate, fetchLimit: 100) - try delete(managedObjects: fetchRequestResults) - } - - /// Convenience method for deleting reports. Only this method should be used for deleting objects from - /// database context. - /// - /// - Parameter managedObjects: Managed objects to delete - private func delete(managedObjects: [Resource.ManagedObjectType]) throws { - managedObjects.forEach { - if let fileName = $0.value(forKey: "hashProperty") as? String, let uuid = UUID(uuidString: fileName) { - try? AttributesStorage.remove(fileName: uuid.uuidString) - } - backgroundContext.delete($0) + try backgroundContext.performAndWaitThrowing { + let predicate = NSPredicate(format: "hashProperty==%@", resource.identifier.uuidString) + let fetchRequestResults = try _getResourcesLocked(predicate: predicate, fetchLimit: 100) + try _deleteLocked(fetchRequestResults) } - try backgroundContext.save() } - + + /// Fetches all stored resources from the database + /// - Returns: [Resource] + /// - Throws: Any error from the fetch request or object initialization func getAll() throws -> [Resource] { - return try getResources().map(Resource.init) + return try backgroundContext.performAndWaitThrowing { + let resources = try _getResourcesLocked() + return try resources.map(Resource.init) + } } - + + /// Fetches resources matching optional sort, predicate, and limit criteria + /// - Parameters: + /// - sortDescriptors: [NSSortDescriptor] + /// - predicate: NSPredicate? + /// - fetchLimit: Int? + /// - Returns: [Resource] + /// - Throws: Any error from the fetch request or object initialization. func get(sortDescriptors: [NSSortDescriptor]? = nil, predicate: NSPredicate? = nil, fetchLimit: Int? = nil) throws -> [Resource] { - return try getResources(sortDescriptors: sortDescriptors, predicate: predicate, fetchLimit: fetchLimit) - .map(Resource.init) + return try backgroundContext.performAndWaitThrowing { + let resources = try _getResourcesLocked(sortDescriptors: sortDescriptors, predicate: predicate, fetchLimit: fetchLimit) + return try resources.map(Resource.init) + } } - + + /// Fetches the newest (by `dateAdded`) resources + /// - Parameter count: Int : Default`1` + /// - Returns: [Resource] + /// - Throws: Any error from the fetch request or object initialization func getLatest(count: Int = 1) throws -> [Resource] { - let sortDescriptors = [NSSortDescriptor(key: "dateAdded", ascending: false)] - let latest = try getResources(sortDescriptors: sortDescriptors, fetchLimit: count) - return try latest.map(Resource.init) + return try backgroundContext.performAndWaitThrowing { + let sortDescriptors = [NSSortDescriptor(key: "dateAdded", ascending: false)] + let latest = try _getResourcesLocked(sortDescriptors: sortDescriptors, fetchLimit: count) + return try latest.map(Resource.init) + } } - + + /// Fetches the oldest (by `dateAdded`) resources + /// - Parameter count: Int : Default`1` + /// - Returns: [Resource] + /// - Throws: Any error from the fetch request or object initialization func getOldest(count: Int = 1) throws -> [Resource] { - let sortDescriptors = [NSSortDescriptor(key: "dateAdded", ascending: true)] - let latest = try getResources(sortDescriptors: sortDescriptors, fetchLimit: count) - return try latest.map(Resource.init) + return try backgroundContext.performAndWaitThrowing { + let sortDescriptors = [NSSortDescriptor(key: "dateAdded", ascending: true)] + let latest = try _getResourcesLocked(sortDescriptors: sortDescriptors, fetchLimit: count) + return try latest.map(Resource.init) + } } - + + /// Increments the retry count for a resource. If the count reaches limit, removes the resource from the database. + /// - Parameters: + /// - resource: Resource + /// - limit: Int + /// - Throws: + /// - `RepositoryError.resourceNotFound` if the resource cannot be fetched + /// - Any error from saving or deleting in Core Data func incrementRetryCount(_ resource: Resource, limit: Int) throws { - let predicate = NSPredicate(format: "hashProperty==%@", resource.identifier.uuidString) - let fetchRequestResults = try getResources(predicate: predicate, fetchLimit: 1) - - guard let fetchedResult = fetchRequestResults.first, - let currentRetryCount: Int = fetchedResult.value(forKey: "retryCount") as? Int else { + try backgroundContext.performAndWaitThrowing { + let predicate = NSPredicate(format: "hashProperty==%@", resource.identifier.uuidString) + let fetchRequestResults = try _getResourcesLocked(predicate: predicate, fetchLimit: 1) + + guard let fetchedResult = fetchRequestResults.first, + let currentRetryCount: Int = fetchedResult.value(forKey: "retryCount") as? Int else { throw RepositoryError.resourceNotFound - } - // if exceeds limit, remove from db, otherwise just increment retryCount property - if currentRetryCount >= limit { - try delete(managedObjects: [fetchedResult]) - } else { - // increment number of retires - fetchedResult.setValue(currentRetryCount + 1, forKey: "retryCount") - // update report data (could be modified) - fetchedResult.setValue(resource.reportData, forKey: "reportData") - try backgroundContext.save() - } - } - - func clear() throws { - let managedObjects = try getResources() - try delete(managedObjects: managedObjects) - } - - /// Remove oldest result if max number of records is exceeded or total database size is exceeded. - private func removeOldestRecordIfNeeded() throws { - if settings.maxRecordCount != BacktraceDatabaseSettings.unlimited { - // check number of records - while try countResources() + 1 > settings.maxRecordCount { - try removeOldestRecord() } - } - - if settings.maxDatabaseSize != BacktraceDatabaseSettings.unlimited { - // check database size - while try BacktraceFileManager.sizeOfFile(at: url) > settings.maxDatabaseSizeInBytes { - let size = try BacktraceFileManager.sizeOfFile(at: url) - BacktraceLogger.debug("Database size before removing last record: \(size)") - try removeOldestRecord() + // if exceeds limit, remove from db, otherwise just increment retryCount property + if currentRetryCount >= limit { + try _deleteLocked([fetchedResult]) + } else { + // increment number of retires + fetchedResult.setValue(currentRetryCount + 1, forKey: "retryCount") + // update report data (could be modified) + fetchedResult.setValue(resource.reportData, forKey: "reportData") + try backgroundContext.save() } } } - - private func removeOldestRecord() throws { - let sortDescriptors = [NSSortDescriptor(key: "dateAdded", ascending: true)] - let oldestResource = try getResources(sortDescriptors: sortDescriptors, fetchLimit: 1) - try delete(managedObjects: oldestResource) + + /// Deletes all stored resources + /// - Throws: Any error from fetching or deleting the records + func clear() throws { + try backgroundContext.performAndWaitThrowing { + let managedObjects = try _getResourcesLocked() + try _deleteLocked(managedObjects) + } } - + + /// Returns the total count of resources in the database + /// - Returns: Int: The number of resources func countResources() throws -> Int { - let resourcesCountRequest = NSFetchRequest(entityName: Resource.entityName) - return try backgroundContext.count(for: resourcesCountRequest) + try backgroundContext.performAndWaitThrowing { + try _countResourcesLocked() + } } - - private func getResources(sortDescriptors: [NSSortDescriptor]? = nil, - predicate: NSPredicate? = nil, - fetchLimit: Int? = nil) throws -> [Resource.ManagedObjectType] { + + // MARK: - Private Locked Helpers + // Must be called only inside performAndWait{} + + /// Convenience method for fetching objects from the context + /// Must be called only inside a `performAndWaitThrowing` block + /// + /// - Parameters: + /// - Parameter sortDescriptors:[NSSortDescriptor]? + /// - Parameter predicate:NSPredicate? + /// - Parameter fetchLimit:Int? + /// - Returns: [Resource.ManagedObjectType] + /// - Throws: Any error from `fetch(_:)`. + private func _getResourcesLocked(sortDescriptors: [NSSortDescriptor]? = nil, + predicate: NSPredicate? = nil, + fetchLimit: Int? = nil) throws -> [Resource.ManagedObjectType] { let request = NSFetchRequest(entityName: Resource.entityName) request.returnsObjectsAsFaults = false if let fetchLimit = fetchLimit { @@ -229,4 +268,59 @@ extension PersistentRepository: Repository { request.predicate = predicate return try backgroundContext.fetch(request) } + + /// Convenience method for deleting the specified managed objects + /// Must be called only within a `performAndWaitThrowing` block + /// + /// - Parameter managedObjects: Managed objects to delete + /// - Throws: Any error from `save()`. + private func _deleteLocked(_ managedObjects: [Resource.ManagedObjectType]) throws { + managedObjects.forEach { + if let fileName = $0.value(forKey: "hashProperty") as? String, let uuid = UUID(uuidString: fileName) { + try? AttributesStorage.remove(fileName: uuid.uuidString) + } + backgroundContext.delete($0) + } + try backgroundContext.save() + } + + /// Counts the total number of resources in the store + /// Must be called only within a `performAndWaitThrowing` block + /// + /// - Returns: Int + /// - Throws: Any error from `count(for:)` + private func _countResourcesLocked() throws -> Int { + let resourcesCountRequest = NSFetchRequest(entityName: Resource.entityName) + return try backgroundContext.count(for: resourcesCountRequest) + } + + /// Removes the oldest record if the maximum number of records or total database size is exceeded + /// Must be called only within a `performAndWaitThrowing` block + /// - Throws: Any error from counting, removing records, or checking file size + private func _removeOldestRecordIfNeededLocked() throws { + // check number of records + if settings.maxRecordCount != BacktraceDatabaseSettings.unlimited { + while try _countResourcesLocked() + 1 > settings.maxRecordCount { + try _removeOldestRecordLocked() + } + } + + // check database size + if settings.maxDatabaseSize != BacktraceDatabaseSettings.unlimited { + while try BacktraceFileManager.sizeOfFile(at: url) > settings.maxDatabaseSizeInBytes { + let size = try BacktraceFileManager.sizeOfFile(at: url) + BacktraceLogger.debug("Database size before removing last record: \(size)") + try _removeOldestRecordLocked() + } + } + } + + /// Removes the single oldest record (by `dateAdded` + /// Must be called only within a `performAndWaitThrowing` block + /// - Throws: Any error from fetching or deleting the record + private func _removeOldestRecordLocked() throws { + let sortDescriptors = [NSSortDescriptor(key: "dateAdded", ascending: true)] + let oldestResource = try _getResourcesLocked(sortDescriptors: sortDescriptors, fetchLimit: 1) + try _deleteLocked(oldestResource) + } } diff --git a/Tests/BacktraceDatabaseTests.swift b/Tests/BacktraceDatabaseTests.swift index 1002e6c9..f0cfc621 100644 --- a/Tests/BacktraceDatabaseTests.swift +++ b/Tests/BacktraceDatabaseTests.swift @@ -51,6 +51,83 @@ final class BacktraceDatabaseTests: QuickSpec { } expect { try? repository.countResources() }.toEventually(equal(100)) } + + throwingIt("supports concurrent read/write operations") { + try repository.clear() + + let writeGroup = DispatchGroup() + let readGroup = DispatchGroup() + + // concurrent writes + for _ in 1...5 { + writeGroup.enter() + DispatchQueue.global().async { + defer { writeGroup.leave() } + do { + let report = try crashReporter.generateLiveReport(attributes: [:]) + try repository.save(report) + } catch { + fail("Failed to save concurrently: \(error)") + } + } + } + + // concurrent reads + for _ in 1...5 { + readGroup.enter() + DispatchQueue.global().async { + defer { readGroup.leave() } + do { + _ = try repository.getLatest() + } catch { + fail("Failed to fetch concurrently: \(error)") + } + } + } + + writeGroup.wait() + readGroup.wait() + expect(try? repository.countResources()).to(equal(5)) + } + + throwingIt("test with a custom maxRecordCount, removes oldest records when max record count is exceeded") { + let settingsWithLimit = BacktraceDatabaseSettings() + settingsWithLimit.maxRecordCount = 5 + let limitedRepository = try PersistentRepository( + settings: settingsWithLimit) + try limitedRepository.clear() + // Insert 6 reports + let timeOrderedReports = try (1...6).map { _ -> BacktraceReport in + let report = try crashReporter.generateLiveReport(attributes: [:]) + try limitedRepository.save(report) + return report + } + // Should remove oldest if limit is 5 + let finalCount = try limitedRepository.countResources() + expect(finalCount).to(equal(5)) + + // check if the first inserted record is deleted + let firstInserted = timeOrderedReports.first! + let allResources = try limitedRepository.getAll() + expect(allResources).notTo(containElementSatisfying { $0.identifier == firstInserted.identifier }) + } + + throwingIt("increments retry count and removes resource when limit is exceeded") { + try repository.clear() + let report = try crashReporter.generateLiveReport(attributes: [:]) + try repository.save(report) + // First increment + try repository.incrementRetryCount(report, limit: 3) + // second increment + try repository.incrementRetryCount(report, limit: 3) + // getLatest should still return latest + let secondCheck = try repository.getLatest().first + expect(secondCheck).toNot(beNil()) + // Exceed the limit + try repository.incrementRetryCount(report, limit: 2) + // Now it should be removed + expect { try repository.countResources() }.to(equal(0)) + } } } } diff --git a/Tests/NSManagedObjectContextExtensionTests.swift b/Tests/NSManagedObjectContextExtensionTests.swift new file mode 100644 index 00000000..b42251c3 --- /dev/null +++ b/Tests/NSManagedObjectContextExtensionTests.swift @@ -0,0 +1,55 @@ +import Nimble +import CoreData +import Quick +@testable import Backtrace +#if SWIFT_PACKAGE +import Foundation +#endif + +private enum MockTestError: Error, Equatable { + case somethingWentWrong +} + +final class NSManagedObjectContextExtensionTests: QuickSpec { + override func spec() { + describe("performAndWaitThrowing") { + throwingContext("with an in-memory NSManagedObjectContext") { + var inMemoryContext: NSManagedObjectContext! + + beforeEach { + // in-memory context + let mom = NSManagedObjectModel() + let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: mom) + try? persistentStoreCoordinator.addPersistentStore(ofType: NSInMemoryStoreType, + configurationName: nil, + at: nil, + options: nil) + inMemoryContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) + inMemoryContext.persistentStoreCoordinator = persistentStoreCoordinator + } + + afterEach { + inMemoryContext = nil + } + + throwingIt("returns the correct value if the block succeeds") { + let expected = "expected result" + let result: String = try inMemoryContext.performAndWaitThrowing { + // Return a simple string + return expected + } + expect(result).to(equal(expected)) + } + + throwingIt("rethrows an error if the block fails") { + expect { + try inMemoryContext.performAndWaitThrowing { + throw MockTestError.somethingWentWrong + } + } + .to(throwError(MockTestError.somethingWentWrong)) + } + } + } + } +}