Skip to content

Commit bc63f91

Browse files
authored
Merge pull request #16 from bookingcom/gtarasov/hangs_to_crashlytics
Adding optional support to send fatal and non-fatal hangs to Firebase Crashlytics
2 parents a501b2a + 5aa47bb commit bc63f91

33 files changed

+1372
-152
lines changed

.github/workflows/tests.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
2525
- name: SwiftLint
2626
run:
27-
Pods/SwiftLint/swiftlint
27+
Pods/SwiftLint/swiftlint lint --strict
2828
# to compile swift package we should remove workspace and xcodeproj
2929
# otherwise xcodebuild takes schemes from them
3030
- name: Remove workspace
@@ -34,6 +34,8 @@ jobs:
3434
- name: Compile
3535
run: |
3636
xcodebuild -scheme PerformanceSuite -destination 'generic/platform=iOS'
37+
xcodebuild -scheme PerformanceSuiteCrashlytics -destination 'generic/platform=iOS'
38+
xcodebuild -scheme PerformanceApp -destination 'generic/platform=iOS'
3739
# restore removed files just in case it is needed for the further steps
3840
- name: Restore workspace
3941
run: |

Package.resolved

Lines changed: 131 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,50 @@ let package = Package(
1111
.library(
1212
name: "PerformanceSuite",
1313
targets: ["PerformanceSuite"]),
14+
.library(
15+
name: "PerformanceSuiteCrashlytics",
16+
targets: ["PerformanceSuiteCrashlytics"]),
17+
.executable(
18+
name: "PerformanceApp",
19+
targets: ["PerformanceApp"]
20+
),
21+
],
22+
dependencies: [
23+
.package(url: "https://github.com/firebase/firebase-ios-sdk.git", from: .init(10, 0, 0)),
24+
.package(url: "https://github.com/yene/GCDWebServer", exact: .init(3, 5, 7)),
1425
],
1526
targets: [
1627
.target(
1728
name: "PerformanceSuite",
1829
dependencies: ["MainThreadCallStack"],
1930
path: "PerformanceSuite/Sources"
2031
),
32+
.target(
33+
name: "PerformanceSuiteCrashlytics",
34+
dependencies: [
35+
"PerformanceSuite",
36+
"MainThreadCallStack",
37+
"CrashlyticsImports",
38+
.product(name: "FirebaseCrashlytics", package: "firebase-ios-sdk"),
39+
],
40+
path: "PerformanceSuite/Crashlytics/Sources"
41+
),
42+
.target(name: "CrashlyticsImports",
43+
dependencies: [
44+
.product(name: "FirebaseCrashlytics", package: "firebase-ios-sdk")
45+
],
46+
path: "PerformanceSuite/Crashlytics/Imports"),
2147
.target(name: "MainThreadCallStack",
2248
path: "PerformanceSuite/MainThreadCallStack"),
49+
50+
.executableTarget(
51+
name: "PerformanceApp",
52+
dependencies: [
53+
"PerformanceSuite",
54+
"PerformanceSuiteCrashlytics",
55+
.product(name: "GCDWebServer", package: "GCDWebServer"),
56+
],
57+
path: "PerformanceSuite/PerformanceApp"
58+
),
2359
]
2460
)

PerformanceSuite.podspec

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,41 @@ Pod::Spec.new do |s|
66
s.license = { :type => 'MIT', :file => 'LICENSE' }
77
s.author = { 'Gleb Tarasov' => '[email protected]' }
88
s.source = { :git => 'https://github.com/bookingcom/perfsuite-ios.git', :tag => s.version.to_s }
9-
s.source_files = 'PerformanceSuite/Sources/**/*.swift', 'PerformanceSuite/MainThreadCallStack/**/*.{h,c}'
10-
s.public_header_files = 'PerformanceSuite/MainThreadCallStack/include/*.h'
11-
s.platform = :ios, "14.0"
12-
s.swift_version = "5.7.1"
9+
s.platform = :ios, '14.0'
10+
s.swift_version = '5.7.1'
11+
s.default_subspec = 'Core'
12+
13+
s.subspec 'Core' do |core_spec|
14+
core_spec.source_files = 'PerformanceSuite/Sources/**/*.swift', 'PerformanceSuite/MainThreadCallStack/**/*.{h,c}'
15+
core_spec.public_header_files = 'PerformanceSuite/MainThreadCallStack/include/*.h'
16+
end
17+
18+
s.subspec 'Crashlytics' do |cr_spec|
19+
cr_spec.source_files = 'PerformanceSuite/Crashlytics/Sources/*.swift', 'PerformanceSuite/Crashlytics/Imports/include/*.h'
20+
cr_spec.public_header_files = 'PerformanceSuite/Crashlytics/Imports/include/*.h'
21+
cr_spec.dependency 'PerformanceSuite/Core'
22+
cr_spec.dependency 'FirebaseCrashlytics'
23+
end
1324

