Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Proxy server header capability #628

Merged
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
13 changes: 8 additions & 5 deletions Sources/Flush.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -156,3 +158,4 @@ class Flush: AppLifecycle {
}

}

13 changes: 10 additions & 3 deletions Sources/FlushRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -31,12 +33,16 @@ class FlushRequest: Network {
return nil
}

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)]
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)
Expand Down Expand Up @@ -97,3 +103,4 @@ class FlushRequest: Network {
}

}

129 changes: 121 additions & 8 deletions Sources/Mixpanel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Copy link
Contributor

Choose a reason for hiding this comment

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

if proxyServerConfig is optional, could you add proxyServerConfig: ProxyServerConfig? = nil to the existing initialize function rather than adding a new function?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the feedback on the PR 🙌 . On using the same intialize function, they are few points I want to highlight.

  • By combining serverURL and ProxyServerConfig into one method, we risk users setting both parameters, which doesn't align with their intended use. Ideally one of them should be set. Keeping them separate ensures users have a clear choice without the possibility of configuring conflicting options.
  • With two distinct initialization methods, we're directly guiding users on their options. One method is for basic server URL configuration, and the other is for more advanced proxy configurations.

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).
Expand Down Expand Up @@ -97,6 +140,47 @@ open class Mixpanel {
superProperties: superProperties,
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,
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)

/**
Expand Down Expand Up @@ -178,20 +262,48 @@ final class MixpanelManager {
superProperties: Properties? = 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] {
mainInstance = instance
return
}
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!
Expand Down Expand Up @@ -245,3 +357,4 @@ final class MixpanelManager {
}

}

Loading
Loading