Skip to content

Commit f798401

Browse files
authored
Added Intercom Destination Example (#73)
* Added Intercom Destination
1 parent 6739426 commit f798401

File tree

3 files changed

+256
-0
lines changed

3 files changed

+256
-0
lines changed

Examples/apps/DestinationsExample/DestinationsExample.xcodeproj/project.pbxproj

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
469F7B20266012CB0038E773 /* FlurryDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 469F7B1F266012CB0038E773 /* FlurryDestination.swift */; };
2020
469F7B23266013100038E773 /* Adjust in Frameworks */ = {isa = PBXBuildFile; productRef = 469F7B22266013100038E773 /* Adjust */; };
2121
469F7B25266013320038E773 /* AdjustDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 469F7B24266013320038E773 /* AdjustDestination.swift */; };
22+
96469A9B270279A600AC5772 /* IntercomDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96469A9A270279A600AC5772 /* IntercomDestination.swift */; };
23+
96469A9E2702862100AC5772 /* Intercom in Frameworks */ = {isa = PBXBuildFile; productRef = 96469A9D2702862100AC5772 /* Intercom */; };
2224
965DC0FA2668077400DDF9C7 /* MixpanelDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965DC0F82668077400DDF9C7 /* MixpanelDestination.swift */; };
2325
965DC0FB2668077400DDF9C7 /* AmplitudeSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965DC0F92668077400DDF9C7 /* AmplitudeSession.swift */; };
2426
965DC0FE2668079400DDF9C7 /* Mixpanel in Frameworks */ = {isa = PBXBuildFile; productRef = 965DC0FD2668079400DDF9C7 /* Mixpanel */; };
@@ -46,6 +48,7 @@
4648
469F7B152660116A0038E773 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
4749
469F7B1F266012CB0038E773 /* FlurryDestination.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlurryDestination.swift; sourceTree = "<group>"; };
4850
469F7B24266013320038E773 /* AdjustDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdjustDestination.swift; sourceTree = "<group>"; };
51+
96469A9A270279A600AC5772 /* IntercomDestination.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntercomDestination.swift; sourceTree = "<group>"; };
4952
965DC0F82668077400DDF9C7 /* MixpanelDestination.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MixpanelDestination.swift; sourceTree = "<group>"; };
5053
965DC0F92668077400DDF9C7 /* AmplitudeSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AmplitudeSession.swift; sourceTree = "<group>"; };
5154
965DC1222669947F00DDF9C7 /* FirebaseDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirebaseDestination.swift; sourceTree = "<group>"; };
@@ -69,6 +72,7 @@
6972
965DC1212669942800DDF9C7 /* FirebaseAnalytics in Frameworks */,
7073
469F7B1D266011D70038E773 /* Segment in Frameworks */,
7174
965DC0FE2668079400DDF9C7 /* Mixpanel in Frameworks */,
75+
96469A9E2702862100AC5772 /* Intercom in Frameworks */,
7276
469F7B23266013100038E773 /* Adjust in Frameworks */,
7377
);
7478
runOnlyForDeploymentPostprocessing = 0;
@@ -129,6 +133,7 @@
129133
96D8F16E26EFFA09007F8B28 /* ExampleDestination.swift */,
130134
965DC1222669947F00DDF9C7 /* FirebaseDestination.swift */,
131135
469F7B1F266012CB0038E773 /* FlurryDestination.swift */,
136+
96469A9A270279A600AC5772 /* IntercomDestination.swift */,
132137
965DC0F82668077400DDF9C7 /* MixpanelDestination.swift */,
133138
);
134139
name = destination_plugins;
@@ -168,6 +173,7 @@
168173
965DC1202669942800DDF9C7 /* FirebaseAnalytics */,
169174
BA384C9726824F3700AFEA1B /* AppsFlyerLib */,
170175
96DBF37F26FA984A00724B0B /* ComScore */,
176+
96469A9D2702862100AC5772 /* Intercom */,
171177
);
172178
productName = DestinationsExample;
173179
productReference = 469F7B04266011690038E773 /* DestinationsExample.app */;
@@ -203,6 +209,7 @@
203209
965DC11F2669942800DDF9C7 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
204210
BA384C9626824F3700AFEA1B /* XCRemoteSwiftPackageReference "AppsFlyerFramework" */,
205211
96DBF37E26FA984900724B0B /* XCRemoteSwiftPackageReference "Comscore-Swift-Package-Manager" */,
212+
96469A9C2702862100AC5772 /* XCRemoteSwiftPackageReference "intercom-ios" */,
206213
);
207214
productRefGroup = 469F7B05266011690038E773 /* Products */;
208215
projectDirPath = "";
@@ -236,6 +243,7 @@
236243
BA384C9A2682973300AFEA1B /* AppsFlyerDestination.swift in Sources */,
237244
469F7B20266012CB0038E773 /* FlurryDestination.swift in Sources */,
238245
469F7B0C266011690038E773 /* ViewController.swift in Sources */,
246+
96469A9B270279A600AC5772 /* IntercomDestination.swift in Sources */,
239247
96D8F16F26EFFA09007F8B28 /* ExampleDestination.swift in Sources */,
240248
965DC0FA2668077400DDF9C7 /* MixpanelDestination.swift in Sources */,
241249
96DBF37D26FA943300724B0B /* ComscoreDestination.swift in Sources */,
@@ -471,6 +479,14 @@
471479
version = 4.29.6;
472480
};
473481
};
482+
96469A9C2702862100AC5772 /* XCRemoteSwiftPackageReference "intercom-ios" */ = {
483+
isa = XCRemoteSwiftPackageReference;
484+
repositoryURL = "[email protected]:intercom/intercom-ios.git";
485+
requirement = {
486+
kind = upToNextMajorVersion;
487+
minimumVersion = 9.0.0;
488+
};
489+
};
474490
965DC0FC2668079400DDF9C7 /* XCRemoteSwiftPackageReference "mixpanel-swift" */ = {
475491
isa = XCRemoteSwiftPackageReference;
476492
repositoryURL = "[email protected]:mixpanel/mixpanel-swift.git";
@@ -520,6 +536,11 @@
520536
package = 469F7B21266013100038E773 /* XCRemoteSwiftPackageReference "ios_sdk" */;
521537
productName = Adjust;
522538
};
539+
96469A9D2702862100AC5772 /* Intercom */ = {
540+
isa = XCSwiftPackageProductDependency;
541+
package = 96469A9C2702862100AC5772 /* XCRemoteSwiftPackageReference "intercom-ios" */;
542+
productName = Intercom;
543+
};
523544
965DC0FD2668079400DDF9C7 /* Mixpanel */ = {
524545
isa = XCSwiftPackageProductDependency;
525546
package = 965DC0FC2668079400DDF9C7 /* XCRemoteSwiftPackageReference "mixpanel-swift" */;

Examples/destination_plugins/ComscoreDestination.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ private extension ComscoreDestination {
263263

264264

265265
// MARK: - Playback methods
266+
// MARK: -
266267

267268
func videoPlaybackStarted(event: TrackEvent, properties: JSON) {
268269
streamAnalytics = SCORStreamingAnalytics()
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
//
2+
// ComscoreDestination.swift
3+
// ComscoreDestination
4+
//
5+
// Created by Cody Garvin on 9/21/21.
6+
//
7+
8+
import Segment
9+
import Intercom
10+
import CoreMedia
11+
12+
/**
13+
An implementation of the Comscore Analytics device mode destination as a plugin.
14+
*/
15+
16+
class IntercomDestination: DestinationPlugin {
17+
let timeline = Timeline()
18+
let type = PluginType.destination
19+
let key = "Intercom"
20+
var analytics: Analytics? = nil
21+
22+
private var intercomSettings: IntercomSettings?
23+
private var configurationLabels = [String: Any]()
24+
25+
func update(settings: Settings, type: UpdateType) {
26+
// Skip if you have a singleton and don't want to keep updating via settings.
27+
guard type == .initial else { return }
28+
29+
// Grab the settings and assign them for potential later usage.
30+
// Note: Since integrationSettings is generic, strongly type the variable.
31+
guard let tempSettings: IntercomSettings = settings.integrationSettings(forPlugin: self) else { return }
32+
intercomSettings = tempSettings
33+
Intercom.setApiKey(tempSettings.mobileApiKey, forAppId: tempSettings.appId)
34+
analytics?.log(message: "Intercolm.setApiKey(\(tempSettings.mobileApiKey), forApId:\(tempSettings.appId))", kind: .debug)
35+
}
36+
37+
func identify(event: IdentifyEvent) -> IdentifyEvent? {
38+
39+
if let userId = event.userId {
40+
Intercom.registerUser(withUserId: userId)
41+
analytics?.log(message: "Intercom.registerUser(withUserId: \(userId)", kind: .debug)
42+
} else if let _ = event.anonymousId {
43+
Intercom.registerUnidentifiedUser()
44+
analytics?.log(message: "Intercom.registerUnidentifiedUser()", kind: .debug)
45+
}
46+
47+
if let integration = event.integrations?.dictionaryValue?["Intercom"] as? [AnyHashable: Any],
48+
let userHash = integration["user_hash"] as? String {
49+
Intercom.setUserHash(userHash)
50+
}
51+
52+
if let traits = event.traits?.dictionaryValue {
53+
// Set user attributes
54+
setUserAttributes(traits, event: event)
55+
}
56+
57+
return event
58+
}
59+
60+
func track(event: TrackEvent) -> TrackEvent? {
61+
62+
// Properties can not be empty
63+
guard let properties = event.properties?.dictionaryValue else {
64+
65+
Intercom.logEvent(withName: event.event)
66+
analytics?.log(message: "Intercom.logEvent(withName: \(event.event))", kind: .debug)
67+
return event
68+
}
69+
70+
var output = [String: Any]()
71+
var price = [String: Any]()
72+
var isAmountSet = false
73+
74+
for (key, value) in properties {
75+
output[key] = value
76+
77+
if let dataValue = value as? Double,
78+
key == "revenue" || key == "total" && !isAmountSet {
79+
let amountInCents = dataValue * 100
80+
price["amount"] = amountInCents
81+
output.removeValue(forKey: key)
82+
isAmountSet = true
83+
}
84+
85+
if key == "currency" {
86+
price["currency"] = value
87+
output.removeValue(forKey: "currency")
88+
}
89+
90+
if price.count > 0 {
91+
output["price"] = price
92+
}
93+
94+
if value is [String: Any] || value is [Any] {
95+
output.removeValue(forKey: key)
96+
}
97+
}
98+
99+
Intercom.logEvent(withName: event.event, metaData: output)
100+
analytics?.log(message: "Intercom.logEvent(withName: \(event.event), metaData: \(output))", kind: .debug)
101+
102+
return event
103+
}
104+
105+
func group(event: GroupEvent) -> GroupEvent? {
106+
107+
// id is required field for adding or modifying a company
108+
guard let traits = event.traits?.dictionaryValue,
109+
let groupId = event.groupId else { return event }
110+
111+
let company = setCompanyAttributes(traits)
112+
company.companyId = groupId
113+
114+
let userAttributes = ICMUserAttributes()
115+
userAttributes.companies = [company]
116+
117+
Intercom.updateUser(userAttributes)
118+
analytics?.log(message: "Intercom.updateUser(\(userAttributes))", kind: .debug)
119+
120+
return event
121+
}
122+
123+
func reset() {
124+
Intercom.logout()
125+
analytics?.log(message: "Intercom.logout()", kind: .debug)
126+
}
127+
}
128+
129+
// Example of what settings may look like.
130+
private struct IntercomSettings: Codable {
131+
let appId: String
132+
let mobileApiKey: String
133+
}
134+
135+
private extension IntercomDestination {
136+
137+
func setUserAttributes(_ traits: [String: Any], event: RawEvent?) {
138+
let userAttributes = ICMUserAttributes()
139+
var customAttributes = traits
140+
141+
if let email = traits["email"] as? String {
142+
userAttributes.email = email
143+
customAttributes.removeValue(forKey: "email")
144+
}
145+
146+
if let userId = traits["user_id"] as? String {
147+
userAttributes.userId = userId
148+
customAttributes.removeValue(forKey: "user_id")
149+
}
150+
151+
if let name = traits["name"] as? String {
152+
userAttributes.name = name
153+
customAttributes.removeValue(forKey: "name")
154+
}
155+
156+
if let phone = traits["phone"] as? String {
157+
userAttributes.phone = phone
158+
customAttributes.removeValue(forKey: "phone")
159+
}
160+
161+
if let createdAt = traits["created_at"] as? Double {
162+
let date = Date(timeIntervalSince1970: createdAt)
163+
userAttributes.signedUpAt = date
164+
customAttributes.removeValue(forKey: "created_at")
165+
}
166+
167+
if let integration = event?.integrations?.dictionaryValue?["Intercom"] as? [AnyHashable: Any] {
168+
if let languageOverride = integration["language_override"] as? String {
169+
userAttributes.languageOverride = languageOverride
170+
}
171+
172+
if let unsubscribed = integration["unsubscribed"] as? Bool {
173+
userAttributes.unsubscribedFromEmails = unsubscribed
174+
}
175+
}
176+
177+
if let company = traits["company"] as? [String: Any] {
178+
let companyData = setCompanyAttributes(company)
179+
userAttributes.companies = [companyData]
180+
}
181+
182+
for (key, value) in traits {
183+
if !(value is String) &&
184+
!(value is Int) &&
185+
!(value is Double) &&
186+
!(value is Bool) {
187+
customAttributes.removeValue(forKey: key)
188+
}
189+
}
190+
191+
userAttributes.customAttributes = customAttributes
192+
Intercom.updateUser(userAttributes)
193+
analytics?.log(message: "Intercom.updateUser(\(userAttributes)", kind: .debug)
194+
}
195+
196+
func setCompanyAttributes(_ company: [String: Any]) -> ICMCompany {
197+
let companyData = ICMCompany()
198+
var customTraits = company
199+
200+
if let companyId = company["id"] as? String {
201+
companyData.companyId = companyId
202+
customTraits.removeValue(forKey: "id")
203+
}
204+
205+
if let monthlySpending = company["monthly_spend"] as? Double {
206+
companyData.monthlySpend = NSNumber(value: monthlySpending)
207+
customTraits.removeValue(forKey: "monthly_spend")
208+
}
209+
210+
if let plan = company["plan"] as? String {
211+
companyData.plan = plan
212+
customTraits.removeValue(forKey: "plan")
213+
}
214+
215+
if let createdAt = company["created_at"] as? Double {
216+
let date = Date(timeIntervalSince1970: createdAt)
217+
companyData.createdAt = date
218+
customTraits.removeValue(forKey: "created_at")
219+
}
220+
221+
for (key, value) in company {
222+
if !(value is String) &&
223+
!(value is Int) &&
224+
!(value is Double) &&
225+
!(value is Bool) {
226+
customTraits.removeValue(forKey: key)
227+
}
228+
}
229+
230+
companyData.customAttributes = customTraits
231+
232+
return companyData
233+
}
234+
}

0 commit comments

Comments
 (0)