Skip to content

Commit c09663c

Browse files
bsneedBrandon Sneed
andauthored
Integration enable/disable refactor (#92)
* Integration enable/disable refactor * Addresses critique in PR * Made process public and added documentation. * Added key version of hasIntegrationSettings. * Fixups in amplitude session plugin. * Address feedback from Pray * temp fix for filehandle issue. Co-authored-by: Brandon Sneed <[email protected]>
1 parent 067fd77 commit c09663c

File tree

14 files changed

+125
-116
lines changed

14 files changed

+125
-116
lines changed

Examples/destination_plugins/AmplitudeSession.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class AmplitudeSession: EventPlugin, iOSLifecycle {
5353
private let fireTime = TimeInterval(300)
5454

5555
func update(settings: Settings, type: UpdateType) {
56-
if settings.isDestinationEnabled(key: key) {
56+
if settings.hasIntegrationSettings(key: key) {
5757
active = true
5858
} else {
5959
active = false

Segment.xcodeproj/project.pbxproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@
502502
attributes = {
503503
LastSwiftMigration = 9999;
504504
LastSwiftUpdateCheck = 1220;
505-
LastUpgradeCheck = 1250;
505+
LastUpgradeCheck = 1310;
506506
};
507507
buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "Segment" */;
508508
compatibilityVersion = "Xcode 3.2";

Segment.xcodeproj/xcshareddata/xcschemes/Segment-Package.xcscheme

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<Scheme
3-
LastUpgradeVersion = "1250"
3+
LastUpgradeVersion = "1310"
44
version = "1.3">
55
<BuildAction
66
parallelizeBuildables = "YES"

Sources/Segment/Analytics.swift

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ public class Analytics {
2424

2525
public var timeline: Timeline
2626

27+
/// Initialize this instance of Analytics with a given configuration setup.
28+
/// - Parameters:
29+
/// - configuration: The configuration to use
2730
public init(configuration: Configuration) {
2831
self.configuration = configuration
2932

@@ -44,6 +47,9 @@ public class Analytics {
4447
_ = timeline.process(incomingEvent: event)
4548
}
4649

50+
/// Process a raw event through the system. Useful when one needs to queue and replay events at a later time.
51+
/// - Parameters:
52+
/// - event: An event conforming to RawEvent that will be processed.
4753
public func process(event: RawEvent) {
4854
switch event {
4955
case let e as TrackEvent:
@@ -65,27 +71,32 @@ public class Analytics {
6571
// MARK: - System Modifiers
6672

6773
extension Analytics {
74+
/// Returns the anonymousId currently in use.
6875
public var anonymousId: String {
6976
if let userInfo: UserInfo = store.currentState() {
7077
return userInfo.anonymousId
7178
}
7279
return ""
7380
}
7481

82+
/// Returns the userId that was specified in the last identify call.
7583
public var userId: String? {
7684
if let userInfo: UserInfo = store.currentState() {
7785
return userInfo.userId
7886
}
7987
return nil
8088
}
8189

90+
/// Returns the traits that were specified in the last identify call.
8291
public func traits<T: Codable>() -> T? {
8392
if let userInfo: UserInfo = store.currentState() {
8493
return userInfo.traits?.codableValue()
8594
}
8695
return nil
8796
}
8897

98+
/// Tells this instance of Analytics to flush any queued events up to Segment.com. This command will also
99+
/// be sent to each plugin present in the system.
89100
public func flush() {
90101
apply { plugin in
91102
if let p = plugin as? EventPlugin {
@@ -94,6 +105,8 @@ extension Analytics {
94105
}
95106
}
96107

108+
/// Resets this instance of Analytics to a clean slate. Traits, UserID's, anonymousId, etc are all cleared or reset. This
109+
/// command will also be sent to each plugin present in the system.
97110
public func reset() {
98111
store.dispatch(action: UserInfo.ResetAction())
99112
apply { plugin in
@@ -103,6 +116,22 @@ extension Analytics {
103116
}
104117
}
105118

119+
/// Retrieve the version of this library in use.
120+
/// - Returns: A string representing the version in "BREAKING.FEATURE.FIX" format.
121+
public func version() -> String {
122+
return Analytics.version()
123+
}
124+
125+
/// Retrieve the version of this library in use.
126+
/// - Returns: A string representing the version in "BREAKING.FEATURE.FIX" format.
127+
public static func version() -> String {
128+
return __segment_version
129+
}
130+
}
131+
132+
extension Analytics {
133+
/// Manually retrieve the settings that were supplied from Segment.com.
134+
/// - Returns: A Settings object containing integration settings, tracking plan, etc.
106135
public func settings() -> Settings? {
107136
var settings: Settings?
108137
if let system: System = store.currentState() {
@@ -111,11 +140,12 @@ extension Analytics {
111140
return settings
112141
}
113142

114-
public func version() -> String {
115-
return Analytics.version()
116-
}
117-
118-
public static func version() -> String {
119-
return __segment_version
143+
/// Manually enable a destination plugin. This is useful when a given DestinationPlugin doesn't have any Segment tie-ins at all.
144+
/// This will allow the destination to be processed in the same way within this library.
145+
/// - Parameters:
146+
/// - plugin: The destination plugin to enable.
147+
public func manuallyEnableDestination(plugin: DestinationPlugin) {
148+
self.store.dispatch(action: System.AddDestinationToSettingsAction(key: plugin.key))
120149
}
150+
121151
}

Sources/Segment/Plugins.swift

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,6 @@ extension Analytics {
140140
public func add(plugin: Plugin) -> Plugin {
141141
plugin.configure(analytics: self)
142142
timeline.add(plugin: plugin)
143-
if !(plugin is SegmentDestination), let destPlugin = plugin as? DestinationPlugin {
144-
// need to maintain the list of integrations to inject into payload
145-
store.dispatch(action: System.AddIntegrationAction(key: destPlugin.key))
146-
}
147-
148143
return plugin
149144
}
150145

@@ -155,9 +150,6 @@ extension Analytics {
155150
*/
156151
public func remove(plugin: Plugin) {
157152
timeline.remove(plugin: plugin)
158-
if !(plugin is SegmentDestination), let destPlugin = plugin as? DestinationPlugin {
159-
store.dispatch(action: System.RemoveIntegrationAction(key: destPlugin.key))
160-
}
161153
}
162154

163155
public func find<T: Plugin>(pluginType: T.Type) -> T? {

Sources/Segment/Plugins/SegmentDestination.swift

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -70,29 +70,13 @@ public class SegmentDestination: DestinationPlugin {
7070
}
7171

7272
// MARK: - Event Handling Methods
73-
public func identify(event: IdentifyEvent) -> IdentifyEvent? {
74-
queueEvent(event: event)
75-
return event
76-
}
77-
78-
public func track(event: TrackEvent) -> TrackEvent? {
79-
queueEvent(event: event)
80-
return event
81-
}
82-
83-
public func screen(event: ScreenEvent) -> ScreenEvent? {
84-
queueEvent(event: event)
85-
return event
86-
}
87-
88-
public func alias(event: AliasEvent) -> AliasEvent? {
89-
queueEvent(event: event)
90-
return event
91-
}
92-
93-
public func group(event: GroupEvent) -> GroupEvent? {
94-
queueEvent(event: event)
95-
return event
73+
public func execute<T: RawEvent>(event: T?) -> T? {
74+
let result: T? = event
75+
if let r = result {
76+
let modified = configureCloudDestinations(event: r)
77+
queueEvent(event: modified)
78+
}
79+
return result
9680
}
9781

9882
// MARK: - Abstracted Lifecycle Methods
@@ -156,6 +140,37 @@ public class SegmentDestination: DestinationPlugin {
156140
}
157141
}
158142

143+
// MARK: - Utility methods
144+
extension SegmentDestination {
145+
internal func configureCloudDestinations<T: RawEvent>(event: T) -> T {
146+
guard let integrationSettings = analytics?.settings() else { return event }
147+
guard let plugins = analytics?.timeline.plugins[.destination]?.plugins as? [DestinationPlugin] else { return event }
148+
guard let customerValues = event.integrations?.dictionaryValue else { return event }
149+
150+
var merged = [String: Any]()
151+
152+
// compare settings to loaded plugins
153+
for plugin in plugins {
154+
let hasSettings = integrationSettings.hasIntegrationSettings(forPlugin: plugin)
155+
if hasSettings {
156+
// we have a device mode plugin installed.
157+
// tell segment not to send it via cloud mode.
158+
merged[plugin.key] = false
159+
}
160+
}
161+
162+
// apply customer values; the customer is always right!
163+
for (key, value) in customerValues {
164+
merged[key] = value
165+
}
166+
167+
var modified = event
168+
modified.integrations = try? JSON(merged)
169+
170+
return modified
171+
}
172+
}
173+
159174
// MARK: - Upload management
160175

161176
extension SegmentDestination {

Sources/Segment/Settings.swift

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,13 @@ public struct Settings: Codable {
6868
return integrationSettings(forKey: plugin.key)
6969
}
7070

71-
public func isDestinationEnabled(key: String) -> Bool {
71+
public func hasIntegrationSettings(forPlugin plugin: DestinationPlugin) -> Bool {
72+
return hasIntegrationSettings(key: plugin.key)
73+
}
74+
75+
public func hasIntegrationSettings(key: String) -> Bool {
7276
guard let settings = integrations?.dictionaryValue else { return false }
73-
if settings.keys.contains(key) {
74-
return true
75-
}
76-
return false
77+
return (settings[key] != nil)
7778
}
7879
}
7980

@@ -85,15 +86,7 @@ extension Settings: Equatable {
8586
}
8687
}
8788

88-
extension Analytics {
89-
/// Manually enable a destination plugin. This is useful when a given DestinationPlugin doesn't have any Segment tie-ins at all.
90-
/// This will allow the destination to be processed in the same way within this library.
91-
/// - Parameters:
92-
/// - plugin: The destination plugin to enable.
93-
public func manuallyEnableDestination(plugin: DestinationPlugin) {
94-
self.store.dispatch(action: System.AddIntegrationAction(key: plugin.key))
95-
}
96-
89+
extension Analytics {
9790
internal func update(settings: Settings, type: UpdateType) {
9891
apply { (plugin) in
9992
// tell all top level plugins to update.

Sources/Segment/State.swift

Lines changed: 14 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import Sovran
1212

1313
struct System: State {
1414
let configuration: Configuration
15-
let integrations: JSON?
1615
let settings: Settings?
1716
let running: Bool
1817

@@ -21,60 +20,34 @@ struct System: State {
2120

2221
func reduce(state: System) -> System {
2322
let result = System(configuration: state.configuration,
24-
integrations: state.integrations,
2523
settings: settings,
2624
running: state.running)
2725
return result
2826
}
2927
}
30-
31-
struct AddIntegrationAction: Action {
32-
let key: String
28+
29+
struct ToggleRunningAction: Action {
30+
let running: Bool
3331

3432
func reduce(state: System) -> System {
35-
// we need to set any destination plugins to false in the
36-
// integrations payload. this prevents them from being sent
37-
// by segment.com once an event reaches segment.
38-
if var integrations = state.integrations?.dictionaryValue {
39-
integrations[key] = false
40-
if let jsonIntegrations = try? JSON(integrations) {
41-
let result = System(configuration: state.configuration,
42-
integrations: jsonIntegrations,
43-
settings: state.settings,
44-
running: state.running)
45-
return result
46-
}
47-
}
48-
return state
33+
return System(configuration: state.configuration,
34+
settings: state.settings,
35+
running: running)
4936
}
5037
}
5138

52-
struct RemoveIntegrationAction: Action {
39+
struct AddDestinationToSettingsAction: Action {
5340
let key: String
5441

5542
func reduce(state: System) -> System {
56-
if var integrations = state.integrations?.dictionaryValue {
57-
integrations.removeValue(forKey: key)
58-
if let jsonIntegrations = try? JSON(integrations) {
59-
let result = System(configuration: state.configuration,
60-
integrations: jsonIntegrations,
61-
settings: state.settings,
62-
running: state.running)
63-
return result
64-
}
43+
var settings = state.settings
44+
if var integrations = settings?.integrations?.dictionaryValue {
45+
integrations[key] = true
46+
settings?.integrations = try? JSON(integrations)
6547
}
66-
return state
67-
}
68-
}
69-
70-
struct ToggleRunningAction: Action {
71-
let running: Bool
72-
73-
func reduce(state: System) -> System {
7448
return System(configuration: state.configuration,
75-
integrations: state.integrations,
76-
settings: state.settings,
77-
running: running)
49+
settings: settings,
50+
running: state.running)
7851
}
7952
}
8053
}
@@ -139,8 +112,7 @@ extension System {
139112
settings = Settings(writeKey: configuration.values.writeKey, apiHost: HTTPClient.getDefaultAPIHost())
140113
}
141114
}
142-
let integrationDictionary = try! JSON([String: Any]())
143-
return System(configuration: configuration, integrations: integrationDictionary, settings: settings, running: false)
115+
return System(configuration: configuration, settings: settings, running: false)
144116
}
145117
}
146118

Sources/Segment/Timeline.swift

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -210,18 +210,28 @@ extension DestinationPlugin {
210210
}
211211
return result
212212
}
213+
214+
internal func isDestinationEnabled(event: RawEvent) -> Bool {
215+
var customerDisabled = false
216+
if let disabled: Bool = event.integrations?.value(forKeyPath: KeyPath(self.key)), disabled == false {
217+
customerDisabled = true
218+
}
219+
220+
var hasSettings = false
221+
if let settings = analytics?.settings() {
222+
hasSettings = settings.hasIntegrationSettings(forPlugin: self)
223+
}
224+
225+
return (hasSettings == true && customerDisabled == false)
226+
}
213227

214228
internal func process<E: RawEvent>(incomingEvent: E) -> E? {
215229
// This will process plugins (think destination middleware) that are tied
216230
// to this destination.
217231

218232
var result: E? = nil
219233

220-
// For destination plugins, we will always have some kind of `settings`,
221-
// and if we don't, it means this destination hasn't been setup on app.segment.com,
222-
// which in turn ALSO means that we shouldn't be sending events to it.
223-
224-
if let enabled = analytics?.settings()?.isDestinationEnabled(key: self.key), enabled == true {
234+
if isDestinationEnabled(event: incomingEvent) {
225235
// apply .before and .enrichment types first ...
226236
let beforeResult = timeline.applyPlugins(type: .before, event: incomingEvent)
227237
let enrichmentResult = timeline.applyPlugins(type: .enrichment, event: beforeResult)

0 commit comments

Comments
 (0)