diff --git a/Analytics/Classes/Integrations/SEGIntegrationsManager.m b/Analytics/Classes/Integrations/SEGIntegrationsManager.m index 6c663f62a..091569955 100644 --- a/Analytics/Classes/Integrations/SEGIntegrationsManager.m +++ b/Analytics/Classes/Integrations/SEGIntegrationsManager.m @@ -372,14 +372,14 @@ - (void)refreshSettings if (self.settingsRequest) { return; } - + self.settingsRequest = [self.httpClient settingsForWriteKey:self.configuration.writeKey completionHandler:^(BOOL success, NSDictionary *settings) { seg_dispatch_specific_async(_serialQueue, ^{ if (success) { [self setCachedSettings:settings]; } else { // Hotfix: If settings request fail, fall back to using just Segment integration - // Won't catch situation where this callback never gets called - that will get addressed separately in regular dev + // Won't catch situation where this callback never gets called - that will get addressed separately in regular dev [self setCachedSettings:@{ @"integrations": @{ @"Segment.io": @{ @"apiKey": self.configuration.writeKey }, @@ -425,14 +425,9 @@ - (BOOL)isTrackEvent:(NSString *)event enabledForIntegration:(NSString *)key inP - (void)forwardSelector:(SEL)selector arguments:(NSArray *)arguments options:(NSDictionary *)options { - // If the event has opted in for syncrhonous delivery, this may be called on any thread. - // Only allow one to be delivered at a time. - @synchronized(self) - { - [self.integrations enumerateKeysAndObjectsUsingBlock:^(NSString *key, id integration, BOOL *stop) { - [self invokeIntegration:integration key:key selector:selector arguments:arguments options:options]; - }]; - } + [self.integrations enumerateKeysAndObjectsUsingBlock:^(NSString *key, id integration, BOOL *stop) { + [self invokeIntegration:integration key:key selector:selector arguments:arguments options:options]; + }]; } - (void)invokeIntegration:(id)integration key:(NSString *)key selector:(SEL)selector arguments:(NSArray *)arguments options:(NSDictionary *)options @@ -495,11 +490,10 @@ - (void)flushMessageQueue - (void)callIntegrationsWithSelector:(SEL)selector arguments:(NSArray *)arguments options:(NSDictionary *)options sync:(BOOL)sync { - if (sync && self.initialized) { - [self forwardSelector:selector arguments:arguments options:options]; - return; - } - + // TODO: Currently we ignore the `sync` argument and queue the event asynchronously. + // For integrations that need events to be on the main thread, they'll have to do so + // manually and hop back on to the main thread. + // Eventually we should figure out a way to handle this in analytics-ios itself. seg_dispatch_specific_async(_serialQueue, ^{ if (self.initialized) { [self flushMessageQueue]; diff --git a/AnalyticsTests/AnalyticsTests.swift b/AnalyticsTests/AnalyticsTests.swift index f43baaafd..554652476 100644 --- a/AnalyticsTests/AnalyticsTests.swift +++ b/AnalyticsTests/AnalyticsTests.swift @@ -23,22 +23,22 @@ class AnalyticsTests: QuickSpec { var analytics: SEGAnalytics! var testMiddleware: TestMiddleware! var testApplication: TestApplication! - + beforeEach { testMiddleware = TestMiddleware() config.middlewares = [testMiddleware] testApplication = TestApplication() config.application = testApplication config.trackApplicationLifecycleEvents = true - + analytics = SEGAnalytics(configuration: config) analytics.test_integrationsManager()?.test_setCachedSettings(settings: cachedSettings) } - + afterEach { analytics.reset() } - + it("initialized correctly") { expect(analytics.configuration.flushAt) == 20 expect(analytics.configuration.writeKey) == "QUI5ydwIGeFFTa1IvCBUhxL9PyW5B0jE" @@ -47,30 +47,30 @@ class AnalyticsTests: QuickSpec { expect(analytics.configuration.shouldUseBluetooth) == false expect(analytics.getAnonymousId()).toNot(beNil()) } - + it("persists anonymousId") { let analytics2 = SEGAnalytics(configuration: config) expect(analytics.getAnonymousId()) == analytics2.getAnonymousId() } - + it("persists userId") { analytics.identify("testUserId1") - + let analytics2 = SEGAnalytics(configuration: config) analytics2.test_integrationsManager()?.test_setCachedSettings(settings: cachedSettings) expect(analytics.test_integrationsManager()?.test_segmentIntegration()?.test_userId()) == "testUserId1" expect(analytics2.test_integrationsManager()?.test_segmentIntegration()?.test_userId()) == "testUserId1" } - + it("continues user activity") { let activity = NSUserActivity(activityType: NSUserActivityTypeBrowsingWeb) activity.webpageURL = URL(string: "http://www.segment.com") analytics.continue(activity) let referrer = analytics.test_integrationsManager()?.test_segmentIntegration()?.test_referrer() - expect(referrer?["url"] as? String) == "http://www.segment.com" + expect(referrer?["url"] as? String).toEventually(equal("http://www.segment.com")) } - + it("clears user data") { analytics.identify("testUserId1", traits: [ "Test trait key" : "Test trait value"]) analytics.reset() @@ -90,7 +90,7 @@ class AnalyticsTests: QuickSpec { expect(event?.properties?["referring_application"] as? String) == "testApp" expect(event?.properties?["url"] as? String) == "test://test" } - + it("fires Application Opened during UIApplicationWillEnterForeground") { testMiddleware.swallowEvent = true NotificationCenter.default.post(name: .UIApplicationWillEnterForeground, object: testApplication) @@ -98,14 +98,14 @@ class AnalyticsTests: QuickSpec { expect(event?.event) == "Application Opened" expect(event?.properties?["from_background"] as? Bool) == true } - + it("flushes when UIApplicationDidEnterBackgroundNotification is fired") { analytics.track("test") NotificationCenter.default.post(name: .UIApplicationDidEnterBackground, object: testApplication) expect(testApplication.backgroundTasks.count).toEventually(equal(1)) expect(testApplication.backgroundTasks[0].isEnded).toEventually(beFalse()) } - + it("protocol conformance should not interfere with UIApplication interface") { // In Xcode8/iOS10, UIApplication.h typedefs UIBackgroundTaskIdentifier as NSUInteger, // whereas Swift has UIBackgroundTaskIdentifier typealiaed to Int.