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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,4 @@ iOSInjectionProject/
.DS_Store
Package.resolved
*.xcuserdatad
/.swiftpm/xcode/xcshareddata
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
objects = {

/* Begin PBXBuildFile section */
46871694270E16080028B595 /* NotificationTracking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4687168E270E16080028B595 /* NotificationTracking.swift */; };
46871695270E16080028B595 /* UIKitScreenTracking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4687168F270E16080028B595 /* UIKitScreenTracking.swift */; };
46871696270E16080028B595 /* ConsentTracking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46871690270E16080028B595 /* ConsentTracking.swift */; };
46871697270E16080028B595 /* IDFACollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46871691270E16080028B595 /* IDFACollection.swift */; };
46871698270E16080028B595 /* ConsoleLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46871692270E16080028B595 /* ConsoleLogger.swift */; };
469EC8D0266066130068F9E3 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 469EC8CF266066130068F9E3 /* SystemConfiguration.framework */; };
469EC8E0266828860068F9E3 /* FlurryAnalyticsSPM in Frameworks */ = {isa = PBXBuildFile; productRef = 469EC8DF266828860068F9E3 /* FlurryAnalyticsSPM */; };
469F7B08266011690038E773 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 469F7B07266011690038E773 /* AppDelegate.swift */; };
Expand Down Expand Up @@ -36,6 +41,11 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
4687168E270E16080028B595 /* NotificationTracking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationTracking.swift; sourceTree = "<group>"; };
4687168F270E16080028B595 /* UIKitScreenTracking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKitScreenTracking.swift; sourceTree = "<group>"; };
46871690270E16080028B595 /* ConsentTracking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsentTracking.swift; sourceTree = "<group>"; };
46871691270E16080028B595 /* IDFACollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IDFACollection.swift; sourceTree = "<group>"; };
46871692270E16080028B595 /* ConsoleLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsoleLogger.swift; sourceTree = "<group>"; };
469EC8CF266066130068F9E3 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
469EC8E1266828AF0068F9E3 /* DestinationsExample-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "DestinationsExample-Bridging-Header.h"; sourceTree = "<group>"; };
469F7B04266011690038E773 /* DestinationsExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DestinationsExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -80,6 +90,19 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
4687168C270E16080028B595 /* other_plugins */ = {
isa = PBXGroup;
children = (
4687168E270E16080028B595 /* NotificationTracking.swift */,
4687168F270E16080028B595 /* UIKitScreenTracking.swift */,
46871690270E16080028B595 /* ConsentTracking.swift */,
46871691270E16080028B595 /* IDFACollection.swift */,
46871692270E16080028B595 /* ConsoleLogger.swift */,
);
name = other_plugins;
path = ../../../other_plugins;
sourceTree = "<group>";
};
469F7AFB266011690038E773 = {
isa = PBXGroup;
children = (
Expand All @@ -101,6 +124,7 @@
isa = PBXGroup;
children = (
BA384C9D2686609000AFEA1B /* DestinationsExample.entitlements */,
4687168C270E16080028B595 /* other_plugins */,
469F7B1E266012CB0038E773 /* destination_plugins */,
469F7B07266011690038E773 /* AppDelegate.swift */,
469F7B09266011690038E773 /* SceneDelegate.swift */,
Expand Down Expand Up @@ -241,6 +265,7 @@
buildActionMask = 2147483647;
files = (
BA384C9A2682973300AFEA1B /* AppsFlyerDestination.swift in Sources */,
46871698270E16080028B595 /* ConsoleLogger.swift in Sources */,
469F7B20266012CB0038E773 /* FlurryDestination.swift in Sources */,
469F7B0C266011690038E773 /* ViewController.swift in Sources */,
96469A9B270279A600AC5772 /* IntercomDestination.swift in Sources */,
Expand All @@ -249,9 +274,13 @@
96DBF37D26FA943300724B0B /* ComscoreDestination.swift in Sources */,
965DC0FB2668077400DDF9C7 /* AmplitudeSession.swift in Sources */,
469F7B08266011690038E773 /* AppDelegate.swift in Sources */,
46871694270E16080028B595 /* NotificationTracking.swift in Sources */,
469F7B25266013320038E773 /* AdjustDestination.swift in Sources */,
469F7B0A266011690038E773 /* SceneDelegate.swift in Sources */,
965DC1232669947F00DDF9C7 /* FirebaseDestination.swift in Sources */,
46871695270E16080028B595 /* UIKitScreenTracking.swift in Sources */,
46871696270E16080028B595 /* ConsentTracking.swift in Sources */,
46871697270E16080028B595 /* IDFACollection.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
4 changes: 2 additions & 2 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 33 additions & 9 deletions Sources/Segment/Plugins/Logger/SegmentLog.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,32 @@ internal class SegmentLog: UtilityPlugin {

// For internal use only. Note: This will contain the last created instance
// of analytics when used in a multi-analytics environment.
internal static var sharedAnalytics: Analytics?
internal static var sharedAnalytics: Analytics? = nil

#if DEBUG
internal static var globalLogger: SegmentLog {
get {
let logger = SegmentLog()
logger.addTargets()
return logger
}
}
#endif

required init() { }

func configure(analytics: Analytics) {
self.analytics = analytics
SegmentLog.sharedAnalytics = analytics
addTargets()
}

internal func addTargets() {
#if !os(Linux)
try? add(target: SystemTarget(), for: LoggingType.log)
#if DEBUG
try? add(target: ConsoleTarget(), for: LoggingType.log)
#endif
#else
try? add(target: ConsoleTarget(), for: LoggingType.log)
#endif
Expand Down Expand Up @@ -146,16 +163,23 @@ internal extension Analytics {
/// - function: The name of the function the log came from. This will be captured automatically.
/// - line: The line number in the function the log came from. This will be captured automatically.
static func segmentLog(message: String, kind: LogFilterKind? = nil, function: String = #function, line: Int = #line) {
SegmentLog.sharedAnalytics?.apply { plugin in
if let loggerPlugin = plugin as? SegmentLog {
var filterKind = loggerPlugin.filterKind
if let logKind = kind {
filterKind = logKind
if let shared = SegmentLog.sharedAnalytics {
shared.apply { plugin in
if let loggerPlugin = plugin as? SegmentLog {
var filterKind = loggerPlugin.filterKind
if let logKind = kind {
filterKind = logKind
}

let log = LogFactory.buildLog(destination: .log, title: "", message: message, kind: filterKind, function: function, line: line)
loggerPlugin.log(log, destination: .log)
}

let log = LogFactory.buildLog(destination: .log, title: "", message: message, kind: filterKind, function: function, line: line)
loggerPlugin.log(log, destination: .log)
}
} else {
#if DEBUG
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm debating if we should change this from DEBUG to DEBUGLOG, that way our console doesn't get spammed and we can easily disable this and use Console as a backup. Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really following what you mean here. Please hit me up on slack.

let log = LogFactory.buildLog(destination: .log, title: "", message: message, kind: .debug, function: function, line: line)
SegmentLog.globalLogger.log(log, destination: .log)
#endif
}
}

Expand Down
8 changes: 8 additions & 0 deletions Sources/Segment/Settings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ extension Settings: Equatable {
}

extension Analytics {
/// Manually enable a destination plugin. This is useful when a given DestinationPlugin doesn't have any Segment tie-ins at all.
/// This will allow the destination to be processed in the same way within this library.
/// - Parameters:
/// - plugin: The destination plugin to enable.
public func manuallyEnableDestination(plugin: DestinationPlugin) {
self.store.dispatch(action: System.AddIntegrationAction(key: plugin.key))
}

internal func update(settings: Settings, type: UpdateType) {
apply { (plugin) in
// tell all top level plugins to update.
Expand Down
191 changes: 183 additions & 8 deletions Sources/Segment/Types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,10 @@ import Foundation
import Sovran


// MARK: - Event Parameter Types

typealias Integrations = Codable
typealias Properties = Codable
typealias Traits = Codable


// MARK: - Event Types

public protocol RawEvent: Codable {

var type: String? { get set }
var anonymousId: String? { get set }
var messageId: String? { get set }
Expand Down Expand Up @@ -149,11 +143,192 @@ public struct AliasEvent: RawEvent {
}
}

// MARK: - RawEvent conveniences

internal struct IntegrationConstants {
static let allIntegrationsKey = "All"
}

extension RawEvent {
/**
Disable all cloud-mode integrations for this event, except for any specific keys given.
This will preserve any per-integration specific settings if the integration is to remain enabled.
- Parameters:
- exceptKeys: A list of integration keys to exclude from disabling.
*/
public mutating func disableCloudIntegrations(exceptKeys: [String]? = nil) {
guard let existing = integrations?.dictionaryValue else {
// this shouldn't happen, might oughta log it.
Analytics.segmentLog(message: "Unable to get what should be a valid list of integrations from event.", kind: .error)
return
}
var new = [String: Any]()
new[IntegrationConstants.allIntegrationsKey] = false
if let exceptKeys = exceptKeys {
for key in exceptKeys {
if let value = existing[key], value is [String: Any] {
new[key] = value
} else {
new[key] = true
}
}
}

do {
integrations = try JSON(new)
} catch {
// this shouldn't happen, log it.
Analytics.segmentLog(message: "Unable to convert list of integrations to JSON. \(error)", kind: .error)
}
}

/**
Enable all cloud-mode integrations for this event, except for any specific keys given.
- Parameters:
- exceptKeys: A list of integration keys to exclude from enabling.
*/
public mutating func enableCloudIntegrations(exceptKeys: [String]? = nil) {
var new = [String: Any]()
new[IntegrationConstants.allIntegrationsKey] = true
if let exceptKeys = exceptKeys {
for key in exceptKeys {
new[key] = false
}
}

do {
integrations = try JSON(new)
} catch {
// this shouldn't happen, log it.
Analytics.segmentLog(message: "Unable to convert list of integrations to JSON. \(error)", kind: .error)
}
}

/**
Disable a specific cloud-mode integration using it's key name.
- Parameters:
- key: The key name of the integration to disable.
*/
public mutating func disableIntegration(key: String) {
guard let existing = integrations?.dictionaryValue else {
// this shouldn't happen, might oughta log it.
Analytics.segmentLog(message: "Unable to get what should be a valid list of integrations from event.", kind: .error)
return
}
// we don't really care what the value of this key was before, as
// a disabled one can only be false.
var new = existing
new[key] = false

do {
integrations = try JSON(new)
} catch {
// this shouldn't happen, log it.
Analytics.segmentLog(message: "Unable to convert list of integrations to JSON. \(error)", kind: .error)
}
}

/**
Enable a specific cloud-mode integration using it's key name.
- Parameters:
- key: The key name of the integration to enable.
*/
public mutating func enableIntegration(key: String) {
guard let existing = integrations?.dictionaryValue else {
// this shouldn't happen, might oughta log it.
Analytics.segmentLog(message: "Unable to get what should be a valid list of integrations from event.", kind: .error)
return
}

var new = existing
// if it's a dictionary already, it's considered enabled, so don't
// overwrite whatever they may have put there. If that's not the case
// just set it to true since that's the only other value it could have
// to be considered `enabled`.
if (existing[key] as? [String: Any]) == nil {
new[key] = true
}

do {
integrations = try JSON(new)
} catch {
// this shouldn't happen, log it.
Analytics.segmentLog(message: "Unable to convert list of integrations to JSON. \(error)", kind: .error)
}
}

/**
Set values to be received for this event in cloud-mode, specific to an integration key path.
Note that when specifying nil as the value, the key will be removed for the given key path. Additionally,
any keys that don't already exist in the path will be created as necessary.

Example:
```
trackEvent.setIntegrationValue(42, forKeyPath: "Amplitude.threshold")
```

- Parameters:
- value: The value to set for the given keyPath, or nil.
- forKeyPath: The key path for the value.
*/
public mutating func setIntegrationValue(_ value: Any?, forKeyPath keyPath: String) {
guard let existing = integrations?.dictionaryValue else {
// this shouldn't happen, might oughta log it.
Analytics.segmentLog(message: "Unable to get what should be a valid list of integrations from event.", kind: .error)
return
}

var new = existing
new[keyPath: KeyPath(keyPath)] = value
do {
integrations = try JSON(new)
} catch {
// this shouldn't happen, log it.
Analytics.segmentLog(message: "Unable to convert list of integrations to JSON. \(error)", kind: .error)
}
}

/**
Set context values for this event.
Note that when specifying nil as the value, the key will be removed for the given key path. Additionally,
any keys that don't already exist in the path will be created as necessary.

Example:
```
// the metadata key will be created as a dictionary, and the key nickname will be set.
trackEvent.setContextValue("Brandon's device", forKeyPath: "device.metadata.nickname")

// the metadata key will be removed entirely.
trackEvent.setContextValue(nil, forKeyPath: "device.metadata")
```

- Parameters:
- value: The value to set for the given keyPath, or nil.
- forKeyPath: The key path for the value.
*/
public mutating func setContextValue(_ value: Any?, forKeyPath keyPath: String) {
guard let existing = context?.dictionaryValue else {
// this shouldn't happen, might oughta log it.
Analytics.segmentLog(message: "Unable to get what should be a valid context from event.", kind: .error)
return
}

var new = existing
new[keyPath: KeyPath(keyPath)] = value
do {
context = try JSON(new)
} catch {
// this shouldn't happen, log it.
Analytics.segmentLog(message: "Unable to convert context to JSON. \(error)", kind: .error)
}
}
}


// MARK: - RawEvent data helpers

extension RawEvent {
public mutating func applyRawEventData(event: RawEvent?) {
internal mutating func applyRawEventData(event: RawEvent?) {
if let e = event {
anonymousId = e.anonymousId
messageId = e.messageId
Expand Down
Loading