From 918d3ad6a3692442917af80d931f7fc255832caf Mon Sep 17 00:00:00 2001 From: mohitanand-cred Date: Tue, 12 Dec 2023 17:12:45 +0530 Subject: [PATCH 1/3] headers and query item capability added to proxy server --- Sources/Flush.swift | 13 +++-- Sources/FlushRequest.swift | 13 +++-- Sources/Mixpanel.swift | 93 ++++++++++++++++++++++++++++++---- Sources/MixpanelInstance.swift | 77 ++++++++++++++++++++++++++-- Sources/Network.swift | 11 ++++ 5 files changed, 186 insertions(+), 21 deletions(-) diff --git a/Sources/Flush.swift b/Sources/Flush.swift index ef13688f..510365bf 100644 --- a/Sources/Flush.swift +++ b/Sources/Flush.swift @@ -57,11 +57,11 @@ class Flush: AppLifecycle { flushIntervalReadWriteLock = DispatchQueue(label: "com.mixpanel.flush_interval.lock", qos: .utility, attributes: .concurrent, autoreleaseFrequency: .workItem) } - func flushQueue(_ queue: Queue, type: FlushType) { + func flushQueue(_ queue: Queue, type: FlushType, headers: [String: String], queryItems: [URLQueryItem]) { if flushRequest.requestNotAllowed() { return } - flushQueueInBatches(queue, type: type) + flushQueueInBatches(queue, type: type, headers: headers, queryItems: queryItems) } func startFlushTimer() { @@ -95,7 +95,7 @@ class Flush: AppLifecycle { } } - func flushQueueInBatches(_ queue: Queue, type: FlushType) { + func flushQueueInBatches(_ queue: Queue, type: FlushType, headers: [String: String], queryItems: [URLQueryItem]) { var mutableQueue = queue while !mutableQueue.isEmpty { let batchSize = min(mutableQueue.count, flushBatchSize) @@ -115,8 +115,10 @@ class Flush: AppLifecycle { } #endif // os(iOS) let success = flushRequest.sendRequest(requestData, - type: type, - useIP: useIPAddressForGeoLocation) + type: type, + useIP: useIPAddressForGeoLocation, + headers: headers, + queryItems: queryItems) #if os(iOS) if !MixpanelInstance.isiOSAppExtension() { delegate?.updateNetworkActivityIndicator(false) @@ -156,3 +158,4 @@ class Flush: AppLifecycle { } } + diff --git a/Sources/FlushRequest.swift b/Sources/FlushRequest.swift index 7f87a8c2..c6c0fc3a 100644 --- a/Sources/FlushRequest.swift +++ b/Sources/FlushRequest.swift @@ -21,7 +21,9 @@ class FlushRequest: Network { func sendRequest(_ requestData: String, type: FlushType, - useIP: Bool) -> Bool { + useIP: Bool, + headers: [String: String], + queryItems: [URLQueryItem] = []) -> Bool { let responseParser: (Data) -> Int? = { data in let response = String(data: data, encoding: String.Encoding.utf8) @@ -31,12 +33,16 @@ class FlushRequest: Network { return nil } + let resourceHeaders: [String: String] = headers.merging(["Content-Type": "application/json"]) {(_,new) in new} + let ipString = useIP ? "1" : "0" + var resourceQueryItems: [URLQueryItem] = [URLQueryItem(name: "ip", value: ipString)] + resourceQueryItems.append(contentsOf: queryItems) let resource = Network.buildResource(path: type.rawValue, method: .post, requestBody: requestData.data(using: .utf8), - queryItems: [URLQueryItem(name: "ip", value: ipString)], - headers: ["Content-Type": "application/json"], + queryItems: resourceQueryItems, + headers: resourceHeaders, parse: responseParser) var result = false let semaphore = DispatchSemaphore(value: 0) @@ -97,3 +103,4 @@ class FlushRequest: Network { } } + diff --git a/Sources/Mixpanel.swift b/Sources/Mixpanel.swift index 50fd5798..097fa52a 100644 --- a/Sources/Mixpanel.swift +++ b/Sources/Mixpanel.swift @@ -56,6 +56,49 @@ open class Mixpanel { superProperties: superProperties, serverURL: serverURL) } + + /** + Initializes an instance of the API with the given project token. + + Returns a new Mixpanel instance API object. This allows you to create more than one instance + of the API object, which is convenient if you'd like to send data to more than + one Mixpanel project from a single app. + + - parameter token: your project token + - parameter trackAutomaticEvents: Whether or not to collect common mobile events + - parameter flushInterval: Optional. Interval to run background flushing + - parameter instanceName: Optional. The name you want to uniquely identify the Mixpanel Instance. + It is useful when you want more than one Mixpanel instance under the same project token. + - parameter optOutTrackingByDefault: Optional. Whether or not to be opted out from tracking by default + - parameter useUniqueDistinctId: Optional. whether or not to use the unique device identifier as the distinct_id + - parameter superProperties: Optional. Super properties dictionary to register during initialization + - parameter proxyServerConfig: Optional. Setup for proxy server. + + - important: If you have more than one Mixpanel instance, it is beneficial to initialize + the instances with an instanceName. Then they can be reached by calling getInstance with name. + + - returns: returns a mixpanel instance if needed to keep throughout the project. + You can always get the instance by calling getInstance(name) + */ + + @discardableResult + open class func initialize(token apiToken: String, + trackAutomaticEvents: Bool, + flushInterval: Double = 60, + instanceName: String? = nil, + optOutTrackingByDefault: Bool = false, + useUniqueDistinctId: Bool = false, + superProperties: Properties? = nil, + proxyServerConfig: ProxyServerConfig) -> MixpanelInstance { + return MixpanelManager.sharedInstance.initialize(token: apiToken, + flushInterval: flushInterval, + instanceName: ((instanceName != nil) ? instanceName! : apiToken), + trackAutomaticEvents: trackAutomaticEvents, + optOutTrackingByDefault: optOutTrackingByDefault, + useUniqueDistinctId: useUniqueDistinctId, + superProperties: superProperties, + proxyServerConfig: proxyServerConfig) + } #else /** Initializes an instance of the API with the given project token (MAC OS ONLY). @@ -97,6 +140,24 @@ open class Mixpanel { superProperties: superProperties, serverURL: serverURL) } + + @discardableResult + open class func initialize(token apiToken: String, + flushInterval: Double = 60, + instanceName: String? = nil, + optOutTrackingByDefault: Bool = false, + useUniqueDistinctId: Bool = false, + superProperties: Properties? = nil, + proxyServerConfig: ProxyServerConfig) -> MixpanelInstance { + return MixpanelManager.sharedInstance.initialize(token: apiToken, + flushInterval: flushInterval, + instanceName: ((instanceName != nil) ? instanceName! : apiToken), + trackAutomaticEvents: false, + optOutTrackingByDefault: optOutTrackingByDefault, + useUniqueDistinctId: useUniqueDistinctId, + superProperties: superProperties, + proxyServerConfig: proxyServerConfig) + } #endif // os(OSX) /** @@ -176,7 +237,8 @@ final class MixpanelManager { optOutTrackingByDefault: Bool = false, useUniqueDistinctId: Bool = false, superProperties: Properties? = nil, - serverURL: String? = nil + serverURL: String? = nil, + proxyServerConfig: ProxyServerConfig? = nil ) -> MixpanelInstance { instanceQueue.sync { var instance: MixpanelInstance? @@ -184,14 +246,26 @@ final class MixpanelManager { mainInstance = instance return } - instance = MixpanelInstance(apiToken: apiToken, - flushInterval: flushInterval, - name: instanceName, - trackAutomaticEvents: trackAutomaticEvents, - optOutTrackingByDefault: optOutTrackingByDefault, - useUniqueDistinctId: useUniqueDistinctId, - superProperties: superProperties, - serverURL: serverURL) + + if let proxyServerConfig = proxyServerConfig { + instance = MixpanelInstance(apiToken: apiToken, + flushInterval: flushInterval, + name: instanceName, + trackAutomaticEvents: trackAutomaticEvents, + optOutTrackingByDefault: optOutTrackingByDefault, + useUniqueDistinctId: useUniqueDistinctId, + superProperties: superProperties, + proxyServerConfig: proxyServerConfig) + } else { + instance = MixpanelInstance(apiToken: apiToken, + flushInterval: flushInterval, + name: instanceName, + trackAutomaticEvents: trackAutomaticEvents, + optOutTrackingByDefault: optOutTrackingByDefault, + useUniqueDistinctId: useUniqueDistinctId, + superProperties: superProperties, + serverURL: serverURL) + } readWriteLock.write { instances[instanceName] = instance! mainInstance = instance! @@ -245,3 +319,4 @@ final class MixpanelManager { } } + diff --git a/Sources/MixpanelInstance.swift b/Sources/MixpanelInstance.swift index 5152582a..a072fca4 100644 --- a/Sources/MixpanelInstance.swift +++ b/Sources/MixpanelInstance.swift @@ -21,6 +21,21 @@ import CoreTelephony #endif // os(iOS) private let devicePrefix = "$device:" + +/** + * Delegate protocol for updating the Proxy Server API's network behavior. + */ +public protocol MixpanelProxyServerDelegate: AnyObject { + /** + Asks the delegate to return API resource items like query params & headers for proxy Server. + + - parameter mixpanel: The mixpanel instance + + - returns: return ServerProxyResource to give custom headers and query params. + */ + func mixpanelResourceForProxyServer(_ mixpanel: MixpanelInstance) -> ServerProxyResource? +} + /** * Delegate protocol for controlling the Mixpanel API's network behavior. */ @@ -44,6 +59,21 @@ protocol AppLifecycle { func applicationWillResignActive() } +public struct ProxyServerConfig { + public init?(serverUrl: String, delegate: MixpanelProxyServerDelegate? = nil) { + /// check if proxy server is not same as default mixpanel API + /// if same, then fail the initializer + /// this is to avoid case where client might inadvertently use headers intended for the proxy server + /// on Mixpanel's default server, leading to unexpected behavior. + guard serverUrl != BasePath.DefaultMixpanelAPI else { return nil } + self.serverUrl = serverUrl + self.delegate = delegate + } + + let serverUrl: String + let delegate: MixpanelProxyServerDelegate? +} + /// The class that represents the Mixpanel Instance open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDelegate { @@ -141,6 +171,10 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele } } + /// The a MixpanelProxyServerDelegate object that gives config control over Proxy Server's network activity. + open weak var proxyServerDelegate: MixpanelProxyServerDelegate? + + open var debugDescription: String { return "Mixpanel(\n" + " Token: \(apiToken),\n" @@ -226,9 +260,39 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele #if os(iOS) || os(tvOS) let automaticEvents = AutomaticEvents() #endif - init(apiToken: String?, flushInterval: Double, name: String, trackAutomaticEvents: Bool, optOutTrackingByDefault: Bool = false, - useUniqueDistinctId: Bool = false, superProperties: Properties? = nil, - serverURL: String? = nil) { + + convenience init( + apiToken: String?, + flushInterval: Double, + name: String, + trackAutomaticEvents: Bool, + optOutTrackingByDefault: Bool = false, + useUniqueDistinctId: Bool = false, + superProperties: Properties? = nil, + proxyServerConfig: ProxyServerConfig + ) { + self.init(apiToken: apiToken, + flushInterval: flushInterval, + name: name, + trackAutomaticEvents: trackAutomaticEvents, + optOutTrackingByDefault: optOutTrackingByDefault, + useUniqueDistinctId: useUniqueDistinctId, + superProperties: superProperties, + serverURL: proxyServerConfig.serverUrl) + self.proxyServerDelegate = proxyServerConfig.delegate + } + + + init( + apiToken: String?, + flushInterval: Double, + name: String, + trackAutomaticEvents: Bool, + optOutTrackingByDefault: Bool = false, + useUniqueDistinctId: Bool = false, + superProperties: Properties? = nil, + serverURL: String? = nil + ) { if let apiToken = apiToken, !apiToken.isEmpty { self.apiToken = apiToken } @@ -987,7 +1051,11 @@ extension MixpanelInstance { if hasOptedOutTracking() { return } - self.flushInstance.flushQueue(queue, type: type) + let proxyServerResource = proxyServerDelegate?.mixpanelResourceForProxyServer(self) + let headers: [String: String] = proxyServerResource?.headers ?? [:] + let queryItems = proxyServerResource?.queryItems ?? [] + + self.flushInstance.flushQueue(queue, type: type, headers: headers, queryItems: queryItems) } func flushSuccess(type: FlushType, ids: [Int32]) { @@ -1530,3 +1598,4 @@ extension MixpanelInstance { } } + diff --git a/Sources/Network.swift b/Sources/Network.swift index 1e394971..6b6a92d7 100644 --- a/Sources/Network.swift +++ b/Sources/Network.swift @@ -52,6 +52,16 @@ enum Reason { case other(Error) } +public struct ServerProxyResource { + public init(queryItems: [URLQueryItem]? = nil, headers: [String : String]) { + self.queryItems = queryItems + self.headers = headers + } + + let queryItems: [URLQueryItem]? + let headers: [String: String] +} + class Network { let basePathIdentifier: String @@ -192,3 +202,4 @@ class Network { } } } + From 91c16b9cee80e2a8afe0cc56d6e6befcd2ffe02c Mon Sep 17 00:00:00 2001 From: mohitanand-cred Date: Thu, 21 Dec 2023 13:11:45 +0530 Subject: [PATCH 2/3] added header capability to other network requests --- Sources/FlushRequest.swift | 2 +- Sources/MixpanelInstance.swift | 62 ++++++++++++++++++++++++++-------- Sources/Network.swift | 13 ++++--- 3 files changed, 57 insertions(+), 20 deletions(-) diff --git a/Sources/FlushRequest.swift b/Sources/FlushRequest.swift index c6c0fc3a..f14485cc 100644 --- a/Sources/FlushRequest.swift +++ b/Sources/FlushRequest.swift @@ -33,7 +33,7 @@ class FlushRequest: Network { return nil } - let resourceHeaders: [String: String] = headers.merging(["Content-Type": "application/json"]) {(_,new) in new} + let resourceHeaders: [String: String] = ["Content-Type": "application/json"].merging(headers) {(_,new) in new } let ipString = useIP ? "1" : "0" var resourceQueryItems: [URLQueryItem] = [URLQueryItem(name: "ip", value: ipString)] diff --git a/Sources/MixpanelInstance.swift b/Sources/MixpanelInstance.swift index a072fca4..b574e793 100644 --- a/Sources/MixpanelInstance.swift +++ b/Sources/MixpanelInstance.swift @@ -33,7 +33,7 @@ public protocol MixpanelProxyServerDelegate: AnyObject { - returns: return ServerProxyResource to give custom headers and query params. */ - func mixpanelResourceForProxyServer(_ mixpanel: MixpanelInstance) -> ServerProxyResource? + func mixpanelResourceForProxyServer(_ name: String) -> ServerProxyResource? } /** @@ -172,7 +172,7 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele } /// The a MixpanelProxyServerDelegate object that gives config control over Proxy Server's network activity. - open weak var proxyServerDelegate: MixpanelProxyServerDelegate? + open weak var proxyServerDelegate: MixpanelProxyServerDelegate? = nil open var debugDescription: String { @@ -208,7 +208,9 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele if (superProperties["$lib_version"] != nil) { trackProps["$lib_version"] = self.superProperties["$lib_version"] as! String } - Network.sendHttpEvent(serverURL: self.serverURL, eventName: "Toggle SDK Logging", apiToken: "metrics-1", distinctId: apiToken, properties: trackProps) + // add headers + let headers = self.proxyServerDelegate?.mixpanelResourceForProxyServer(name)?.headers ?? [:] + Network.sendHttpEvent(serverURL: self.serverURL, headers: headers, eventName: "Toggle SDK Logging", apiToken: "metrics-1", distinctId: apiToken, properties: trackProps) #endif } } @@ -278,12 +280,11 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele optOutTrackingByDefault: optOutTrackingByDefault, useUniqueDistinctId: useUniqueDistinctId, superProperties: superProperties, - serverURL: proxyServerConfig.serverUrl) - self.proxyServerDelegate = proxyServerConfig.delegate + serverURL: proxyServerConfig.serverUrl, + proxyServerDelegate: proxyServerConfig.delegate) } - - init( + convenience init( apiToken: String?, flushInterval: Double, name: String, @@ -292,6 +293,29 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele useUniqueDistinctId: Bool = false, superProperties: Properties? = nil, serverURL: String? = nil + ) { + self.init(apiToken: apiToken, + flushInterval: flushInterval, + name: name, + trackAutomaticEvents: trackAutomaticEvents, + optOutTrackingByDefault: optOutTrackingByDefault, + useUniqueDistinctId: useUniqueDistinctId, + superProperties: superProperties, + serverURL: serverURL, + proxyServerDelegate: nil) + } + + + private init( + apiToken: String?, + flushInterval: Double, + name: String, + trackAutomaticEvents: Bool, + optOutTrackingByDefault: Bool = false, + useUniqueDistinctId: Bool = false, + superProperties: Properties? = nil, + serverURL: String? = nil, + proxyServerDelegate: MixpanelProxyServerDelegate? = nil ) { if let apiToken = apiToken, !apiToken.isEmpty { self.apiToken = apiToken @@ -301,9 +325,13 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele self.serverURL = serverURL BasePath.namedBasePaths[name] = serverURL } + self.proxyServerDelegate = proxyServerDelegate #if DEBUG + //add headers here + let headers = proxyServerDelegate?.mixpanelResourceForProxyServer(name)?.headers ?? [:] MixpanelInstance.didDebugInit( serverURL: self.serverURL, + headers: headers, distinctId: self.apiToken, libName: superProperties?.get(key: "mp_lib", defaultValue: nil), libVersion: superProperties?.get(key: "$lib_version", defaultValue: nil) @@ -619,7 +647,7 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele } #endif #endif // os(iOS) - private class func didDebugInit(serverURL: String, distinctId: String, libName: String?, libVersion: String?) { + private class func didDebugInit(serverURL: String, headers: [String: String], distinctId: String, libName: String?, libVersion: String?) { if distinctId.count == 32 { let debugInitCount = UserDefaults.standard.integer(forKey: InternalKeys.mpDebugInitCountKey) + 1 var properties: Properties = ["Debug Launch Count": debugInitCount] @@ -629,14 +657,15 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele if let libVersion = libVersion { properties["$lib_version"] = libVersion } - Network.sendHttpEvent(serverURL: serverURL, eventName: "SDK Debug Launch", apiToken: "metrics-1", distinctId: distinctId, properties: properties) { (_) in } - checkIfImplemented(serverURL: serverURL, distinctId: distinctId, properties: properties) + // add headers + Network.sendHttpEvent(serverURL: serverURL, headers: headers, eventName: "SDK Debug Launch", apiToken: "metrics-1", distinctId: distinctId, properties: properties) { (_) in } + checkIfImplemented(serverURL: serverURL, headers: headers, distinctId: distinctId, properties: properties) UserDefaults.standard.set(debugInitCount, forKey: InternalKeys.mpDebugInitCountKey) UserDefaults.standard.synchronize() } } - private class func checkIfImplemented(serverURL: String, distinctId: String, properties: Properties) { + private class func checkIfImplemented(serverURL: String, headers: [String: String], distinctId: String, properties: Properties) { let hasImplemented: Bool = UserDefaults.standard.bool(forKey: InternalKeys.mpDebugImplementedKey) if !hasImplemented { var completed = 0 @@ -655,8 +684,9 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele "Aliased": hasAliased, "Used People": hasUsedPeople, ]) {(_,new) in new} + // add headers Network.sendHttpEvent( - serverURL: serverURL, + serverURL: serverURL, headers: headers, eventName: "SDK Implemented", apiToken: "metrics-1", distinctId: distinctId, @@ -955,8 +985,10 @@ extension MixpanelInstance { } let defaultsKey = "trackedKey" if !UserDefaults.standard.bool(forKey: defaultsKey) { - trackingQueue.async { [apiToken, defaultsKey, serverURL] in - Network.sendHttpEvent(serverURL: serverURL, eventName: "Integration", apiToken: "85053bf24bba75239b16a601d9387e17", distinctId: apiToken, updatePeople: false) { [defaultsKey] (success) in + trackingQueue.async { [apiToken, defaultsKey, serverURL, name] in + // add headers + let headers = self.proxyServerDelegate?.mixpanelResourceForProxyServer(name)?.headers ?? [:] + Network.sendHttpEvent(serverURL: serverURL, headers: headers, eventName: "Integration", apiToken: "85053bf24bba75239b16a601d9387e17", distinctId: apiToken, updatePeople: false) { [defaultsKey] (success) in if success { UserDefaults.standard.set(true, forKey: defaultsKey) UserDefaults.standard.synchronize() @@ -1051,7 +1083,7 @@ extension MixpanelInstance { if hasOptedOutTracking() { return } - let proxyServerResource = proxyServerDelegate?.mixpanelResourceForProxyServer(self) + let proxyServerResource = proxyServerDelegate?.mixpanelResourceForProxyServer(name) let headers: [String: String] = proxyServerResource?.headers ?? [:] let queryItems = proxyServerResource?.queryItems ?? [] diff --git a/Sources/Network.swift b/Sources/Network.swift index 6b6a92d7..7e7774ea 100644 --- a/Sources/Network.swift +++ b/Sources/Network.swift @@ -58,8 +58,8 @@ public struct ServerProxyResource { self.headers = headers } - let queryItems: [URLQueryItem]? - let headers: [String: String] + public let queryItems: [URLQueryItem]? + public let headers: [String: String] } class Network { @@ -139,6 +139,7 @@ class Network { } class func sendHttpEvent(serverURL: String, + headers: [String: String], eventName: String, apiToken: String, distinctId: String, @@ -165,10 +166,12 @@ class Network { let requestBody = "ip=1&data=\(requestData)" .data(using: String.Encoding.utf8) + let resourceHeaders: [String: String] = ["Accept-Encoding": "gzip"].merging(headers) {(_,new) in new } + let resource = Network.buildResource(path: FlushType.events.rawValue, method: .post, requestBody: requestBody, - headers: ["Accept-Encoding": "gzip"], + headers: resourceHeaders, parse: responseParser) Network.apiRequest(base: serverURL, @@ -191,11 +194,13 @@ class Network { if updatePeople { let engageData = JSONHandler.encodeAPIData([["$token": apiToken, "$distinct_id": distinctId, "$add": [eventName: 1]] as [String : Any]]) if let engageData = engageData { + let resourceHeaders: [String: String] = ["Accept-Encoding": "gzip"].merging(headers) {(_,new) in new } + let engageBody = "ip=1&data=\(engageData)".data(using: String.Encoding.utf8) let engageResource = Network.buildResource(path: FlushType.people.rawValue, method: .post, requestBody: engageBody, - headers: ["Accept-Encoding": "gzip"], + headers: resourceHeaders, parse: responseParser) Network.apiRequest(base: serverURL, resource: engageResource) { _, _, _ in } success: { _, _ in } } From c432bc8b728af67c2bb923efc22b58378a0018f4 Mon Sep 17 00:00:00 2001 From: mohitanand-cred Date: Fri, 9 Feb 2024 12:05:37 +0530 Subject: [PATCH 3/3] added comment for macos initialize function and separated MixpanelInstance creation for ProxyServerConfig & ServerURL --- Sources/Mixpanel.swift | 80 +++++++++++++++++++++++++++++++----------- 1 file changed, 59 insertions(+), 21 deletions(-) diff --git a/Sources/Mixpanel.swift b/Sources/Mixpanel.swift index 097fa52a..0c5f4766 100644 --- a/Sources/Mixpanel.swift +++ b/Sources/Mixpanel.swift @@ -141,6 +141,29 @@ open class Mixpanel { serverURL: serverURL) } + /** + Initializes an instance of the API with the given project token (MAC OS ONLY). + + Returns a new Mixpanel instance API object. This allows you to create more than one instance + of the API object, which is convenient if you'd like to send data to more than + one Mixpanel project from a single app. + + - parameter token: your project token + - parameter flushInterval: Optional. Interval to run background flushing + - parameter instanceName: Optional. The name you want to uniquely identify the Mixpanel Instance. + It is useful when you want more than one Mixpanel instance under the same project token. + - parameter optOutTrackingByDefault: Optional. Whether or not to be opted out from tracking by default + - parameter useUniqueDistinctId: Optional. whether or not to use the unique device identifier as the distinct_id + - parameter superProperties: Optional. Super properties dictionary to register during initialization + - parameter proxyServerConfig: Optional. Setup for proxy server. + + - important: If you have more than one Mixpanel instance, it is beneficial to initialize + the instances with an instanceName. Then they can be reached by calling getInstance with name. + + - returns: returns a mixpanel instance if needed to keep throughout the project. + You can always get the instance by calling getInstance(name) + */ + @discardableResult open class func initialize(token apiToken: String, flushInterval: Double = 60, @@ -237,9 +260,42 @@ final class MixpanelManager { optOutTrackingByDefault: Bool = false, useUniqueDistinctId: Bool = false, superProperties: Properties? = nil, - serverURL: String? = nil, - proxyServerConfig: ProxyServerConfig? = nil + serverURL: String? = nil ) -> MixpanelInstance { + return dequeueInstance(instanceName: instanceName) { + return MixpanelInstance(apiToken: apiToken, + flushInterval: flushInterval, + name: instanceName, + trackAutomaticEvents: trackAutomaticEvents, + optOutTrackingByDefault: optOutTrackingByDefault, + useUniqueDistinctId: useUniqueDistinctId, + superProperties: superProperties, + serverURL: serverURL) + } + } + + func initialize(token apiToken: String, + flushInterval: Double, + instanceName: String, + trackAutomaticEvents: Bool, + optOutTrackingByDefault: Bool = false, + useUniqueDistinctId: Bool = false, + superProperties: Properties? = nil, + proxyServerConfig: ProxyServerConfig + ) -> MixpanelInstance { + return dequeueInstance(instanceName: instanceName) { + return MixpanelInstance(apiToken: apiToken, + flushInterval: flushInterval, + name: instanceName, + trackAutomaticEvents: trackAutomaticEvents, + optOutTrackingByDefault: optOutTrackingByDefault, + useUniqueDistinctId: useUniqueDistinctId, + superProperties: superProperties, + proxyServerConfig: proxyServerConfig) + } + } + + private func dequeueInstance(instanceName: String, instanceCreation: () -> MixpanelInstance) -> MixpanelInstance { instanceQueue.sync { var instance: MixpanelInstance? if let instance = instances[instanceName] { @@ -247,25 +303,7 @@ final class MixpanelManager { return } - if let proxyServerConfig = proxyServerConfig { - instance = MixpanelInstance(apiToken: apiToken, - flushInterval: flushInterval, - name: instanceName, - trackAutomaticEvents: trackAutomaticEvents, - optOutTrackingByDefault: optOutTrackingByDefault, - useUniqueDistinctId: useUniqueDistinctId, - superProperties: superProperties, - proxyServerConfig: proxyServerConfig) - } else { - instance = MixpanelInstance(apiToken: apiToken, - flushInterval: flushInterval, - name: instanceName, - trackAutomaticEvents: trackAutomaticEvents, - optOutTrackingByDefault: optOutTrackingByDefault, - useUniqueDistinctId: useUniqueDistinctId, - superProperties: superProperties, - serverURL: serverURL) - } + instance = instanceCreation() readWriteLock.write { instances[instanceName] = instance! mainInstance = instance!