diff --git a/Mixpanel-swift.podspec b/Mixpanel-swift.podspec index 9d6825d7..f10d2367 100644 --- a/Mixpanel-swift.podspec +++ b/Mixpanel-swift.podspec @@ -21,7 +21,7 @@ Pod::Spec.new do |s| 'Sources/Constants.swift', 'Sources/MixpanelType.swift', 'Sources/Mixpanel.swift', 'Sources/MixpanelInstance.swift', 'Sources/Flush.swift','Sources/Track.swift', 'Sources/People.swift', 'Sources/AutomaticEvents.swift', 'Sources/Group.swift', - 'Sources/ReadWriteLock.swift', 'Sources/SessionMetadata.swift', 'Sources/MPDB.swift', 'Sources/MixpanelPersistence.swift'] + 'Sources/ReadWriteLock.swift', 'Sources/SessionMetadata.swift', 'Sources/MPDB.swift', 'Sources/MixpanelPersistence.swift', 'Sources/Data+Compression.swift'] s.tvos.deployment_target = '11.0' s.tvos.frameworks = 'UIKit', 'Foundation' s.tvos.pod_target_xcconfig = { diff --git a/Mixpanel.xcodeproj/project.pbxproj b/Mixpanel.xcodeproj/project.pbxproj index 17a24ac8..19d02829 100644 --- a/Mixpanel.xcodeproj/project.pbxproj +++ b/Mixpanel.xcodeproj/project.pbxproj @@ -45,6 +45,10 @@ 86F86EF7224554B900B69832 /* Group.swift in Sources */ = {isa = PBXBuildFile; fileRef = 673ABE3921360CBE00B1784B /* Group.swift */; }; 86F86F3622497F1200B69832 /* AutomaticEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = E151FA371E70DFB5002EF53D /* AutomaticEvents.swift */; }; 86F86F3722497F2900B69832 /* AutomaticEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = E151FA371E70DFB5002EF53D /* AutomaticEvents.swift */; }; + 95ECF0682C9B851A006364D2 /* Data+Compression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95ECF0662C9B83D8006364D2 /* Data+Compression.swift */; }; + 95ECF0692C9B851B006364D2 /* Data+Compression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95ECF0662C9B83D8006364D2 /* Data+Compression.swift */; }; + 95ECF06A2C9B851B006364D2 /* Data+Compression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95ECF0662C9B83D8006364D2 /* Data+Compression.swift */; }; + 95ECF06B2C9B851C006364D2 /* Data+Compression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95ECF0662C9B83D8006364D2 /* Data+Compression.swift */; }; BB9614171F3BB87700C3EF3E /* ReadWriteLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB9614161F3BB87700C3EF3E /* ReadWriteLock.swift */; }; E10D118D1EC0F30900195CCD /* AutomaticEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = E151FA371E70DFB5002EF53D /* AutomaticEvents.swift */; }; E115948B1CFF1538007F8B4F /* Mixpanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E115948A1CFF1538007F8B4F /* Mixpanel.swift */; }; @@ -107,6 +111,7 @@ 8625BEBA26D045CE0009BAA9 /* MPDB.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MPDB.swift; sourceTree = ""; }; 868550AB2699096F001FCDDC /* MixpanelPersistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MixpanelPersistence.swift; sourceTree = ""; }; 86F86E81224404BD00B69832 /* Mixpanel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Mixpanel.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 95ECF0662C9B83D8006364D2 /* Data+Compression.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Compression.swift"; sourceTree = ""; }; BB9614161F3BB87700C3EF3E /* ReadWriteLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadWriteLock.swift; sourceTree = ""; }; E115947D1CFF1491007F8B4F /* Mixpanel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Mixpanel.framework; sourceTree = BUILT_PRODUCTS_DIR; }; E11594821CFF1491007F8B4F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = ../Info.plist; sourceTree = ""; }; @@ -254,6 +259,7 @@ E189D8FA1D5A692A007F3F29 /* Utilities */ = { isa = PBXGroup; children = ( + 95ECF0662C9B83D8006364D2 /* Data+Compression.swift */, E11594981D01689F007F8B4F /* JSONHandler.swift */, E1982BFE1D0AC2E2006B7330 /* Error.swift */, E1D335CF1D3059A800E68E12 /* AutomaticProperties.swift */, @@ -484,6 +490,7 @@ 86F86EC522443A2C00B69832 /* People.swift in Sources */, 86F86EC422443A2300B69832 /* ReadWriteLock.swift in Sources */, 8625BEBE26D045CE0009BAA9 /* MPDB.swift in Sources */, + 95ECF06B2C9B851C006364D2 /* Data+Compression.swift in Sources */, 86F86EC222443A1300B69832 /* Track.swift in Sources */, 86F86EC122443A0E00B69832 /* JSONHandler.swift in Sources */, 86F86EC022443A0800B69832 /* MixpanelType.swift in Sources */, @@ -512,6 +519,7 @@ E11594971D006022007F8B4F /* Network.swift in Sources */, E15FF7C81D0435670076CDE3 /* People.swift in Sources */, 673ABE3A21360CBE00B1784B /* Group.swift in Sources */, + 95ECF0682C9B851A006364D2 /* Data+Compression.swift in Sources */, E11594A11D01C597007F8B4F /* Track.swift in Sources */, E11594991D01689F007F8B4F /* JSONHandler.swift in Sources */, E1D335D01D3059A800E68E12 /* AutomaticProperties.swift in Sources */, @@ -540,6 +548,7 @@ E12782BF1D4AB5CB0025FB05 /* MixpanelInstance.swift in Sources */, E12782C11D4AB5CB0025FB05 /* Network.swift in Sources */, 8625BEBC26D045CE0009BAA9 /* MPDB.swift in Sources */, + 95ECF0692C9B851B006364D2 /* Data+Compression.swift in Sources */, E12782C21D4AB5CB0025FB05 /* JSONHandler.swift in Sources */, E12782C31D4AB5CB0025FB05 /* Flush.swift in Sources */, E12782C41D4AB5CB0025FB05 /* FlushRequest.swift in Sources */, @@ -568,6 +577,7 @@ E1F15FD61E64B5FC00391AE3 /* FlushRequest.swift in Sources */, E1F15FD71E64B60200391AE3 /* PrintLogging.swift in Sources */, 8625BEBD26D045CE0009BAA9 /* MPDB.swift in Sources */, + 95ECF06A2C9B851B006364D2 /* Data+Compression.swift in Sources */, E1F15FE21E64B60D00391AE3 /* Flush.swift in Sources */, E1F15FD51E64B5F800391AE3 /* Network.swift in Sources */, E1F15FDE1E64B60A00391AE3 /* MixpanelType.swift in Sources */, @@ -805,7 +815,7 @@ HEADER_SEARCH_PATHS = ""; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; OTHER_SWIFT_FLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = com.mixpanel.Mixpanel; @@ -853,7 +863,7 @@ HEADER_SEARCH_PATHS = ""; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; OTHER_SWIFT_FLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = com.mixpanel.Mixpanel; diff --git a/MixpanelDemo/MixpanelDemoTests/MixpanelDemoTests.swift b/MixpanelDemo/MixpanelDemoTests/MixpanelDemoTests.swift index f85359aa..36052d58 100644 --- a/MixpanelDemo/MixpanelDemoTests/MixpanelDemoTests.swift +++ b/MixpanelDemo/MixpanelDemoTests/MixpanelDemoTests.swift @@ -1189,4 +1189,14 @@ class MixpanelDemoTests: MixpanelBaseTests { } } } + + func testGzipCompressionInit() { + let testMixpanel = Mixpanel.initialize(token: randomId(), trackAutomaticEvents: false, useGzipCompression: true) + XCTAssertTrue(testMixpanel.useGzipCompression == true, "the init of GzipCompression failed") + } + + func testGzipCompressionDefault() { + let testMixpanel = Mixpanel.initialize(token: randomId(), trackAutomaticEvents: false) + XCTAssertTrue(testMixpanel.useGzipCompression == false, "the default gzip option disabled failed") + } } diff --git a/Sources/Constants.swift b/Sources/Constants.swift index 43a51456..88796d92 100644 --- a/Sources/Constants.swift +++ b/Sources/Constants.swift @@ -27,6 +27,10 @@ struct BundleConstants { static let ID = "com.mixpanel.Mixpanel" } +struct GzipSettings { + static let gzipHeaderOffset = Int32(16) +} + #if !os(OSX) && !os(watchOS) && !os(visionOS) extension UIDevice { var iPhoneX: Bool { diff --git a/Sources/Data+Compression.swift b/Sources/Data+Compression.swift new file mode 100644 index 00000000..faf2b200 --- /dev/null +++ b/Sources/Data+Compression.swift @@ -0,0 +1,93 @@ +// +// Data+Compression.swift +// MixpanelSessionReplay +// +// Copyright © 2024 Mixpanel. All rights reserved. +// + +import Foundation +import zlib + +public enum GzipError: Swift.Error { + case stream + case data + case memory + case buffer + case version + case unknown(code: Int) + + init(code: Int32) { + switch code { + case Z_STREAM_ERROR: + self = .stream + case Z_DATA_ERROR: + self = .data + case Z_MEM_ERROR: + self = .memory + case Z_BUF_ERROR: + self = .buffer + case Z_VERSION_ERROR: + self = .version + default: + self = .unknown(code: Int(code)) + } + } +} + +extension Data { + /// Compresses the data using gzip compression. + /// Adapted from: https://github.com/1024jp/GzipSwift/blob/main/Sources/Gzip/Data%2BGzip.swift + /// - Parameter level: Compression level. + /// - Returns: The compressed data. + /// - Throws: `GzipError` if compression fails. + public func gzipCompressed(level: Int32 = Z_DEFAULT_COMPRESSION) throws -> Data { + guard !self.isEmpty else { + Logger.warn(message: "Empty Data object cannot be compressed.") + return Data() + } + + let originalSize = self.count + + var stream = z_stream() + stream.next_in = UnsafeMutablePointer(mutating: (self as NSData).bytes.bindMemory(to: Bytef.self, capacity: self.count)) + stream.avail_in = uint(self.count) + + let windowBits = MAX_WBITS + GzipSettings.gzipHeaderOffset // Use gzip header instead of zlib header + let memLevel = MAX_MEM_LEVEL + let strategy = Z_DEFAULT_STRATEGY + + var status = deflateInit2_(&stream, level, Z_DEFLATED, windowBits, memLevel, strategy, ZLIB_VERSION, Int32(MemoryLayout.size)) + guard status == Z_OK else { + throw GzipError(code: status) + } + + var compressedData = Data(count: self.count / 2) + repeat { + if Int(stream.total_out) >= compressedData.count { + compressedData.count += self.count / 2 + } + stream.next_out = compressedData.withUnsafeMutableBytes { $0.baseAddress!.assumingMemoryBound(to: Bytef.self) }.advanced(by: Int(stream.total_out)) + stream.avail_out = uint(compressedData.count) - uint(stream.total_out) + + status = deflate(&stream, Z_FINISH) + } while stream.avail_out == 0 && status == Z_OK + + guard status == Z_STREAM_END else { + throw GzipError(code: status) + } + + deflateEnd(&stream) + compressedData.count = Int(stream.total_out) + + let compressedSize = compressedData.count + let compressionRatio = Double(compressedSize) / Double(originalSize) + let compressionPercentage = (1 - compressionRatio) * 100 + + let roundedCompressionRatio = floor(compressionRatio * 1000) / 1000 + let roundedCompressionPercentage = floor(compressionPercentage * 1000) / 1000 + + Logger.info(message: "Payload gzipped: original size = \(originalSize) bytes, compressed size = \(compressedSize) bytes, compression ratio = \(roundedCompressionRatio), compression percentage = \(roundedCompressionPercentage)%") + + return compressedData + } +} diff --git a/Sources/Flush.swift b/Sources/Flush.swift index a11618bb..222457bd 100644 --- a/Sources/Flush.swift +++ b/Sources/Flush.swift @@ -28,6 +28,7 @@ class Flush: AppLifecycle { private var _serverURL = BasePath.DefaultMixpanelAPI private let flushRequestReadWriteLock: DispatchQueue + var useGzipCompression: Bool var serverURL: String { get { @@ -68,8 +69,9 @@ class Flush: AppLifecycle { } } - required init(serverURL: String) { + required init(serverURL: String, useGzipCompression: Bool) { self.flushRequest = FlushRequest(serverURL: serverURL) + self.useGzipCompression = useGzipCompression _serverURL = serverURL flushRequestReadWriteLock = DispatchQueue(label: "com.mixpanel.flush_interval.lock", qos: .utility, attributes: .concurrent, autoreleaseFrequency: .workItem) } @@ -135,7 +137,7 @@ class Flush: AppLifecycle { type: type, useIP: useIPAddressForGeoLocation, headers: headers, - queryItems: queryItems) + queryItems: queryItems, useGzipCompression: useGzipCompression) #if os(iOS) if !MixpanelInstance.isiOSAppExtension() { delegate?.updateNetworkActivityIndicator(false) diff --git a/Sources/FlushRequest.swift b/Sources/FlushRequest.swift index bf260364..806866f0 100644 --- a/Sources/FlushRequest.swift +++ b/Sources/FlushRequest.swift @@ -23,7 +23,8 @@ class FlushRequest: Network { type: FlushType, useIP: Bool, headers: [String: String], - queryItems: [URLQueryItem] = []) -> Bool { + queryItems: [URLQueryItem] = [], + useGzipCompression: Bool) -> Bool { let responseParser: (Data) -> Int? = { data in let response = String(data: data, encoding: String.Encoding.utf8) @@ -33,14 +34,25 @@ class FlushRequest: Network { return nil } - let resourceHeaders: [String: String] = ["Content-Type": "application/json"].merging(headers) {(_,new) in new } - + var resourceHeaders: [String: String] = ["Content-Type": "application/json"].merging(headers) {(_,new) in new } + var compressedData: Data? = nil + + if useGzipCompression && type == .events { + if let requestDataRaw = requestData.data(using: .utf8) { + do { + compressedData = try requestDataRaw.gzipCompressed() + resourceHeaders["Content-Encoding"] = "gzip" + } catch { + Logger.error(message: "Failed to compress data with gzip: \(error)") + } + } + } 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), + requestBody: compressedData ?? requestData.data(using: .utf8), queryItems: resourceQueryItems, headers: resourceHeaders, parse: responseParser) diff --git a/Sources/Mixpanel.swift b/Sources/Mixpanel.swift index 0c5f4766..36bf8f64 100644 --- a/Sources/Mixpanel.swift +++ b/Sources/Mixpanel.swift @@ -31,6 +31,7 @@ open class Mixpanel { - 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 serverURL: Optional. Mixpanel cluster URL + - parameter useGzipCompression: Optional. Whether to use gzip compression for network requests. - 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. @@ -46,7 +47,8 @@ open class Mixpanel { optOutTrackingByDefault: Bool = false, useUniqueDistinctId: Bool = false, superProperties: Properties? = nil, - serverURL: String? = nil) -> MixpanelInstance { + serverURL: String? = nil, + useGzipCompression: Bool = false) -> MixpanelInstance { return MixpanelManager.sharedInstance.initialize(token: apiToken, flushInterval: flushInterval, instanceName: ((instanceName != nil) ? instanceName! : apiToken), @@ -54,7 +56,8 @@ open class Mixpanel { optOutTrackingByDefault: optOutTrackingByDefault, useUniqueDistinctId: useUniqueDistinctId, superProperties: superProperties, - serverURL: serverURL) + serverURL: serverURL, + useGzipCompression: useGzipCompression) } /** @@ -73,6 +76,7 @@ open class Mixpanel { - 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. + - parameter useGzipCompression: Optional. Whether to use gzip compression for network requests. - 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. @@ -89,7 +93,8 @@ open class Mixpanel { optOutTrackingByDefault: Bool = false, useUniqueDistinctId: Bool = false, superProperties: Properties? = nil, - proxyServerConfig: ProxyServerConfig) -> MixpanelInstance { + proxyServerConfig: ProxyServerConfig, + useGzipCompression: Bool = false) -> MixpanelInstance { return MixpanelManager.sharedInstance.initialize(token: apiToken, flushInterval: flushInterval, instanceName: ((instanceName != nil) ? instanceName! : apiToken), @@ -97,7 +102,8 @@ open class Mixpanel { optOutTrackingByDefault: optOutTrackingByDefault, useUniqueDistinctId: useUniqueDistinctId, superProperties: superProperties, - proxyServerConfig: proxyServerConfig) + proxyServerConfig: proxyServerConfig, + useGzipCompression: useGzipCompression) } #else /** @@ -115,6 +121,7 @@ open class Mixpanel { - 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 serverURL: Optional. Mixpanel cluster URL + - parameter useGzipCompression: Optional. Whether to use gzip compression for network requests. - 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. @@ -130,7 +137,8 @@ open class Mixpanel { optOutTrackingByDefault: Bool = false, useUniqueDistinctId: Bool = false, superProperties: Properties? = nil, - serverURL: String? = nil) -> MixpanelInstance { + serverURL: String? = nil, + useGzipCompression: Bool = false) -> MixpanelInstance { return MixpanelManager.sharedInstance.initialize(token: apiToken, flushInterval: flushInterval, instanceName: ((instanceName != nil) ? instanceName! : apiToken), @@ -138,7 +146,8 @@ open class Mixpanel { optOutTrackingByDefault: optOutTrackingByDefault, useUniqueDistinctId: useUniqueDistinctId, superProperties: superProperties, - serverURL: serverURL) + serverURL: serverURL, + useGzipCompression: useGzipCompression) } /** @@ -156,6 +165,7 @@ open class Mixpanel { - 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. + - parameter useGzipCompression: Optional. Whether to use gzip compression for network requests. - 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. @@ -171,7 +181,8 @@ open class Mixpanel { optOutTrackingByDefault: Bool = false, useUniqueDistinctId: Bool = false, superProperties: Properties? = nil, - proxyServerConfig: ProxyServerConfig) -> MixpanelInstance { + proxyServerConfig: ProxyServerConfig, + useGzipCompression: Bool = false) -> MixpanelInstance { return MixpanelManager.sharedInstance.initialize(token: apiToken, flushInterval: flushInterval, instanceName: ((instanceName != nil) ? instanceName! : apiToken), @@ -179,7 +190,8 @@ open class Mixpanel { optOutTrackingByDefault: optOutTrackingByDefault, useUniqueDistinctId: useUniqueDistinctId, superProperties: superProperties, - proxyServerConfig: proxyServerConfig) + proxyServerConfig: proxyServerConfig, + useGzipCompression: useGzipCompression) } #endif // os(OSX) @@ -260,7 +272,8 @@ final class MixpanelManager { optOutTrackingByDefault: Bool = false, useUniqueDistinctId: Bool = false, superProperties: Properties? = nil, - serverURL: String? = nil + serverURL: String? = nil, + useGzipCompression: Bool = false ) -> MixpanelInstance { return dequeueInstance(instanceName: instanceName) { return MixpanelInstance(apiToken: apiToken, @@ -270,7 +283,8 @@ final class MixpanelManager { optOutTrackingByDefault: optOutTrackingByDefault, useUniqueDistinctId: useUniqueDistinctId, superProperties: superProperties, - serverURL: serverURL) + serverURL: serverURL, + useGzipCompression: useGzipCompression) } } @@ -281,7 +295,8 @@ final class MixpanelManager { optOutTrackingByDefault: Bool = false, useUniqueDistinctId: Bool = false, superProperties: Properties? = nil, - proxyServerConfig: ProxyServerConfig + proxyServerConfig: ProxyServerConfig, + useGzipCompression: Bool = false ) -> MixpanelInstance { return dequeueInstance(instanceName: instanceName) { return MixpanelInstance(apiToken: apiToken, @@ -291,7 +306,8 @@ final class MixpanelManager { optOutTrackingByDefault: optOutTrackingByDefault, useUniqueDistinctId: useUniqueDistinctId, superProperties: superProperties, - proxyServerConfig: proxyServerConfig) + proxyServerConfig: proxyServerConfig, + useGzipCompression: useGzipCompression) } } diff --git a/Sources/MixpanelInstance.swift b/Sources/MixpanelInstance.swift index bda85a8f..63f22ec6 100644 --- a/Sources/MixpanelInstance.swift +++ b/Sources/MixpanelInstance.swift @@ -170,6 +170,12 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele flushInstance.serverURL = serverURL } } + + open var useGzipCompression: Bool = false { + didSet { + flushInstance.useGzipCompression = useGzipCompression + } + } /// The a MixpanelProxyServerDelegate object that gives config control over Proxy Server's network activity. open weak var proxyServerDelegate: MixpanelProxyServerDelegate? = nil @@ -270,7 +276,8 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele optOutTrackingByDefault: Bool = false, useUniqueDistinctId: Bool = false, superProperties: Properties? = nil, - proxyServerConfig: ProxyServerConfig + proxyServerConfig: ProxyServerConfig, + useGzipCompression: Bool = false ) { self.init(apiToken: apiToken, flushInterval: flushInterval, @@ -280,7 +287,8 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele useUniqueDistinctId: useUniqueDistinctId, superProperties: superProperties, serverURL: proxyServerConfig.serverUrl, - proxyServerDelegate: proxyServerConfig.delegate) + proxyServerDelegate: proxyServerConfig.delegate, + useGzipCompression: useGzipCompression) } convenience init( @@ -291,7 +299,8 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele optOutTrackingByDefault: Bool = false, useUniqueDistinctId: Bool = false, superProperties: Properties? = nil, - serverURL: String? = nil + serverURL: String? = nil, + useGzipCompression: Bool = false ) { self.init(apiToken: apiToken, flushInterval: flushInterval, @@ -301,7 +310,8 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele useUniqueDistinctId: useUniqueDistinctId, superProperties: superProperties, serverURL: serverURL, - proxyServerDelegate: nil) + proxyServerDelegate: nil, + useGzipCompression: useGzipCompression) } @@ -314,7 +324,8 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele useUniqueDistinctId: Bool = false, superProperties: Properties? = nil, serverURL: String? = nil, - proxyServerDelegate: MixpanelProxyServerDelegate? = nil + proxyServerDelegate: MixpanelProxyServerDelegate? = nil, + useGzipCompression: Bool = false ) { if let apiToken = apiToken, !apiToken.isEmpty { self.apiToken = apiToken @@ -323,6 +334,7 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele if let serverURL = serverURL { self.serverURL = serverURL } + self.useGzipCompression = useGzipCompression self.proxyServerDelegate = proxyServerDelegate let label = "com.mixpanel.\(self.apiToken)" trackingQueue = DispatchQueue(label: "\(label).tracking)", qos: .utility, autoreleaseFrequency: .workItem) @@ -334,7 +346,7 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele self.useUniqueDistinctId = useUniqueDistinctId readWriteLock = ReadWriteLock(label: "com.mixpanel.globallock") - flushInstance = Flush(serverURL: self.serverURL) + flushInstance = Flush(serverURL: self.serverURL, useGzipCompression: useGzipCompression) sessionMetadata = SessionMetadata(trackingQueue: trackingQueue) trackInstance = Track(apiToken: self.apiToken, instanceName: self.name,