mirror of
https://github.com/mixpanel/mixpanel-swift
synced 2026-04-21 13:37:18 +00:00
add unit test cases for migration
lint fixes
This commit is contained in:
parent
74bce0de3c
commit
6ee1f2cfd6
21 changed files with 248 additions and 131 deletions
|
|
@ -61,3 +61,4 @@ disabled_rules:
|
|||
- type_body_length
|
||||
- variable_name
|
||||
- trailing_whitespace
|
||||
- unused_optional_binding
|
||||
|
|
|
|||
|
|
@ -28,6 +28,11 @@
|
|||
8654F3002671636B00ACEED5 /* MixpanelPeopleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8654F2FB2671636B00ACEED5 /* MixpanelPeopleTests.swift */; };
|
||||
8654F3012671636B00ACEED5 /* MixpanelOptOutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8654F2FC2671636B00ACEED5 /* MixpanelOptOutTests.swift */; };
|
||||
8654F3052671C4B000ACEED5 /* MixpanelGroupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8654F3032671C4B000ACEED5 /* MixpanelGroupTests.swift */; };
|
||||
8675E9A126DF356B0096858F /* mixpanel-testToken-events in Resources */ = {isa = PBXBuildFile; fileRef = 8675E99C26DF356A0096858F /* mixpanel-testToken-events */; };
|
||||
8675E9A226DF356B0096858F /* mixpanel-testToken-groups in Resources */ = {isa = PBXBuildFile; fileRef = 8675E99E26DF356A0096858F /* mixpanel-testToken-groups */; };
|
||||
8675E9A326DF356B0096858F /* mixpanel-testToken-people in Resources */ = {isa = PBXBuildFile; fileRef = 8675E99F26DF356A0096858F /* mixpanel-testToken-people */; };
|
||||
8675E9AB26E144FE0096858F /* mixpanel-testToken-optOutStatus in Resources */ = {isa = PBXBuildFile; fileRef = 8675E9AA26E144FE0096858F /* mixpanel-testToken-optOutStatus */; };
|
||||
8675E9B426E171AE0096858F /* mixpanel-testToken-properties in Resources */ = {isa = PBXBuildFile; fileRef = 8675E9B326E171AE0096858F /* mixpanel-testToken-properties */; };
|
||||
86F86E8F22440C5D00B69832 /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 86F86E8D22440C5C00B69832 /* Interface.storyboard */; };
|
||||
86F86E9122440C5F00B69832 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 86F86E9022440C5F00B69832 /* Assets.xcassets */; };
|
||||
86F86E9822440C5F00B69832 /* MixpanelDemoWatch Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 86F86E9722440C5F00B69832 /* MixpanelDemoWatch Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
|
|
@ -275,6 +280,11 @@
|
|||
8654F2FB2671636B00ACEED5 /* MixpanelPeopleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MixpanelPeopleTests.swift; sourceTree = "<group>"; };
|
||||
8654F2FC2671636B00ACEED5 /* MixpanelOptOutTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MixpanelOptOutTests.swift; sourceTree = "<group>"; };
|
||||
8654F3032671C4B000ACEED5 /* MixpanelGroupTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MixpanelGroupTests.swift; sourceTree = "<group>"; };
|
||||
8675E99C26DF356A0096858F /* mixpanel-testToken-events */ = {isa = PBXFileReference; explicitFileType = text.pbxproject; path = "mixpanel-testToken-events"; sourceTree = "<group>"; };
|
||||
8675E99E26DF356A0096858F /* mixpanel-testToken-groups */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = "mixpanel-testToken-groups"; sourceTree = "<group>"; };
|
||||
8675E99F26DF356A0096858F /* mixpanel-testToken-people */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = "mixpanel-testToken-people"; sourceTree = "<group>"; };
|
||||
8675E9AA26E144FE0096858F /* mixpanel-testToken-optOutStatus */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = "mixpanel-testToken-optOutStatus"; sourceTree = "<group>"; };
|
||||
8675E9B326E171AE0096858F /* mixpanel-testToken-properties */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = "mixpanel-testToken-properties"; sourceTree = "<group>"; };
|
||||
86F86E8B22440C5C00B69832 /* MixpanelDemoWatch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MixpanelDemoWatch.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
86F86E8E22440C5C00B69832 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Interface.storyboard; sourceTree = "<group>"; };
|
||||
86F86E9022440C5F00B69832 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
|
|
@ -323,13 +333,6 @@
|
|||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
0AAB97762ADB463FE09D6381 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
8654F2C1266ED84F00ACEED5 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
|
@ -470,6 +473,18 @@
|
|||
path = MixpanelDemoMacUITests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8675E9A626DF37B20096858F /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8675E99C26DF356A0096858F /* mixpanel-testToken-events */,
|
||||
8675E9B326E171AE0096858F /* mixpanel-testToken-properties */,
|
||||
8675E99E26DF356A0096858F /* mixpanel-testToken-groups */,
|
||||
8675E99F26DF356A0096858F /* mixpanel-testToken-people */,
|
||||
8675E9AA26E144FE0096858F /* mixpanel-testToken-optOutStatus */,
|
||||
);
|
||||
name = Resources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
86F86E8C22440C5C00B69832 /* MixpanelDemoWatch */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -524,6 +539,7 @@
|
|||
E12BD03A1D8A14D6008989C9 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8675E9A626DF37B20096858F /* Resources */,
|
||||
E12BD03C1D8A14F3008989C9 /* Assets.xcassets */,
|
||||
E15FF7ED1D0461130076CDE3 /* Info.plist */,
|
||||
);
|
||||
|
|
@ -825,11 +841,6 @@
|
|||
LastUpgradeCheck = 1200;
|
||||
ORGANIZATIONNAME = Mixpanel;
|
||||
TargetAttributes = {
|
||||
60DDD14623D39620004F7CBB = {
|
||||
CreatedOnToolsVersion = 11.2;
|
||||
DevelopmentTeam = E8FVX7QLET;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
8654F2C3266ED84F00ACEED5 = {
|
||||
CreatedOnToolsVersion = 12.5;
|
||||
};
|
||||
|
|
@ -960,13 +971,6 @@
|
|||
/* End PBXReferenceProxy section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
60DDD14523D39620004F7CBB /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
8654F2C2266ED84F00ACEED5 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
|
@ -1045,6 +1049,11 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
E12BD03D1D8A14F3008989C9 /* Assets.xcassets in Resources */,
|
||||
8675E9A226DF356B0096858F /* mixpanel-testToken-groups in Resources */,
|
||||
8675E9B426E171AE0096858F /* mixpanel-testToken-properties in Resources */,
|
||||
8675E9A126DF356B0096858F /* mixpanel-testToken-events in Resources */,
|
||||
8675E9A326DF356B0096858F /* mixpanel-testToken-people in Resources */,
|
||||
8675E9AB26E144FE0096858F /* mixpanel-testToken-optOutStatus in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -988,4 +988,84 @@ class MixpanelDemoTests: MixpanelBaseTests {
|
|||
removeDBfile(testToken)
|
||||
}
|
||||
|
||||
func testMigration() {
|
||||
let token = "testToken"
|
||||
// clean up
|
||||
removeDBfile(token)
|
||||
// copy the legacy archived file for the migration test
|
||||
let legacyFiles = ["mixpanel-testToken-events", "mixpanel-testToken-properties", "mixpanel-testToken-groups", "mixpanel-testToken-people", "mixpanel-testToken-optOutStatus"]
|
||||
prepareForMigrationFiles(legacyFiles)
|
||||
// initialize mixpanel will do the migration automatically if found legacy archive files.
|
||||
let testMixpanel = Mixpanel.initialize(token: token, flushInterval: 60)
|
||||
let fileManager = FileManager.default
|
||||
let libraryUrls = fileManager.urls(for: .libraryDirectory,
|
||||
in: .userDomainMask)
|
||||
XCTAssertFalse(fileManager.fileExists(atPath: (libraryUrls.first?.appendingPathComponent("mixpanel-testToken-events"))!.path), "after migration, the legacy archive files should be removed")
|
||||
XCTAssertFalse(fileManager.fileExists(atPath: (libraryUrls.first?.appendingPathComponent("mixpanel-testToken-properties"))!.path), "after migration, the legacy archive files should be removed")
|
||||
XCTAssertFalse(fileManager.fileExists(atPath: (libraryUrls.first?.appendingPathComponent("mixpanel-testToken-groups"))!.path), "after migration, the legacy archive files should be removed")
|
||||
XCTAssertFalse(fileManager.fileExists(atPath: (libraryUrls.first?.appendingPathComponent("mixpanel-testToken-people"))!.path), "after migration, the legacy archive files should be removed")
|
||||
|
||||
let events = eventQueue(token: testMixpanel.apiToken)
|
||||
XCTAssertEqual(events.count, 306)
|
||||
|
||||
XCTAssertEqual(events[0]["event"] as? String, "$identify")
|
||||
XCTAssertEqual(events[1]["event"] as? String, "Logged in")
|
||||
XCTAssertEqual(events[2]["event"] as? String, "$ae_first_open")
|
||||
XCTAssertEqual(events[3]["event"] as? String, "Tracked event 1")
|
||||
let properties = events.last?["properties"] as? InternalProperties
|
||||
XCTAssertEqual(properties?["Cool Property"] as? [Int], [12345,301])
|
||||
XCTAssertEqual(properties?["Super Property 2"] as? String, "p2")
|
||||
|
||||
let people = peopleQueue(token: testMixpanel.apiToken)
|
||||
XCTAssertEqual(people.count, 6)
|
||||
XCTAssertEqual(people[0]["$distinct_id"] as? String, "demo_user")
|
||||
XCTAssertEqual(people[0]["$token"] as? String, "testToken")
|
||||
let appendProperties = people[5]["$append"] as! InternalProperties
|
||||
XCTAssertEqual(appendProperties["d"] as? String, "goodbye")
|
||||
|
||||
let group = groupQueue(token: testMixpanel.apiToken)
|
||||
XCTAssertEqual(group.count, 2)
|
||||
XCTAssertEqual(group[0]["$group_key"] as? String, "Cool Property")
|
||||
let setProperties = group[0]["$set"] as! InternalProperties
|
||||
XCTAssertEqual(setProperties["g"] as? String, "yo")
|
||||
let setProperties2 = group[1]["$set"] as! InternalProperties
|
||||
XCTAssertEqual(setProperties2["a"] as? Int, 1)
|
||||
XCTAssertTrue(MixpanelPersistence.loadOptOutStatusFlag(apiToken: token)!)
|
||||
XCTAssertTrue(MixpanelPersistence.loadAutomacticEventsEnabledFlag(apiToken: token))
|
||||
|
||||
//timedEvents
|
||||
let testTimedEvents = MixpanelPersistence.loadTimedEvents(apiToken: token)
|
||||
XCTAssertEqual(testTimedEvents.count, 3)
|
||||
XCTAssertNotNil(testTimedEvents["Time Event A"])
|
||||
XCTAssertNotNil(testTimedEvents["Time Event B"])
|
||||
XCTAssertNotNil(testTimedEvents["Time Event C"])
|
||||
let identity = MixpanelPersistence.loadIdentity(apiToken: token)
|
||||
XCTAssertEqual(identity.distinctID, "demo_user")
|
||||
XCTAssertEqual(identity.peopleDistinctID, "demo_user")
|
||||
XCTAssertNotNil(identity.anonymousId)
|
||||
XCTAssertEqual(identity.userId, "demo_user")
|
||||
XCTAssertEqual(identity.alias, "New Alias")
|
||||
XCTAssertEqual(identity.hadPersistedDistinctId, false)
|
||||
|
||||
let superProperties = MixpanelPersistence.loadSuperProperties(apiToken: token)
|
||||
XCTAssertEqual(superProperties.count, 7)
|
||||
XCTAssertEqual(superProperties["Super Property 1"] as? Int, 1)
|
||||
XCTAssertEqual(superProperties["Super Property 7"] as? NSNull, NSNull())
|
||||
removeDBfile("testToken")
|
||||
}
|
||||
|
||||
func prepareForMigrationFiles(_ fileNames: [String]) {
|
||||
for fileName in fileNames {
|
||||
let fileManager = FileManager.default
|
||||
let filepath = Bundle(for: type(of: self)).url(forResource: fileName, withExtension: nil)!
|
||||
let libraryUrls = fileManager.urls(for: .libraryDirectory,
|
||||
in: .userDomainMask)
|
||||
let destURL = libraryUrls.first?.appendingPathComponent(fileName)
|
||||
do {
|
||||
try FileManager.default.copyItem(at: filepath, to: destURL!)
|
||||
} catch let error {
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
BIN
MixpanelDemo/MixpanelDemoTests/mixpanel-testToken-events
Normal file
BIN
MixpanelDemo/MixpanelDemoTests/mixpanel-testToken-events
Normal file
Binary file not shown.
BIN
MixpanelDemo/MixpanelDemoTests/mixpanel-testToken-groups
Normal file
BIN
MixpanelDemo/MixpanelDemoTests/mixpanel-testToken-groups
Normal file
Binary file not shown.
BIN
MixpanelDemo/MixpanelDemoTests/mixpanel-testToken-optOutStatus
Normal file
BIN
MixpanelDemo/MixpanelDemoTests/mixpanel-testToken-optOutStatus
Normal file
Binary file not shown.
BIN
MixpanelDemo/MixpanelDemoTests/mixpanel-testToken-people
Normal file
BIN
MixpanelDemo/MixpanelDemoTests/mixpanel-testToken-people
Normal file
Binary file not shown.
BIN
MixpanelDemo/MixpanelDemoTests/mixpanel-testToken-properties
Normal file
BIN
MixpanelDemo/MixpanelDemoTests/mixpanel-testToken-properties
Normal file
Binary file not shown.
|
|
@ -8,10 +8,10 @@ let package = Package(
|
|||
.iOS(.v9),
|
||||
.tvOS(.v9),
|
||||
.macOS(.v10_10),
|
||||
.watchOS(.v3),
|
||||
.watchOS(.v3)
|
||||
],
|
||||
products: [
|
||||
.library(name: "Mixpanel", targets: ["Mixpanel"]),
|
||||
.library(name: "Mixpanel", targets: ["Mixpanel"])
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
|
|
@ -21,8 +21,8 @@ let package = Package(
|
|||
"Info.plist"
|
||||
],
|
||||
swiftSettings: [
|
||||
.define("DECIDE", .when(platforms: [.iOS])),
|
||||
.define("DECIDE", .when(platforms: [.iOS]))
|
||||
]
|
||||
),
|
||||
)
|
||||
]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
// Copyright © 2017 Mixpanel. All rights reserved.
|
||||
//
|
||||
|
||||
protocol AEDelegate {
|
||||
protocol AEDelegate: AnyObject {
|
||||
func track(event: String?, properties: Properties?)
|
||||
func setOnce(properties: Properties)
|
||||
func increment(property: String, by: Double)
|
||||
|
|
@ -21,26 +21,26 @@ class AutomaticEvents: NSObject, SKPaymentTransactionObserver, SKProductsRequest
|
|||
|
||||
var _minimumSessionDuration: UInt64 = 10000
|
||||
var minimumSessionDuration: UInt64 {
|
||||
set {
|
||||
_minimumSessionDuration = newValue
|
||||
}
|
||||
get {
|
||||
return _minimumSessionDuration
|
||||
}
|
||||
set {
|
||||
_minimumSessionDuration = newValue
|
||||
}
|
||||
}
|
||||
var _maximumSessionDuration: UInt64 = UINT64_MAX
|
||||
var maximumSessionDuration: UInt64 {
|
||||
set {
|
||||
_maximumSessionDuration = newValue
|
||||
}
|
||||
get {
|
||||
return _maximumSessionDuration
|
||||
}
|
||||
set {
|
||||
_maximumSessionDuration = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var awaitingTransactions = [String: SKPaymentTransaction]()
|
||||
let defaults = UserDefaults(suiteName: "Mixpanel")
|
||||
var delegate: AEDelegate?
|
||||
weak var delegate: AEDelegate?
|
||||
var sessionLength: TimeInterval = 0
|
||||
var sessionStartTime: TimeInterval = Date().timeIntervalSince1970
|
||||
var hasAddedObserver = false
|
||||
|
|
@ -121,7 +121,6 @@ class AutomaticEvents: NSObject, SKPaymentTransactionObserver, SKProductsRequest
|
|||
case .purchased:
|
||||
productIdentifiers.insert(trans.payment.productIdentifier)
|
||||
awaitingTransactions[trans.payment.productIdentifier] = trans
|
||||
break
|
||||
case .failed: break
|
||||
case .restored: break
|
||||
default: break
|
||||
|
|
@ -172,4 +171,3 @@ class AutomaticEvents: NSObject, SKPaymentTransactionObserver, SKProductsRequest
|
|||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ class Decide {
|
|||
let lock: ReadWriteLock
|
||||
var decideFetched = false
|
||||
let mixpanelPersistence: MixpanelPersistence
|
||||
|
||||
|
||||
required init(basePathIdentifier: String, lock: ReadWriteLock, mixpanelPersistence: MixpanelPersistence) {
|
||||
self.decideRequest = DecideRequest(basePathIdentifier: basePathIdentifier)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
protocol FlushDelegate {
|
||||
protocol FlushDelegate: AnyObject {
|
||||
func flush(completion: (() -> Void)?)
|
||||
func flushSuccess(type: FlushType, ids: [Int32])
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ protocol FlushDelegate {
|
|||
|
||||
class Flush: AppLifecycle {
|
||||
var timer: Timer?
|
||||
var delegate: FlushDelegate?
|
||||
weak var delegate: FlushDelegate?
|
||||
var useIPAddressForGeoLocation = true
|
||||
var flushRequest: FlushRequest
|
||||
var flushOnBackground = true
|
||||
|
|
@ -27,6 +27,11 @@ class Flush: AppLifecycle {
|
|||
private let flushIntervalReadWriteLock: DispatchQueue
|
||||
|
||||
var flushInterval: Double {
|
||||
get {
|
||||
flushIntervalReadWriteLock.sync {
|
||||
return _flushInterval
|
||||
}
|
||||
}
|
||||
set {
|
||||
flushIntervalReadWriteLock.sync(flags: .barrier, execute: {
|
||||
_flushInterval = newValue
|
||||
|
|
@ -35,11 +40,6 @@ class Flush: AppLifecycle {
|
|||
delegate?.flush(completion: nil)
|
||||
startFlushTimer()
|
||||
}
|
||||
get {
|
||||
flushIntervalReadWriteLock.sync {
|
||||
return _flushInterval
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
required init(basePathIdentifier: String) {
|
||||
|
|
@ -47,7 +47,6 @@ class Flush: AppLifecycle {
|
|||
flushIntervalReadWriteLock = DispatchQueue(label: "com.mixpanel.flush_interval.lock", qos: .utility, attributes: .concurrent)
|
||||
}
|
||||
|
||||
|
||||
func flushQueue(type: FlushType, queue: Queue) {
|
||||
if flushRequest.requestNotAllowed() {
|
||||
return
|
||||
|
|
@ -119,7 +118,9 @@ class Flush: AppLifecycle {
|
|||
if success {
|
||||
// remove
|
||||
self.delegate?.flushSuccess(type: type, ids: ids)
|
||||
mutableQueue = self.removeProcessedBatch(batchSize: batchSize, queue: mutableQueue, type: type)
|
||||
mutableQueue = self.removeProcessedBatch(batchSize: batchSize,
|
||||
queue: mutableQueue,
|
||||
type: type)
|
||||
}
|
||||
shouldContinue = success
|
||||
semaphore.signal()
|
||||
|
|
@ -143,7 +144,6 @@ class Flush: AppLifecycle {
|
|||
return shadowQueue
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Lifecycle
|
||||
func applicationDidBecomeActive() {
|
||||
startFlushTimer()
|
||||
|
|
|
|||
|
|
@ -17,11 +17,17 @@ open class Group {
|
|||
let lock: ReadWriteLock
|
||||
let groupKey: String
|
||||
let groupID: MixpanelType
|
||||
var delegate: FlushDelegate?
|
||||
weak var delegate: FlushDelegate?
|
||||
let metadata: SessionMetadata
|
||||
let mixpanelPersistence: MixpanelPersistence
|
||||
|
||||
init(apiToken: String, serialQueue: DispatchQueue, lock: ReadWriteLock, groupKey: String, groupID: MixpanelType, metadata: SessionMetadata, mixpanelPersistence: MixpanelPersistence) {
|
||||
init(apiToken: String,
|
||||
serialQueue: DispatchQueue,
|
||||
lock: ReadWriteLock,
|
||||
groupKey: String,
|
||||
groupID: MixpanelType,
|
||||
metadata: SessionMetadata,
|
||||
mixpanelPersistence: MixpanelPersistence) {
|
||||
self.apiToken = apiToken
|
||||
self.serialQueue = serialQueue
|
||||
self.lock = lock
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class JSONHandler {
|
|||
}
|
||||
|
||||
class func deserializeData(_ data: Data) -> MPObjectToParse? {
|
||||
var object: MPObjectToParse? = nil
|
||||
var object: MPObjectToParse?
|
||||
do {
|
||||
object = try JSONSerialization.jsonObject(with: data, options: [])
|
||||
} catch {
|
||||
|
|
|
|||
|
|
@ -53,8 +53,7 @@ class MPDB {
|
|||
if sqlite3_open_v2(dbPath, &connection, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nil) != SQLITE_OK {
|
||||
logSqlError(message: "Error opening or creating database at path: \(dbPath)")
|
||||
close()
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
Logger.info(message: "Successfully opened connection to database at path: \(dbPath)")
|
||||
createTables()
|
||||
}
|
||||
|
|
@ -76,8 +75,7 @@ class MPDB {
|
|||
try manager.removeItem(atPath: dbPath)
|
||||
Logger.info(message: "Deleted database file at path: \(dbPath)")
|
||||
}
|
||||
}
|
||||
catch let error {
|
||||
} catch let error {
|
||||
Logger.error(message: "Unable to remove database file at path: \(dbPath), error: \(error)")
|
||||
}
|
||||
}
|
||||
|
|
@ -87,8 +85,9 @@ class MPDB {
|
|||
private func createTableFor(_ persistenceType: PersistenceType) {
|
||||
if let db = connection {
|
||||
let tableName = tableNameFor(persistenceType)
|
||||
let createTableString = "CREATE TABLE IF NOT EXISTS \(tableName)(id integer primary key autoincrement,data blob,time real,flag integer);"
|
||||
var createTableStatement: OpaquePointer? = nil
|
||||
let createTableString =
|
||||
"CREATE TABLE IF NOT EXISTS \(tableName)(id integer primary key autoincrement,data blob,time real,flag integer);"
|
||||
var createTableStatement: OpaquePointer?
|
||||
if sqlite3_prepare_v2(db, createTableString, -1, &createTableStatement, nil) == SQLITE_OK {
|
||||
if sqlite3_step(createTableStatement) == SQLITE_DONE {
|
||||
Logger.info(message: "\(tableName) table created")
|
||||
|
|
@ -114,7 +113,7 @@ class MPDB {
|
|||
if let db = connection {
|
||||
let tableName = tableNameFor(persistenceType)
|
||||
let insertString = "INSERT INTO \(tableName) (data, flag, time) VALUES(?, ?, ?);"
|
||||
var insertStatement: OpaquePointer? = nil
|
||||
var insertStatement: OpaquePointer?
|
||||
data.withUnsafeBytes { rawBuffer in
|
||||
if let pointer = rawBuffer.baseAddress {
|
||||
if sqlite3_prepare_v2(db, insertString, -1, &insertStatement, nil) == SQLITE_OK {
|
||||
|
|
@ -143,7 +142,7 @@ class MPDB {
|
|||
if let db = connection {
|
||||
let tableName = tableNameFor(persistenceType)
|
||||
let deleteString = "DELETE FROM \(tableName)\(ids.isEmpty ? "" : " WHERE id IN \(idsSqlString(ids))")"
|
||||
var deleteStatement: OpaquePointer? = nil
|
||||
var deleteStatement: OpaquePointer?
|
||||
if sqlite3_prepare_v2(db, deleteString, -1, &deleteStatement, nil) == SQLITE_OK {
|
||||
if sqlite3_step(deleteStatement) == SQLITE_DONE {
|
||||
Logger.info(message: "Succesfully deleted rows from table \(tableName)")
|
||||
|
|
@ -175,7 +174,7 @@ class MPDB {
|
|||
if let db = connection {
|
||||
let tableName = tableNameFor(persistenceType)
|
||||
let updateString = "UPDATE \(tableName) SET flag = \(newFlag) where flag = \(!newFlag)"
|
||||
var updateStatement: OpaquePointer? = nil
|
||||
var updateStatement: OpaquePointer?
|
||||
if sqlite3_prepare_v2(db, updateString, -1, &updateStatement, nil) == SQLITE_OK {
|
||||
if sqlite3_step(updateStatement) == SQLITE_DONE {
|
||||
Logger.info(message: "Succesfully update rows from table \(tableName)")
|
||||
|
|
@ -193,13 +192,16 @@ class MPDB {
|
|||
}
|
||||
}
|
||||
|
||||
func readRows(_ persistenceType: PersistenceType, numRows: Int, flag: Bool = false) -> [InternalProperties] {
|
||||
func readRows(_ persistenceType: PersistenceType, numRows: Int, flag: Bool = false) -> [InternalProperties] {
|
||||
var rows: [InternalProperties] = []
|
||||
if let db = connection {
|
||||
let tableName = tableNameFor(persistenceType)
|
||||
let selectString = "SELECT id, data FROM \(tableName) WHERE flag = \(flag ? 1 : 0) ORDER BY time\(numRows == Int.max ? "" : " LIMIT \(numRows)")"
|
||||
var selectStatement: OpaquePointer? = nil
|
||||
var rowsRead: Int = 0
|
||||
let selectString = """
|
||||
SELECT id, data FROM \(tableName) WHERE flag = \(flag ? 1 : 0) \
|
||||
ORDER BY time\(numRows == Int.max ? "" : " LIMIT \(numRows)")
|
||||
"""
|
||||
var selectStatement: OpaquePointer?
|
||||
var rowsRead: Int = 0
|
||||
if sqlite3_prepare_v2(db, selectString, -1, &selectStatement, nil) == SQLITE_OK {
|
||||
while sqlite3_step(selectStatement) == SQLITE_ROW {
|
||||
if let blob = sqlite3_column_blob(selectStatement, 1) {
|
||||
|
|
|
|||
|
|
@ -38,9 +38,9 @@ open class Mixpanel {
|
|||
flushInterval: Double = 60,
|
||||
instanceName: String = UUID().uuidString,
|
||||
optOutTrackingByDefault: Bool = false) -> MixpanelInstance {
|
||||
return MixpanelManager.sharedInstance.initialize(token: apiToken,
|
||||
return MixpanelManager.sharedInstance.initialize(token: apiToken,
|
||||
flushInterval: flushInterval,
|
||||
instanceName: instanceName,
|
||||
instanceName: instanceName,
|
||||
optOutTrackingByDefault: optOutTrackingByDefault)
|
||||
}
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import CoreTelephony
|
|||
/**
|
||||
* Delegate protocol for controlling the Mixpanel API's network behavior.
|
||||
*/
|
||||
public protocol MixpanelDelegate {
|
||||
public protocol MixpanelDelegate: AnyObject {
|
||||
/**
|
||||
Asks the delegate if data should be uploaded to the server.
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele
|
|||
open var apiToken = ""
|
||||
|
||||
/// The a MixpanelDelegate object that gives control over Mixpanel network activity.
|
||||
open var delegate: MixpanelDelegate?
|
||||
open weak var delegate: MixpanelDelegate?
|
||||
|
||||
/// distinctId string that uniquely identifies the current user.
|
||||
open var distinctId = ""
|
||||
|
|
@ -72,7 +72,6 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele
|
|||
|
||||
let mixpanelPersistence: MixpanelPersistence
|
||||
|
||||
|
||||
/// Accessor to the Mixpanel People API object.
|
||||
var groups: [String: Group] = [:]
|
||||
|
||||
|
|
@ -84,42 +83,45 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele
|
|||
/// If this is not set, it will query the Autotrack settings from the Mixpanel server
|
||||
open var trackAutomaticEventsEnabled: Bool? {
|
||||
didSet {
|
||||
MixpanelPersistence.saveAutomacticEventsEnabledFlag(value: trackAutomaticEventsEnabled ?? false, fromDecide: false, apiToken: apiToken)
|
||||
MixpanelPersistence.saveAutomacticEventsEnabledFlag(value: trackAutomaticEventsEnabled ?? false,
|
||||
fromDecide: false,
|
||||
apiToken: apiToken)
|
||||
}
|
||||
}
|
||||
|
||||
/// Flush timer's interval.
|
||||
/// Setting a flush interval of 0 will turn off the flush timer and you need to call the flush() API manually to upload queued data to the Mixpanel server.
|
||||
/// Setting a flush interval of 0 will turn off the flush timer and you need to call the flush() API manually
|
||||
/// to upload queued data to the Mixpanel server.
|
||||
open var flushInterval: Double {
|
||||
set {
|
||||
flushInstance.flushInterval = newValue
|
||||
}
|
||||
get {
|
||||
return flushInstance.flushInterval
|
||||
}
|
||||
set {
|
||||
flushInstance.flushInterval = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// Control whether the library should flush data to Mixpanel when the app
|
||||
/// enters the background. Defaults to true.
|
||||
open var flushOnBackground: Bool {
|
||||
set {
|
||||
flushInstance.flushOnBackground = newValue
|
||||
}
|
||||
get {
|
||||
return flushInstance.flushOnBackground
|
||||
}
|
||||
set {
|
||||
flushInstance.flushOnBackground = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// Controls whether to automatically send the client IP Address as part of
|
||||
/// event tracking. With an IP address, the Mixpanel Dashboard will show you the users' city.
|
||||
/// Defaults to true.
|
||||
open var useIPAddressForGeoLocation: Bool {
|
||||
set {
|
||||
flushInstance.useIPAddressForGeoLocation = newValue
|
||||
}
|
||||
get {
|
||||
return flushInstance.useIPAddressForGeoLocation
|
||||
}
|
||||
set {
|
||||
flushInstance.useIPAddressForGeoLocation = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// The base URL used for Mixpanel API requests.
|
||||
|
|
@ -168,23 +170,23 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele
|
|||
/// The minimum session duration (ms) that is tracked in automatic events.
|
||||
/// The default value is 10000 (10 seconds).
|
||||
open var minimumSessionDuration: UInt64 {
|
||||
set {
|
||||
automaticEvents.minimumSessionDuration = newValue
|
||||
}
|
||||
get {
|
||||
return automaticEvents.minimumSessionDuration
|
||||
}
|
||||
set {
|
||||
automaticEvents.minimumSessionDuration = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// The maximum session duration (ms) that is tracked in automatic events.
|
||||
/// The default value is UINT64_MAX (no maximum session duration).
|
||||
open var maximumSessionDuration: UInt64 {
|
||||
set {
|
||||
automaticEvents.maximumSessionDuration = newValue
|
||||
}
|
||||
get {
|
||||
return automaticEvents.maximumSessionDuration
|
||||
}
|
||||
set {
|
||||
automaticEvents.maximumSessionDuration = newValue
|
||||
}
|
||||
}
|
||||
#endif // DECIDE
|
||||
|
||||
|
|
@ -235,7 +237,9 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele
|
|||
#if os(iOS) && !targetEnvironment(macCatalyst)
|
||||
if let reachability = MixpanelInstance.reachability {
|
||||
var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
|
||||
func reachabilityCallback(reachability: SCNetworkReachability, flags: SCNetworkReachabilityFlags, unsafePointer: UnsafeMutableRawPointer?) {
|
||||
func reachabilityCallback(reachability: SCNetworkReachability,
|
||||
flags: SCNetworkReachabilityFlags,
|
||||
unsafePointer: UnsafeMutableRawPointer?) {
|
||||
let wifi = flags.contains(SCNetworkReachabilityFlags.reachable) && !flags.contains(SCNetworkReachabilityFlags.isWWAN)
|
||||
AutomaticProperties.automaticPropertiesLock.write {
|
||||
AutomaticProperties.properties["$wifi"] = wifi
|
||||
|
|
@ -389,7 +393,8 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele
|
|||
|
||||
#if !os(OSX) && !os(watchOS)
|
||||
static func sharedUIApplication() -> UIApplication? {
|
||||
guard let sharedApplication = UIApplication.perform(NSSelectorFromString("sharedApplication"))?.takeUnretainedValue() as? UIApplication else {
|
||||
guard let sharedApplication =
|
||||
UIApplication.perform(NSSelectorFromString("sharedApplication"))?.takeUnretainedValue() as? UIApplication else {
|
||||
return nil
|
||||
}
|
||||
return sharedApplication
|
||||
|
|
@ -507,7 +512,8 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele
|
|||
#if os(OSX)
|
||||
static func macOSIdentifier() -> String? {
|
||||
let platformExpert: io_service_t = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"))
|
||||
let serialNumberAsCFString = IORegistryEntryCreateCFProperty(platformExpert, kIOPlatformSerialNumberKey as CFString, kCFAllocatorDefault, 0)
|
||||
let serialNumberAsCFString =
|
||||
IORegistryEntryCreateCFProperty(platformExpert, kIOPlatformSerialNumberKey as CFString, kCFAllocatorDefault, 0)
|
||||
IOObjectRelease(platformExpert)
|
||||
return (serialNumberAsCFString?.takeUnretainedValue() as? String)
|
||||
}
|
||||
|
|
@ -752,7 +758,6 @@ extension MixpanelInstance {
|
|||
extension MixpanelInstance {
|
||||
// MARK: - Persistence
|
||||
|
||||
|
||||
open func archive() {
|
||||
self.readWriteLock.read {
|
||||
MixpanelPersistence.saveTimedEvents(timedEvents: timedEvents, apiToken: apiToken)
|
||||
|
|
@ -767,7 +772,6 @@ extension MixpanelInstance {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
func unarchive() {
|
||||
optOutStatus = MixpanelPersistence.loadOptOutStatusFlag(apiToken: apiToken)
|
||||
superProperties = MixpanelPersistence.loadSuperProperties(apiToken: apiToken)
|
||||
|
|
@ -781,7 +785,7 @@ extension MixpanelInstance {
|
|||
mixpanelIdentity.alias,
|
||||
mixpanelIdentity.hadPersistedDistinctId
|
||||
)
|
||||
if distinctId == "" {
|
||||
if distinctId.isEmpty {
|
||||
distinctId = defaultDistinctId()
|
||||
anonymousId = distinctId
|
||||
hadPersistedDistinctId = nil
|
||||
|
|
@ -902,15 +906,18 @@ extension MixpanelInstance {
|
|||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
self.timedEvents = self.trackInstance.track(event: event, properties: properties,
|
||||
timedEvents: self.timedEvents,
|
||||
superProperties: self.superProperties,
|
||||
distinctId: self.distinctId,
|
||||
anonymousId: self.anonymousId,
|
||||
userId: self.userId,
|
||||
hadPersistedDistinctId: self.hadPersistedDistinctId,
|
||||
epochInterval: epochInterval)
|
||||
let mixpanelIdentity = MixpanelIdentity.init(distinctID: self.distinctId,
|
||||
peopleDistinctID: nil,
|
||||
anonymousId: self.anonymousId,
|
||||
userId: self.userId,
|
||||
alias: nil,
|
||||
hadPersistedDistinctId: self.hadPersistedDistinctId)
|
||||
self.timedEvents = self.trackInstance.track(event: event,
|
||||
properties: properties,
|
||||
timedEvents: self.timedEvents,
|
||||
superProperties: self.superProperties,
|
||||
mixpanelIdentity: mixpanelIdentity,
|
||||
epochInterval: epochInterval)
|
||||
}
|
||||
|
||||
if MixpanelInstance.isiOSAppExtension() {
|
||||
|
|
@ -964,7 +971,13 @@ extension MixpanelInstance {
|
|||
|
||||
guard let group = groupsShadow[key] else {
|
||||
readWriteLock.write {
|
||||
groups[key] = Group(apiToken: apiToken, serialQueue: trackingQueue, lock: self.readWriteLock, groupKey: groupKey, groupID: groupID, metadata: sessionMetadata, mixpanelPersistence: mixpanelPersistence)
|
||||
groups[key] = Group(apiToken: apiToken,
|
||||
serialQueue: trackingQueue,
|
||||
lock: self.readWriteLock,
|
||||
groupKey: groupKey,
|
||||
groupID: groupID,
|
||||
metadata: sessionMetadata,
|
||||
mixpanelPersistence: mixpanelPersistence)
|
||||
groupsShadow = groups
|
||||
}
|
||||
return groupsShadow[key]!
|
||||
|
|
@ -973,7 +986,13 @@ extension MixpanelInstance {
|
|||
if !(group.groupKey == groupKey && group.groupID.equals(rhs: groupID)) {
|
||||
// we somehow hit a collision on the map key, return a new group with the correct key and ID
|
||||
Logger.info(message: "groups dictionary key collision: \(key)")
|
||||
let newGroup = Group(apiToken: apiToken, serialQueue: trackingQueue, lock: self.readWriteLock, groupKey: groupKey, groupID: groupID, metadata: sessionMetadata, mixpanelPersistence: mixpanelPersistence)
|
||||
let newGroup = Group(apiToken: apiToken,
|
||||
serialQueue: trackingQueue,
|
||||
lock: self.readWriteLock,
|
||||
groupKey: groupKey,
|
||||
groupID: groupID,
|
||||
metadata: sessionMetadata,
|
||||
mixpanelPersistence: mixpanelPersistence)
|
||||
readWriteLock.write {
|
||||
groups[key] = newGroup
|
||||
}
|
||||
|
|
@ -1058,8 +1077,7 @@ extension MixpanelInstance {
|
|||
- parameter event: the name of the event to clear the timer for
|
||||
*/
|
||||
open func clearTimedEvent(event: String) {
|
||||
trackingQueue.async {
|
||||
[weak self, event] in
|
||||
trackingQueue.async { [weak self, event] in
|
||||
guard let self = self else { return }
|
||||
|
||||
let updatedTimedEvents = self.trackInstance.clearTimedEvent(event: event, timedEvents: self.timedEvents)
|
||||
|
|
@ -1309,7 +1327,8 @@ extension MixpanelInstance {
|
|||
This method will internally track an opt in event to your project.
|
||||
|
||||
- parameter distintId: an optional string to use as the distinct ID for events
|
||||
- parameter properties: an optional properties dictionary that could be passed to add properties to the opt-in event that is sent to Mixpanel
|
||||
- parameter properties: an optional properties dictionary that could be passed to add properties to the opt-in event
|
||||
that is sent to Mixpanel
|
||||
*/
|
||||
open func optInTracking(distinctId: String? = nil, properties: Properties? = nil) {
|
||||
optOutStatus = false
|
||||
|
|
@ -1330,7 +1349,6 @@ extension MixpanelInstance {
|
|||
return optOutStatus ?? false
|
||||
}
|
||||
|
||||
|
||||
// MARK: - AEDelegate
|
||||
func increment(property: String, by: Double) {
|
||||
people?.increment(property: property, by: by)
|
||||
|
|
|
|||
|
|
@ -108,7 +108,6 @@ class MixpanelPersistence {
|
|||
return defaults.object(forKey: "\(prefix)\(MixpanelUserDefaultsKeys.optOutStatus)") as? Bool
|
||||
}
|
||||
|
||||
|
||||
static func saveAutomacticEventsEnabledFlag(value: Bool, fromDecide: Bool, apiToken: String) {
|
||||
guard let defaults = UserDefaults(suiteName: MixpanelUserDefaultsKeys.suiteName) else {
|
||||
return
|
||||
|
|
@ -130,7 +129,8 @@ class MixpanelPersistence {
|
|||
guard let defaults = UserDefaults(suiteName: MixpanelUserDefaultsKeys.suiteName) else {
|
||||
return true
|
||||
}
|
||||
if defaults.object(forKey: "\(prefix)\(MixpanelUserDefaultsKeys.automaticEventEnabled)") == nil && defaults.object(forKey: "\(prefix)\(MixpanelUserDefaultsKeys.automaticEventEnabledFromDecide)") == nil {
|
||||
if defaults.object(forKey: "\(prefix)\(MixpanelUserDefaultsKeys.automaticEventEnabled)") == nil &&
|
||||
defaults.object(forKey: "\(prefix)\(MixpanelUserDefaultsKeys.automaticEventEnabledFromDecide)") == nil {
|
||||
return true // default true
|
||||
}
|
||||
if defaults.object(forKey: "\(prefix)\(MixpanelUserDefaultsKeys.automaticEventEnabled)") != nil {
|
||||
|
|
@ -199,7 +199,12 @@ class MixpanelPersistence {
|
|||
|
||||
static func loadIdentity(apiToken: String) -> MixpanelIdentity {
|
||||
guard let defaults = UserDefaults(suiteName: MixpanelUserDefaultsKeys.suiteName) else {
|
||||
return MixpanelIdentity.init(distinctID: "", peopleDistinctID: nil, anonymousId: nil, userId: nil, alias: nil, hadPersistedDistinctId: nil)
|
||||
return MixpanelIdentity.init(distinctID: "",
|
||||
peopleDistinctID: nil,
|
||||
anonymousId: nil,
|
||||
userId: nil,
|
||||
alias: nil,
|
||||
hadPersistedDistinctId: nil)
|
||||
}
|
||||
let prefix = "\(MixpanelUserDefaultsKeys.prefix)-\(apiToken)-"
|
||||
return MixpanelIdentity.init(
|
||||
|
|
@ -266,7 +271,7 @@ class MixpanelPersistence {
|
|||
MixpanelPersistence.saveOptOutStatusFlag(value: optOutFlag, apiToken: apiToken)
|
||||
}
|
||||
if let automaticEventsFlag = automaticEventsEnabled {
|
||||
MixpanelPersistence.saveAutomacticEventsEnabledFlag(value: automaticEventsFlag, fromDecide: false, apiToken: apiToken) // should fromDecide = false here?
|
||||
MixpanelPersistence.saveAutomacticEventsEnabledFlag(value: automaticEventsFlag, fromDecide: false, apiToken: apiToken)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -327,7 +332,7 @@ class MixpanelPersistence {
|
|||
removeArchivedFile(atPath: groupsFile)
|
||||
}
|
||||
if let propsFile = filePathWithType("properties") {
|
||||
removeArchivedFile(atPath:propsFile)
|
||||
removeArchivedFile(atPath: propsFile)
|
||||
}
|
||||
if let optOutFile = filePathWithType("optOutStatus") {
|
||||
removeArchivedFile(atPath: optOutFile)
|
||||
|
|
|
|||
|
|
@ -62,9 +62,9 @@ class Network {
|
|||
}
|
||||
|
||||
class func apiRequest<A>(base: String,
|
||||
resource: Resource<A>,
|
||||
failure: @escaping (Reason, Data?, URLResponse?) -> Void,
|
||||
success: @escaping (A, URLResponse?) -> Void) {
|
||||
resource: Resource<A>,
|
||||
failure: @escaping (Reason, Data?, URLResponse?) -> Void,
|
||||
success: @escaping (A, URLResponse?) -> Void) {
|
||||
guard let request = buildURLRequest(base, resource: resource) else {
|
||||
return
|
||||
}
|
||||
|
|
@ -116,11 +116,11 @@ class Network {
|
|||
}
|
||||
|
||||
class func buildResource<A>(path: String,
|
||||
method: RequestMethod,
|
||||
requestBody: Data? = nil,
|
||||
queryItems: [URLQueryItem]? = nil,
|
||||
headers: [String: String],
|
||||
parse: @escaping (Data) -> A?) -> Resource<A> {
|
||||
method: RequestMethod,
|
||||
requestBody: Data? = nil,
|
||||
queryItems: [URLQueryItem]? = nil,
|
||||
headers: [String: String],
|
||||
parse: @escaping (Data) -> A?) -> Resource<A> {
|
||||
return Resource(path: path,
|
||||
method: method,
|
||||
requestBody: requestBody,
|
||||
|
|
|
|||
|
|
@ -23,12 +23,15 @@ open class People {
|
|||
let serialQueue: DispatchQueue!
|
||||
let lock: ReadWriteLock
|
||||
var distinctId: String?
|
||||
var delegate: FlushDelegate?
|
||||
weak var delegate: FlushDelegate?
|
||||
let metadata: SessionMetadata
|
||||
let mixpanelPersistence: MixpanelPersistence
|
||||
|
||||
|
||||
init(apiToken: String, serialQueue: DispatchQueue, lock: ReadWriteLock, metadata: SessionMetadata, mixpanelPersistence: MixpanelPersistence) {
|
||||
init(apiToken: String,
|
||||
serialQueue: DispatchQueue,
|
||||
lock: ReadWriteLock,
|
||||
metadata: SessionMetadata,
|
||||
mixpanelPersistence: MixpanelPersistence) {
|
||||
self.apiToken = apiToken
|
||||
self.serialQueue = serialQueue
|
||||
self.lock = lock
|
||||
|
|
@ -94,7 +97,6 @@ open class People {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
func merge(properties: InternalProperties) {
|
||||
addPeopleRecordToQueueWithAction("$merge", properties: properties)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,10 +35,7 @@ class Track {
|
|||
properties: Properties? = nil,
|
||||
timedEvents: InternalProperties,
|
||||
superProperties: InternalProperties,
|
||||
distinctId: String,
|
||||
anonymousId: String?,
|
||||
userId: String?,
|
||||
hadPersistedDistinctId: Bool?,
|
||||
mixpanelIdentity: MixpanelIdentity,
|
||||
epochInterval: Double) -> InternalProperties {
|
||||
var ev = event
|
||||
if ev == nil || ev!.isEmpty {
|
||||
|
|
@ -63,15 +60,15 @@ class Track {
|
|||
shadowTimedEvents.removeValue(forKey: ev!)
|
||||
p["$duration"] = Double(String(format: "%.3f", epochInterval - eventStartTime))
|
||||
}
|
||||
p["distinct_id"] = distinctId
|
||||
if anonymousId != nil {
|
||||
p["$device_id"] = anonymousId
|
||||
p["distinct_id"] = mixpanelIdentity.distinctID
|
||||
if mixpanelIdentity.anonymousId != nil {
|
||||
p["$device_id"] = mixpanelIdentity.anonymousId
|
||||
}
|
||||
if userId != nil {
|
||||
p["$user_id"] = userId
|
||||
if mixpanelIdentity.userId != nil {
|
||||
p["$user_id"] = mixpanelIdentity.userId
|
||||
}
|
||||
if hadPersistedDistinctId != nil {
|
||||
p["$had_persisted_distinct_id"] = hadPersistedDistinctId
|
||||
if mixpanelIdentity.hadPersistedDistinctId != nil {
|
||||
p["$had_persisted_distinct_id"] = mixpanelIdentity.hadPersistedDistinctId
|
||||
}
|
||||
|
||||
p += superProperties
|
||||
|
|
|
|||
Loading…
Reference in a new issue