diff --git a/Examples/destination_plugins/AdjustDestination.swift b/Examples/destination_plugins/AdjustDestination.swift index 477e6467..fc59b4f0 100644 --- a/Examples/destination_plugins/AdjustDestination.swift +++ b/Examples/destination_plugins/AdjustDestination.swift @@ -55,7 +55,10 @@ class AdjustDestination: NSObject, DestinationPlugin, RemoteNotifications { internal var settings: AdjustSettings? = nil - public func update(settings: Settings) { + public func update(settings: Settings, type: UpdateType) { + // we've already set up this singleton SDK, can't do it again, so skip. + guard type == .initial else { return } + guard let settings: AdjustSettings = settings.integrationSettings(forPlugin: self) else { return } self.settings = settings diff --git a/Examples/destination_plugins/AmplitudeSession.swift b/Examples/destination_plugins/AmplitudeSession.swift index 9ce3129e..318c0c7d 100644 --- a/Examples/destination_plugins/AmplitudeSession.swift +++ b/Examples/destination_plugins/AmplitudeSession.swift @@ -52,7 +52,7 @@ class AmplitudeSession: EventPlugin, iOSLifecycle { private var sessionID: TimeInterval? private let fireTime = TimeInterval(300) - func update(settings: Settings) { + func update(settings: Settings, type: UpdateType) { if settings.isDestinationEnabled(key: key) { active = true } else { diff --git a/Examples/destination_plugins/AppsFlyerDestination.swift b/Examples/destination_plugins/AppsFlyerDestination.swift index 228e6837..9733e3b3 100644 --- a/Examples/destination_plugins/AppsFlyerDestination.swift +++ b/Examples/destination_plugins/AppsFlyerDestination.swift @@ -54,7 +54,10 @@ class AppsFlyerDestination: UIResponder, DestinationPlugin { fileprivate var settings: AppsFlyerSettings? = nil - public func update(settings: Settings) { + public func update(settings: Settings, type: UpdateType) { + // we've already set up this singleton SDK, can't do it again, so skip. + guard type == .initial else { return } + guard let settings: AppsFlyerSettings = settings.integrationSettings(forPlugin: self) else { return } self.settings = settings diff --git a/Examples/destination_plugins/FirebaseDestination.swift b/Examples/destination_plugins/FirebaseDestination.swift index bdbf93fb..0cb836da 100644 --- a/Examples/destination_plugins/FirebaseDestination.swift +++ b/Examples/destination_plugins/FirebaseDestination.swift @@ -53,7 +53,10 @@ class FirebaseDestination: DestinationPlugin { let key = "Firebase" var analytics: Segment.Analytics? = nil - func update(settings: Settings) { + func update(settings: Settings, type: UpdateType) { + // we've already set up this singleton SDK, can't do it again, so skip. + guard type == .initial else { return } + guard let firebaseSettings: FirebaseSettings = settings.integrationSettings(forPlugin: self) else { return } if let deepLinkURLScheme = firebaseSettings.deepLinkURLScheme { FirebaseOptions.defaultOptions()?.deepLinkURLScheme = deepLinkURLScheme diff --git a/Examples/destination_plugins/FlurryDestination.swift b/Examples/destination_plugins/FlurryDestination.swift index 35584f4c..09bff30a 100644 --- a/Examples/destination_plugins/FlurryDestination.swift +++ b/Examples/destination_plugins/FlurryDestination.swift @@ -57,7 +57,10 @@ class FlurryDestination: DestinationPlugin { var screenTracksEvents = false - func update(settings: Settings) { + func update(settings: Settings, type: UpdateType) { + // we've already set up this singleton SDK, can't do it again, so skip. + guard type == .initial else { return } + guard let flurrySettings: FlurrySettings = settings.integrationSettings(forPlugin: self) else { return } let builder = FlurrySessionBuilder() diff --git a/Examples/destination_plugins/MixpanelDestination.swift b/Examples/destination_plugins/MixpanelDestination.swift index 1dd8d66e..b1722cc8 100644 --- a/Examples/destination_plugins/MixpanelDestination.swift +++ b/Examples/destination_plugins/MixpanelDestination.swift @@ -47,7 +47,9 @@ class MixpanelDestination: DestinationPlugin, RemoteNotifications { private var mixpanel: MixpanelInstance? = nil private var settings: [String: Any]? = nil - func update(settings: Settings) { + func update(settings: Settings, type: UpdateType) { + // we've already set up this singleton SDK, can't do it again, so skip. + guard type == .initial else { return } // If we have a mixpanel instance, dump all the data first mixpanel?.flush() diff --git a/README.md b/README.md index 5cd4da44..31d9e980 100644 --- a/README.md +++ b/README.md @@ -301,18 +301,19 @@ class AppsFlyerDestination: UIResponder, DestinationPlugin, UserActivities, Remo analytics?.track(name: "AppsFlyer Loaded") } - public func update(settings: Settings) { - - guard let settings: AppsFlyerSettings = settings.integrationSettings(name: "AppsFlyer") else {return} - self.settings = settings + public func update(settings: Settings, type: UpdateType) { + if type == .initial { + // AppsFlyerLib is a singleton, we only want to set it up once. + guard let settings: AppsFlyerSettings = settings.integrationSettings(name: "AppsFlyer") else {return} + self.settings = settings + + AppsFlyerLib.shared().appsFlyerDevKey = settings.appsFlyerDevKey + AppsFlyerLib.shared().appleAppID = settings.appleAppID + AppsFlyerLib.shared().isDebug = true + AppsFlyerLib.shared().deepLinkDelegate = self + } - - AppsFlyerLib.shared().appsFlyerDevKey = settings.appsFlyerDevKey - AppsFlyerLib.shared().appleAppID = settings.appleAppID - AppsFlyerLib.shared().isDebug = true - AppsFlyerLib.shared().deepLinkDelegate = self - - // additional update logic + // additional update logic } // ... diff --git a/Segment.xcodeproj/project.pbxproj b/Segment.xcodeproj/project.pbxproj index 980d71b6..13308e01 100644 --- a/Segment.xcodeproj/project.pbxproj +++ b/Segment.xcodeproj/project.pbxproj @@ -121,6 +121,16 @@ 46A018D325E6C9C200F9CCD8 /* LinuxUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinuxUtils.swift; sourceTree = ""; }; 46A018D925E97FDF00F9CCD8 /* AppleUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleUtils.swift; sourceTree = ""; }; 46A018ED25E9A74F00F9CCD8 /* VendorSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VendorSystem.swift; sourceTree = ""; }; + 46D98E3D26D6FEF300E7A86A /* FlurryDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlurryDestination.swift; sourceTree = ""; }; + 46D98E3E26D6FEF300E7A86A /* AdjustDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdjustDestination.swift; sourceTree = ""; }; + 46D98E3F26D6FEF300E7A86A /* MixpanelDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MixpanelDestination.swift; sourceTree = ""; }; + 46D98E4026D6FEF300E7A86A /* AppsFlyerDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppsFlyerDestination.swift; sourceTree = ""; }; + 46D98E4126D6FEF300E7A86A /* FirebaseDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirebaseDestination.swift; sourceTree = ""; }; + 46D98E4226D6FEF300E7A86A /* AmplitudeSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AmplitudeSession.swift; sourceTree = ""; }; + 46D98E4426D6FEF300E7A86A /* UIKitScreenTracking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitScreenTracking.swift; sourceTree = ""; }; + 46D98E4526D6FEF300E7A86A /* ConsentTracking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsentTracking.swift; sourceTree = ""; }; + 46D98E4626D6FEF300E7A86A /* IDFACollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IDFACollection.swift; sourceTree = ""; }; + 46D98E4726D6FEF300E7A86A /* ConsoleLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleLogger.swift; sourceTree = ""; }; 46E382E62654429A00BA2502 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; 46F7485B26C718710042798E /* ObjCAnalytics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjCAnalytics.swift; sourceTree = ""; }; 46F7485C26C718710042798E /* ObjCConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjCConfiguration.swift; sourceTree = ""; }; @@ -217,6 +227,41 @@ path = Vendors; sourceTree = ""; }; + 46D98E3B26D6FED300E7A86A /* Examples */ = { + isa = PBXGroup; + children = ( + 46D98E3C26D6FEF300E7A86A /* destination_plugins */, + 46D98E4326D6FEF300E7A86A /* other_plugins */, + ); + name = Examples; + sourceTree = ""; + }; + 46D98E3C26D6FEF300E7A86A /* destination_plugins */ = { + isa = PBXGroup; + children = ( + 46D98E3D26D6FEF300E7A86A /* FlurryDestination.swift */, + 46D98E3E26D6FEF300E7A86A /* AdjustDestination.swift */, + 46D98E3F26D6FEF300E7A86A /* MixpanelDestination.swift */, + 46D98E4026D6FEF300E7A86A /* AppsFlyerDestination.swift */, + 46D98E4126D6FEF300E7A86A /* FirebaseDestination.swift */, + 46D98E4226D6FEF300E7A86A /* AmplitudeSession.swift */, + ); + name = destination_plugins; + path = Examples/destination_plugins; + sourceTree = ""; + }; + 46D98E4326D6FEF300E7A86A /* other_plugins */ = { + isa = PBXGroup; + children = ( + 46D98E4426D6FEF300E7A86A /* UIKitScreenTracking.swift */, + 46D98E4526D6FEF300E7A86A /* ConsentTracking.swift */, + 46D98E4626D6FEF300E7A86A /* IDFACollection.swift */, + 46D98E4726D6FEF300E7A86A /* ConsoleLogger.swift */, + ); + name = other_plugins; + path = Examples/other_plugins; + sourceTree = ""; + }; 96208624256DC23F00314F8D /* Frameworks */ = { isa = PBXGroup; children = ( @@ -330,6 +375,7 @@ OBJ_5 = { isa = PBXGroup; children = ( + 46D98E3B26D6FED300E7A86A /* Examples */, OBJ_7 /* Sources */, OBJ_10 /* Tests */, OBJ_6 /* Package.swift */, diff --git a/Sources/Segment/Plugins.swift b/Sources/Segment/Plugins.swift index fabd9bc9..421a8331 100644 --- a/Sources/Segment/Plugins.swift +++ b/Sources/Segment/Plugins.swift @@ -23,12 +23,17 @@ public enum PluginType: Int, CaseIterable { case utility } +public enum UpdateType { + case initial + case refresh +} + public protocol Plugin: AnyObject { var type: PluginType { get } var analytics: Analytics? { get set } func configure(analytics: Analytics) - func update(settings: Settings) + func update(settings: Settings, type: UpdateType) func execute(event: T?) -> T? func shutdown() } diff --git a/Sources/Segment/Plugins/SegmentDestination.swift b/Sources/Segment/Plugins/SegmentDestination.swift index 0ca7776d..d3c9ceb4 100644 --- a/Sources/Segment/Plugins/SegmentDestination.swift +++ b/Sources/Segment/Plugins/SegmentDestination.swift @@ -59,7 +59,7 @@ public class SegmentDestination: DestinationPlugin { } } - public func update(settings: Settings) { + public func update(settings: Settings, type: UpdateType) { let segmentInfo = settings.integrationSettings(forKey: self.key) apiKey = segmentInfo?[Self.Constants.apiKey.rawValue] as? String apiHost = segmentInfo?[Self.Constants.apiHost.rawValue] as? String diff --git a/Sources/Segment/Settings.swift b/Sources/Segment/Settings.swift index 31c7b2c4..39e631fc 100644 --- a/Sources/Segment/Settings.swift +++ b/Sources/Segment/Settings.swift @@ -86,19 +86,19 @@ extension Settings: Equatable { } extension Analytics { - internal func update(settings: Settings) { + internal func update(settings: Settings, type: UpdateType) { apply { (plugin) in // tell all top level plugins to update. - update(plugin: plugin, settings: settings) + update(plugin: plugin, settings: settings, type: type) } } - internal func update(plugin: Plugin, settings: Settings) { - plugin.update(settings: settings) + internal func update(plugin: Plugin, settings: Settings, type: UpdateType) { + plugin.update(settings: settings, type: type) // if it's a destination, tell it's plugins to update as well. if let dest = plugin as? DestinationPlugin { dest.apply { (subPlugin) in - subPlugin.update(settings: settings) + subPlugin.update(settings: settings, type: type) } } } @@ -121,6 +121,10 @@ extension Analytics { let writeKey = self.configuration.values.writeKey let httpClient = HTTPClient(analytics: self, cdnHost: configuration.values.cdnHost) + let systemState: System? = store.currentState() + let hasSettings = (systemState?.settings?.integrations != nil && systemState?.settings?.plan != nil) + let updateType = (hasSettings ? UpdateType.refresh : UpdateType.initial) + // stop things; queue in case our settings have changed. store.dispatch(action: System.ToggleRunningAction(running: false)) httpClient.settingsFor(writeKey: writeKey) { (success, settings) in @@ -130,7 +134,7 @@ extension Analytics { // this will cause them to be cached. self.store.dispatch(action: System.UpdateSettingsAction(settings: s)) // let plugins know we just received some settings.. - self.update(settings: s) + self.update(settings: s, type: updateType) } } // we're good to go back to a running state. diff --git a/Sources/Segment/Timeline.swift b/Sources/Segment/Timeline.swift index 7f84ad28..0adaf092 100644 --- a/Sources/Segment/Timeline.swift +++ b/Sources/Segment/Timeline.swift @@ -56,7 +56,7 @@ internal class Mediator { internal func add(plugin: Plugin) { plugins.append(plugin) if let settings = plugin.analytics?.settings() { - plugin.update(settings: settings) + plugin.update(settings: settings, type: .initial) } } @@ -139,7 +139,7 @@ extension Plugin { return event } - public func update(settings: Settings) { + public func update(settings: Settings, type: UpdateType) { // do nothing by default, user can override. } diff --git a/Tests/Segment-Tests/Support/TestUtilities.swift b/Tests/Segment-Tests/Support/TestUtilities.swift index b3731060..6d231516 100644 --- a/Tests/Segment-Tests/Support/TestUtilities.swift +++ b/Tests/Segment-Tests/Support/TestUtilities.swift @@ -82,7 +82,7 @@ class MyDestination: DestinationPlugin { self.trackCompletion = trackCompletion } - func update(settings: Settings) { + func update(settings: Settings, type: UpdateType) { // }