17
17
#import " Crashlytics/Crashlytics/Controllers/FIRCLSManagerData.h"
18
18
#import " Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.h"
19
19
#import " Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionToken.h"
20
+ #import " Crashlytics/Crashlytics/Helpers/FIRCLSLogger.h"
20
21
#import " Crashlytics/Crashlytics/Models/FIRCLSFileManager.h"
21
22
#import " Crashlytics/Crashlytics/Models/FIRCLSInternalReport.h"
22
23
#import " Crashlytics/Crashlytics/Private/FIRCrashlyticsReport_Private.h"
23
24
#import " Crashlytics/Crashlytics/Public/FirebaseCrashlytics/FIRCrashlyticsReport.h"
24
25
26
+ // This value should stay in sync with the Android SDK
27
+ NSUInteger const FIRCLSMaxUnsentReports = 4 ;
28
+
25
29
@interface FIRCLSExistingReportManager ()
26
30
27
31
@property (nonatomic , strong ) FIRCLSFileManager *fileManager;
28
- @property (nonatomic , strong ) NSOperationQueue *operationQueue;
29
32
@property (nonatomic , strong ) FIRCLSReportUploader *reportUploader;
33
+ @property (nonatomic , strong ) NSOperationQueue *operationQueue;
30
34
31
35
// This list of active reports excludes the brand new active report that will be created this run of
32
36
// the app.
33
37
@property (nonatomic , strong ) NSArray *existingUnemptyActiveReportPaths;
34
38
@property (nonatomic , strong ) NSArray *processingReportPaths;
35
39
@property (nonatomic , strong ) NSArray *preparedReportPaths;
36
40
41
+ @property (nonatomic , strong ) FIRCLSInternalReport *newestInternalReport;
42
+
37
43
@end
38
44
39
45
@implementation FIRCLSExistingReportManager
@@ -52,15 +58,16 @@ - (instancetype)initWithManagerData:(FIRCLSManagerData *)managerData
52
58
return self;
53
59
}
54
60
55
- NSInteger compareOlder (FIRCLSInternalReport *reportA,
61
+ NSInteger compareNewer (FIRCLSInternalReport *reportA,
56
62
FIRCLSInternalReport *reportB,
57
63
void *context) {
58
- return [reportA.dateCreated compare: reportB.dateCreated];
64
+ // Compare naturally sorts with oldest first, so swap A and B
65
+ return [reportB.dateCreated compare: reportA.dateCreated];
59
66
}
60
67
61
68
- (void )collectExistingReports {
62
69
self.existingUnemptyActiveReportPaths =
63
- [self getUnemptyExistingActiveReportsAndDeleteEmpty :self .fileManager.activePathContents];
70
+ [self getUnsentActiveReportsAndDeleteEmptyOrOld :self .fileManager.activePathContents];
64
71
self.processingReportPaths = self.fileManager .processingPathContents ;
65
72
self.preparedReportPaths = self.fileManager .preparedPathContents ;
66
73
}
@@ -70,43 +77,71 @@ - (FIRCrashlyticsReport *)newestUnsentReport {
70
77
return nil ;
71
78
}
72
79
73
- NSMutableArray <NSString *> *allReportPaths =
74
- [NSMutableArray arrayWithArray: self .existingUnemptyActiveReportPaths];
80
+ return [[FIRCrashlyticsReport alloc ] initWithInternalReport: self .newestInternalReport];
81
+ }
82
+
83
+ - (NSUInteger )unsentReportsCount {
84
+ // There are nuances about why we only count active reports.
85
+ // See the header comment for more information.
86
+ return self.existingUnemptyActiveReportPaths .count ;
87
+ }
75
88
89
+ /*
90
+ * This has the side effect of deleting any reports over the max, starting with oldest reports.
91
+ */
92
+ - (NSArray <NSString *> *)getUnsentActiveReportsAndDeleteEmptyOrOld : (NSArray *)reportPaths {
76
93
NSMutableArray <FIRCLSInternalReport *> *validReports = [NSMutableArray array ];
77
- for (NSString *path in allReportPaths ) {
94
+ for (NSString *path in reportPaths ) {
78
95
FIRCLSInternalReport *_Nullable report = [FIRCLSInternalReport reportWithPath: path];
79
96
if (!report) {
80
97
continue ;
81
98
}
99
+
100
+ // Delete reports without any crashes or non-fatals
101
+ if (![report hasAnyEvents ]) {
102
+ [self .operationQueue addOperationWithBlock: ^{
103
+ [self .fileManager removeItemAtPath: path];
104
+ }];
105
+ continue ;
106
+ }
107
+
82
108
[validReports addObject: report];
83
109
}
84
110
85
- [validReports sortUsingFunction: compareOlder context: nil ];
111
+ if (validReports.count == 0 ) {
112
+ return @[];
113
+ }
86
114
87
- FIRCLSInternalReport *_Nullable internalReport = [validReports lastObject ];
88
- return [[FIRCrashlyticsReport alloc ] initWithInternalReport: internalReport];
89
- }
115
+ // Sort with the newest at the end
116
+ [validReports sortUsingFunction: compareNewer context: nil ];
90
117
91
- - (NSUInteger )unsentReportsCount {
92
- // There are nuances about why we only count active reports.
93
- // See the header comment for more information.
94
- return self.existingUnemptyActiveReportPaths .count ;
95
- }
118
+ // Set our report for updating in checkAndUpdateUnsentReports
119
+ self.newestInternalReport = [validReports firstObject ];
96
120
97
- - (NSArray *)getUnemptyExistingActiveReportsAndDeleteEmpty : (NSArray *)reportPaths {
98
- NSMutableArray *unemptyReports = [NSMutableArray array ];
99
- for (NSString *path in reportPaths) {
100
- FIRCLSInternalReport *report = [FIRCLSInternalReport reportWithPath: path];
101
- if ([report hasAnyEvents ]) {
102
- [unemptyReports addObject: path];
103
- } else {
121
+ // Delete any reports above the limit, starting with the oldest
122
+ // which should be at the start of the array.
123
+ if (validReports.count > FIRCLSMaxUnsentReports) {
124
+ NSUInteger deletingCount = validReports.count - FIRCLSMaxUnsentReports;
125
+ FIRCLSInfoLog (@" Deleting %lu unsent reports over the limit of %lu to prevent disk space from "
126
+ @" filling up. To prevent this make sure to call send/deleteUnsentReports." ,
127
+ deletingCount, FIRCLSMaxUnsentReports);
128
+ }
129
+
130
+ // Not that validReports is sorted, delete any reports at indices > MAX_UNSENT_REPORTS, and
131
+ // collect the rest of the reports to return.
132
+ NSMutableArray <NSString *> *validReportPaths = [NSMutableArray array ];
133
+ for (int i = 0 ; i < validReports.count ; i++) {
134
+ if (i >= FIRCLSMaxUnsentReports) {
104
135
[self .operationQueue addOperationWithBlock: ^{
136
+ NSString *path = [[validReports objectAtIndex: i] path ];
105
137
[self .fileManager removeItemAtPath: path];
106
138
}];
139
+ } else {
140
+ [validReportPaths addObject: [[validReports objectAtIndex: i] path ]];
107
141
}
108
142
}
109
- return unemptyReports;
143
+
144
+ return validReportPaths;
110
145
}
111
146
112
147
- (void )sendUnsentReportsWithToken : (FIRCLSDataCollectionToken *)dataCollectionToken
0 commit comments