diff --git a/Analytics/Classes/Internal/SEGAnalyticsUtils.h b/Analytics/Classes/Internal/SEGAnalyticsUtils.h index 8168525e0..f3f8ce64f 100644 --- a/Analytics/Classes/Internal/SEGAnalyticsUtils.h +++ b/Analytics/Classes/Internal/SEGAnalyticsUtils.h @@ -8,7 +8,7 @@ NSString *GenerateUUIDString(void); // Date Utils NSString *iso8601FormattedString(NSDate *date); -void trimQueue(NSMutableArray *array, int size); +void trimQueue(NSMutableArray *array, NSUInteger size); // Async Utils dispatch_queue_t seg_dispatch_queue_create_specific(const char *label, diff --git a/Analytics/Classes/Internal/SEGAnalyticsUtils.m b/Analytics/Classes/Internal/SEGAnalyticsUtils.m index 97a7e15fb..4ccefe55b 100644 --- a/Analytics/Classes/Internal/SEGAnalyticsUtils.m +++ b/Analytics/Classes/Internal/SEGAnalyticsUtils.m @@ -26,7 +26,7 @@ } /** trim the queue so that it contains only upto `max` number of elements. */ -void trimQueue(NSMutableArray *queue, int max) +void trimQueue(NSMutableArray *queue, NSUInteger max) { if (queue.count < max) { return; diff --git a/Analytics/Classes/Internal/SEGSegmentIntegration.m b/Analytics/Classes/Internal/SEGSegmentIntegration.m index ba4c745ac..80e28d2b5 100644 --- a/Analytics/Classes/Internal/SEGSegmentIntegration.m +++ b/Analytics/Classes/Internal/SEGSegmentIntegration.m @@ -465,9 +465,8 @@ - (void)enqueueAction:(NSString *)action dictionary:(NSMutableDictionary *)paylo - (void)queuePayload:(NSDictionary *)payload { @try { - // We only queue upto 1000 items, so trim the queue to 1000-1=999 - // before we add a new element. - trimQueue(self.queue, 999); + // Trim the queue to maxQueueSize - 1 before we add a new element. + trimQueue(self.queue, self.analytics.configuration.maxQueueSize - 1); [self.queue addObject:payload]; [self persistQueue]; [self flushQueueByLength]; diff --git a/Analytics/Classes/SEGAnalyticsConfiguration.h b/Analytics/Classes/SEGAnalyticsConfiguration.h index 4e319aad1..bbdc7a815 100644 --- a/Analytics/Classes/SEGAnalyticsConfiguration.h +++ b/Analytics/Classes/SEGAnalyticsConfiguration.h @@ -69,6 +69,11 @@ typedef NSMutableURLRequest *_Nonnull (^SEGRequestFactory)(NSURL *_Nonnull); */ @property (nonatomic, assign) NSTimeInterval flushInterval; +/** + * The maximum number of items to queue before starting to drop old ones. This should be a value greater than zero, the behaviour is undefined otherwise. `1000` by default. + */ +@property (nonatomic, assign) NSUInteger maxQueueSize; + /** * Whether the analytics client should automatically make a track call for application lifecycle events, such as "Application Installed", "Application Updated" and "Application Opened". */ diff --git a/Analytics/Classes/SEGAnalyticsConfiguration.m b/Analytics/Classes/SEGAnalyticsConfiguration.m index f40525ae0..c95e0a352 100644 --- a/Analytics/Classes/SEGAnalyticsConfiguration.m +++ b/Analytics/Classes/SEGAnalyticsConfiguration.m @@ -56,6 +56,7 @@ - (instancetype)init self.shouldUseBluetooth = NO; self.flushAt = 20; self.flushInterval = 30; + self.maxQueueSize = 1000; _factories = [NSMutableArray array]; Class applicationClass = NSClassFromString(@"UIApplication"); if (applicationClass) { diff --git a/AnalyticsTests/AnalyticsTests.swift b/AnalyticsTests/AnalyticsTests.swift index 5a0fc1aef..0d93e6418 100644 --- a/AnalyticsTests/AnalyticsTests.swift +++ b/AnalyticsTests/AnalyticsTests.swift @@ -42,6 +42,7 @@ class AnalyticsTests: QuickSpec { it("initialized correctly") { expect(analytics.configuration.flushAt) == 20 expect(analytics.configuration.flushInterval) == 30 + expect(analytics.configuration.maxQueueSize) == 1000 expect(analytics.configuration.writeKey) == "QUI5ydwIGeFFTa1IvCBUhxL9PyW5B0jE" expect(analytics.configuration.shouldUseLocationServices) == false expect(analytics.configuration.enableAdvertisingTracking) == true @@ -108,6 +109,32 @@ class AnalyticsTests: QuickSpec { expect(testApplication.backgroundTasks.count).toEventually(equal(1)) expect(testApplication.backgroundTasks[0].isEnded).toEventually(beFalse()) } + + it("respects maxQueueSize") { + let max = 72 + config.maxQueueSize = UInt(max) + + for i in 1...max * 2 { + analytics.track("test #\(i)") + } + + let integration = analytics.test_integrationsManager()?.test_segmentIntegration() + expect(integration).notTo(beNil()) + + var sent = 0 + + analytics.flush() + integration?.test_dispatchBackground { + if let count = integration?.test_queue()?.count { + sent = count + } + else { + sent = -1 + } + } + + expect(sent).toEventually(be(max)) + } it("protocol conformance should not interfere with UIApplication interface") { // In Xcode8/iOS10, UIApplication.h typedefs UIBackgroundTaskIdentifier as NSUInteger, diff --git a/AnalyticsTests/Utils/TestUtils.swift b/AnalyticsTests/Utils/TestUtils.swift index edd267542..b6a7f6306 100644 --- a/AnalyticsTests/Utils/TestUtils.swift +++ b/AnalyticsTests/Utils/TestUtils.swift @@ -64,6 +64,12 @@ extension SEGSegmentIntegration { func test_batchRequest() -> URLSessionUploadTask? { return self.value(forKey: "batchRequest") as? URLSessionUploadTask } + func test_queue() -> [AnyObject]? { + return self.value(forKey: "queue") as? [AnyObject] + } + func test_dispatchBackground(block: @convention(block) () -> Void) { + self.perform(Selector(("dispatchBackground:")), with: block) + } }