Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
469F7B20266012CB0038E773 /* FlurryDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 469F7B1F266012CB0038E773 /* FlurryDestination.swift */; };
469F7B23266013100038E773 /* Adjust in Frameworks */ = {isa = PBXBuildFile; productRef = 469F7B22266013100038E773 /* Adjust */; };
469F7B25266013320038E773 /* AdjustDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 469F7B24266013320038E773 /* AdjustDestination.swift */; };
96469A9B270279A600AC5772 /* IntercomDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96469A9A270279A600AC5772 /* IntercomDestination.swift */; };
96469A9E2702862100AC5772 /* Intercom in Frameworks */ = {isa = PBXBuildFile; productRef = 96469A9D2702862100AC5772 /* Intercom */; };
965DC0FA2668077400DDF9C7 /* MixpanelDestination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965DC0F82668077400DDF9C7 /* MixpanelDestination.swift */; };
965DC0FB2668077400DDF9C7 /* AmplitudeSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965DC0F92668077400DDF9C7 /* AmplitudeSession.swift */; };
965DC0FE2668079400DDF9C7 /* Mixpanel in Frameworks */ = {isa = PBXBuildFile; productRef = 965DC0FD2668079400DDF9C7 /* Mixpanel */; };
Expand Down Expand Up @@ -46,6 +48,7 @@
469F7B152660116A0038E773 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
469F7B1F266012CB0038E773 /* FlurryDestination.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlurryDestination.swift; sourceTree = "<group>"; };
469F7B24266013320038E773 /* AdjustDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdjustDestination.swift; sourceTree = "<group>"; };
96469A9A270279A600AC5772 /* IntercomDestination.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntercomDestination.swift; sourceTree = "<group>"; };
965DC0F82668077400DDF9C7 /* MixpanelDestination.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MixpanelDestination.swift; sourceTree = "<group>"; };
965DC0F92668077400DDF9C7 /* AmplitudeSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AmplitudeSession.swift; sourceTree = "<group>"; };
965DC1222669947F00DDF9C7 /* FirebaseDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirebaseDestination.swift; sourceTree = "<group>"; };
Expand All @@ -69,6 +72,7 @@
965DC1212669942800DDF9C7 /* FirebaseAnalytics in Frameworks */,
469F7B1D266011D70038E773 /* Segment in Frameworks */,
965DC0FE2668079400DDF9C7 /* Mixpanel in Frameworks */,
96469A9E2702862100AC5772 /* Intercom in Frameworks */,
469F7B23266013100038E773 /* Adjust in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -129,6 +133,7 @@
96D8F16E26EFFA09007F8B28 /* ExampleDestination.swift */,
965DC1222669947F00DDF9C7 /* FirebaseDestination.swift */,
469F7B1F266012CB0038E773 /* FlurryDestination.swift */,
96469A9A270279A600AC5772 /* IntercomDestination.swift */,
965DC0F82668077400DDF9C7 /* MixpanelDestination.swift */,
);
name = destination_plugins;
Expand Down Expand Up @@ -168,6 +173,7 @@
965DC1202669942800DDF9C7 /* FirebaseAnalytics */,
BA384C9726824F3700AFEA1B /* AppsFlyerLib */,
96DBF37F26FA984A00724B0B /* ComScore */,
96469A9D2702862100AC5772 /* Intercom */,
);
productName = DestinationsExample;
productReference = 469F7B04266011690038E773 /* DestinationsExample.app */;
Expand Down Expand Up @@ -203,6 +209,7 @@
965DC11F2669942800DDF9C7 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
BA384C9626824F3700AFEA1B /* XCRemoteSwiftPackageReference "AppsFlyerFramework" */,
96DBF37E26FA984900724B0B /* XCRemoteSwiftPackageReference "Comscore-Swift-Package-Manager" */,
96469A9C2702862100AC5772 /* XCRemoteSwiftPackageReference "intercom-ios" */,
);
productRefGroup = 469F7B05266011690038E773 /* Products */;
projectDirPath = "";
Expand Down Expand Up @@ -236,6 +243,7 @@
BA384C9A2682973300AFEA1B /* AppsFlyerDestination.swift in Sources */,
469F7B20266012CB0038E773 /* FlurryDestination.swift in Sources */,
469F7B0C266011690038E773 /* ViewController.swift in Sources */,
96469A9B270279A600AC5772 /* IntercomDestination.swift in Sources */,
96D8F16F26EFFA09007F8B28 /* ExampleDestination.swift in Sources */,
965DC0FA2668077400DDF9C7 /* MixpanelDestination.swift in Sources */,
96DBF37D26FA943300724B0B /* ComscoreDestination.swift in Sources */,
Expand Down Expand Up @@ -471,6 +479,14 @@
version = 4.29.6;
};
};
96469A9C2702862100AC5772 /* XCRemoteSwiftPackageReference "intercom-ios" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "[email protected]:intercom/intercom-ios.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 9.0.0;
};
};
965DC0FC2668079400DDF9C7 /* XCRemoteSwiftPackageReference "mixpanel-swift" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "[email protected]:mixpanel/mixpanel-swift.git";
Expand Down Expand Up @@ -520,6 +536,11 @@
package = 469F7B21266013100038E773 /* XCRemoteSwiftPackageReference "ios_sdk" */;
productName = Adjust;
};
96469A9D2702862100AC5772 /* Intercom */ = {
isa = XCSwiftPackageProductDependency;
package = 96469A9C2702862100AC5772 /* XCRemoteSwiftPackageReference "intercom-ios" */;
productName = Intercom;
};
965DC0FD2668079400DDF9C7 /* Mixpanel */ = {
isa = XCSwiftPackageProductDependency;
package = 965DC0FC2668079400DDF9C7 /* XCRemoteSwiftPackageReference "mixpanel-swift" */;
Expand Down
1 change: 1 addition & 0 deletions Examples/destination_plugins/ComscoreDestination.swift
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ private extension ComscoreDestination {


// MARK: - Playback methods
// MARK: -

func videoPlaybackStarted(event: TrackEvent, properties: JSON) {
streamAnalytics = SCORStreamingAnalytics()
Expand Down
234 changes: 234 additions & 0 deletions Examples/destination_plugins/IntercomDestination.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
//
// ComscoreDestination.swift
// ComscoreDestination
//
// Created by Cody Garvin on 9/21/21.
//

import Segment
import Intercom
import CoreMedia

/**
An implementation of the Comscore Analytics device mode destination as a plugin.
*/

class IntercomDestination: DestinationPlugin {
let timeline = Timeline()
let type = PluginType.destination
let key = "Intercom"
var analytics: Analytics? = nil

private var intercomSettings: IntercomSettings?
private var configurationLabels = [String: Any]()

func update(settings: Settings, type: UpdateType) {
// Skip if you have a singleton and don't want to keep updating via settings.
guard type == .initial else { return }

// Grab the settings and assign them for potential later usage.
// Note: Since integrationSettings is generic, strongly type the variable.
guard let tempSettings: IntercomSettings = settings.integrationSettings(forPlugin: self) else { return }
intercomSettings = tempSettings
Intercom.setApiKey(tempSettings.mobileApiKey, forAppId: tempSettings.appId)
analytics?.log(message: "Intercolm.setApiKey(\(tempSettings.mobileApiKey), forApId:\(tempSettings.appId))", kind: .debug)
}

func identify(event: IdentifyEvent) -> IdentifyEvent? {

if let userId = event.userId {
Intercom.registerUser(withUserId: userId)
analytics?.log(message: "Intercom.registerUser(withUserId: \(userId)", kind: .debug)
} else if let _ = event.anonymousId {
Intercom.registerUnidentifiedUser()
analytics?.log(message: "Intercom.registerUnidentifiedUser()", kind: .debug)
}

if let integration = event.integrations?.dictionaryValue?["Intercom"] as? [AnyHashable: Any],
let userHash = integration["user_hash"] as? String {
Intercom.setUserHash(userHash)
}

if let traits = event.traits?.dictionaryValue {
// Set user attributes
setUserAttributes(traits, event: event)
}

return event
}

func track(event: TrackEvent) -> TrackEvent? {

// Properties can not be empty
guard let properties = event.properties?.dictionaryValue else {

Intercom.logEvent(withName: event.event)
analytics?.log(message: "Intercom.logEvent(withName: \(event.event))", kind: .debug)
return event
}

var output = [String: Any]()
var price = [String: Any]()
var isAmountSet = false

for (key, value) in properties {
output[key] = value

if let dataValue = value as? Double,
key == "revenue" || key == "total" && !isAmountSet {
let amountInCents = dataValue * 100
price["amount"] = amountInCents
output.removeValue(forKey: key)
isAmountSet = true
}

if key == "currency" {
price["currency"] = value
output.removeValue(forKey: "currency")
}

if price.count > 0 {
output["price"] = price
}

if value is [String: Any] || value is [Any] {
output.removeValue(forKey: key)
}
}

Intercom.logEvent(withName: event.event, metaData: output)
analytics?.log(message: "Intercom.logEvent(withName: \(event.event), metaData: \(output))", kind: .debug)

return event
}

func group(event: GroupEvent) -> GroupEvent? {

// id is required field for adding or modifying a company
guard let traits = event.traits?.dictionaryValue,
let groupId = event.groupId else { return event }

let company = setCompanyAttributes(traits)
company.companyId = groupId

let userAttributes = ICMUserAttributes()
userAttributes.companies = [company]

Intercom.updateUser(userAttributes)
analytics?.log(message: "Intercom.updateUser(\(userAttributes))", kind: .debug)

return event
}

func reset() {
Intercom.logout()
analytics?.log(message: "Intercom.logout()", kind: .debug)
}
}

// Example of what settings may look like.
private struct IntercomSettings: Codable {
let appId: String
let mobileApiKey: String
}

private extension IntercomDestination {

func setUserAttributes(_ traits: [String: Any], event: RawEvent?) {
let userAttributes = ICMUserAttributes()
var customAttributes = traits

if let email = traits["email"] as? String {
userAttributes.email = email
customAttributes.removeValue(forKey: "email")
}

if let userId = traits["user_id"] as? String {
userAttributes.userId = userId
customAttributes.removeValue(forKey: "user_id")
}

if let name = traits["name"] as? String {
userAttributes.name = name
customAttributes.removeValue(forKey: "name")
}

if let phone = traits["phone"] as? String {
userAttributes.phone = phone
customAttributes.removeValue(forKey: "phone")
}

if let createdAt = traits["created_at"] as? Double {
let date = Date(timeIntervalSince1970: createdAt)
userAttributes.signedUpAt = date
customAttributes.removeValue(forKey: "created_at")
}

if let integration = event?.integrations?.dictionaryValue?["Intercom"] as? [AnyHashable: Any] {
if let languageOverride = integration["language_override"] as? String {
userAttributes.languageOverride = languageOverride
}

if let unsubscribed = integration["unsubscribed"] as? Bool {
userAttributes.unsubscribedFromEmails = unsubscribed
}
}

if let company = traits["company"] as? [String: Any] {
let companyData = setCompanyAttributes(company)
userAttributes.companies = [companyData]
}

for (key, value) in traits {
if !(value is String) &&
!(value is Int) &&
!(value is Double) &&
!(value is Bool) {
customAttributes.removeValue(forKey: key)
}
}

userAttributes.customAttributes = customAttributes
Intercom.updateUser(userAttributes)
analytics?.log(message: "Intercom.updateUser(\(userAttributes)", kind: .debug)
}

func setCompanyAttributes(_ company: [String: Any]) -> ICMCompany {
let companyData = ICMCompany()
var customTraits = company

if let companyId = company["id"] as? String {
companyData.companyId = companyId
customTraits.removeValue(forKey: "id")
}

if let monthlySpending = company["monthly_spend"] as? Double {
companyData.monthlySpend = NSNumber(value: monthlySpending)
customTraits.removeValue(forKey: "monthly_spend")
}

if let plan = company["plan"] as? String {
companyData.plan = plan
customTraits.removeValue(forKey: "plan")
}

if let createdAt = company["created_at"] as? Double {
let date = Date(timeIntervalSince1970: createdAt)
companyData.createdAt = date
customTraits.removeValue(forKey: "created_at")
}

for (key, value) in company {
if !(value is String) &&
!(value is Int) &&
!(value is Double) &&
!(value is Bool) {
customTraits.removeValue(forKey: key)
}
}

companyData.customAttributes = customTraits

return companyData
}
}