1425
# Sample App which is also used for UI tests as a host
1526
s.app_spec 'PerformanceApp' do |app_spec|
16-
app_spec.source_files = 'PerformanceSuite/PerformanceApp/*.swift'
27+
app_spec.source_files = 'PerformanceSuite/PerformanceApp/**/*.swift'
1728
app_spec.dependency 'GCDWebServer'
29+
app_spec.dependency 'PerformanceSuite/Crashlytics'
1830
end
1931

2032
# Unit tests
2133
s.test_spec 'Tests' do |test_spec|
2234
test_spec.requires_app_host = true
23-
test_spec.source_files = 'PerformanceSuite/Tests/*.swift'
35+
test_spec.source_files = 'PerformanceSuite/Tests/**/*.swift'
36+
test_spec.dependency 'PerformanceSuite/Crashlytics'
2437
end
2538

2639
# UI Tests
2740
s.test_spec 'UITests' do |test_spec|
2841
test_spec.requires_app_host = true
2942
test_spec.test_type = :ui
30-
test_spec.source_files = ['PerformanceSuite/UITests/*.swift', 'PerformanceSuite/PerformanceApp/UITestsInterop.swift']
43+
test_spec.source_files = 'PerformanceSuite/UITests/**/*.swift', 'PerformanceSuite/PerformanceApp/UITestsInterop.swift'
3144
test_spec.app_host_name = 'PerformanceSuite/PerformanceApp'
3245
test_spec.dependency 'PerformanceSuite/PerformanceApp'
3346
test_spec.dependency 'GCDWebServer'
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// we need some .c code for SwiftPM target to have .o file, otherwise it doesn't compile
2+
int fake_function() {}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
@import FirebaseCrashlytics;
2+
3+
NS_ASSUME_NONNULL_BEGIN
4+
5+
/// We are using private function from Crashlytics to be able to record
6+
/// custom exception without immediately sending it.
7+
///
8+
/// We will record stack trace for hang as fatal hang, and if it turned out
9+
/// to be a non-fatal hang, we will remove this Firebase report and replace it
10+
/// with non-fatal report.
11+
///
12+
/// We do not use recordExceptionModel here, because it doesn't return path
13+
/// to the report, so it is much harder to remove created report to re-create it
14+
/// with the different hang type.
15+
NSString *FIRCLSExceptionRecordOnDemandModel(FIRExceptionModel *exceptionModel,
16+
int previousRecordedOnDemandExceptions,
17+
int previousDroppedOnDemandExceptions);
18+
19+
/// We need to pass proper type (first argument) to this method
20+
/// to record stack traces of all the methods,
21+
/// that's why we cannot use public `FIRCLSExceptionRecordOnDemandModel`
22+
NSString *FIRCLSExceptionRecordOnDemand(int type,
23+
const char *name,
24+
const char *reason,
25+
NSArray<FIRStackFrame *> *frames,
26+
BOOL fatal,
27+
int previousRecordedOnDemandExceptions,
28+
int previousDroppedOnDemandExceptions);
29+
30+
31+
/// Firebase marker file which indicates that exception happened during the previous launch.
32+
/// If this file is present: `FIRCrashlytics.didCrashDuringPreviousExecution` will be `YES` on the next launch.
33+
extern const char *FIRCLSCrashedMarkerFileName;
34+
35+
/// This is FIRCLSFileManager, but this class is private.
36+
/// We need to get rootPath to be able to remove marker file after we do recordOnDemandExceptionModel.
37+
/// Because we do not want hangs to be considered as crashes in Crashlytics on the next launch.
38+
@protocol RootPathProvider<NSObject>
39+
40+
@property(nonatomic, readonly, nullable) NSString *rootPath;
41+
42+
@end
43+
44+
@interface FIRCrashlytics (OnDemandException)
45+
46+
/// We use `recordOnDemandExceptionModel` instead of `recordExceptionModel`,
47+
/// because `recordExceptionModel` will send data only on the next launch,
48+
/// but we want to send non-fatal hang events as soon as we receive it.
49+
- (void)recordOnDemandExceptionModel:(FIRExceptionModel *)exceptionModel;
50+
51+
/// We need file manager to get `rootPath` folder
52+
@property(nonatomic, readonly, nullable) id<RootPathProvider> fileManager;
53+
54+
@end
55+
56+
NS_ASSUME_NONNULL_END

0 commit comments

Comments
 (0)