diff --git a/Mixpanel/AutomaticEvents.swift b/Mixpanel/AutomaticEvents.swift index ab62de626..666ca830b 100644 --- a/Mixpanel/AutomaticEvents.swift +++ b/Mixpanel/AutomaticEvents.swift @@ -49,6 +49,7 @@ class AutomaticEvents: NSObject, SKPaymentTransactionObserver, SKProductsRequest var hasAddedObserver = false var automaticPushTracking = true var firstAppOpen = false + let awaitingTransactionsWriteLock = DispatchQueue(label: "com.mixpanel.awaiting_transactions_writeLock") func initializeEvents() { let firstOpenKey = "MPFirstOpen" @@ -119,21 +120,23 @@ class AutomaticEvents: NSObject, SKPaymentTransactionObserver, SKProductsRequest func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { var productsRequest = SKProductsRequest() var productIdentifiers: Set = [] - objc_sync_enter(awaitingTransactions) - for transaction:AnyObject in transactions { - if let trans = transaction as? SKPaymentTransaction { - switch trans.transactionState { - case .purchased: - productIdentifiers.insert(trans.payment.productIdentifier) - awaitingTransactions[trans.payment.productIdentifier] = trans - break - case .failed: break - case .restored: break - default: break + awaitingTransactionsWriteLock.sync { + for transaction:AnyObject in transactions { + if let trans = transaction as? SKPaymentTransaction { + switch trans.transactionState { + case .purchased: + productIdentifiers.insert(trans.payment.productIdentifier) + awaitingTransactions[trans.payment.productIdentifier] = trans + break + case .failed: break + case .restored: break + default: break + } } } } - objc_sync_exit(awaitingTransactions) + + if productIdentifiers.count > 0 { productsRequest = SKProductsRequest(productIdentifiers: productIdentifiers) productsRequest.delegate = self @@ -163,16 +166,16 @@ class AutomaticEvents: NSObject, SKPaymentTransactionObserver, SKProductsRequest func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) { - objc_sync_enter(awaitingTransactions) - for product in response.products { - if let trans = awaitingTransactions[product.productIdentifier] { - delegate?.track(event: "$ae_iap", properties: ["$ae_iap_price": "\(product.price)", - "$ae_iap_quantity": trans.payment.quantity, - "$ae_iap_name": product.productIdentifier]) - awaitingTransactions.removeValue(forKey: product.productIdentifier) + awaitingTransactionsWriteLock.sync { + for product in response.products { + if let trans = awaitingTransactions[product.productIdentifier] { + delegate?.track(event: "$ae_iap", properties: ["$ae_iap_price": "\(product.price)", + "$ae_iap_quantity": trans.payment.quantity, + "$ae_iap_name": product.productIdentifier]) + awaitingTransactions.removeValue(forKey: product.productIdentifier) + } } } - objc_sync_exit(awaitingTransactions) } #if DECIDE diff --git a/Mixpanel/AutomaticProperties.swift b/Mixpanel/AutomaticProperties.swift index 3c2423bcd..ee7dd975c 100644 --- a/Mixpanel/AutomaticProperties.swift +++ b/Mixpanel/AutomaticProperties.swift @@ -21,7 +21,6 @@ class AutomaticProperties { static let automaticPropertiesLock = ReadWriteLock(label: "automaticPropertiesLock") static var properties: InternalProperties = { - objc_sync_enter(AutomaticProperties.self); defer { objc_sync_exit(AutomaticProperties.self) } var p = InternalProperties() #if os(iOS) || TV_OS let screenSize = UIScreen.main.bounds.size @@ -62,7 +61,6 @@ class AutomaticProperties { }() static var peopleProperties: InternalProperties = { - objc_sync_enter(AutomaticProperties.self); defer { objc_sync_exit(AutomaticProperties.self) } var p = InternalProperties() let infoDict = Bundle.main.infoDictionary if let infoDict = infoDict { diff --git a/Mixpanel/Flush.swift b/Mixpanel/Flush.swift index 0dedadc39..dbcfa680b 100644 --- a/Mixpanel/Flush.swift +++ b/Mixpanel/Flush.swift @@ -17,33 +17,33 @@ protocol FlushDelegate { } class Flush: AppLifecycle { - let lock: ReadWriteLock var timer: Timer? var delegate: FlushDelegate? var useIPAddressForGeoLocation = true var flushRequest: FlushRequest var flushOnBackground = true var _flushInterval = 0.0 + private let flushIntervalReadWriteLock: DispatchQueue + var flushInterval: Double { set { - objc_sync_enter(self) - _flushInterval = newValue - objc_sync_exit(self) + flushIntervalReadWriteLock.sync(flags: .barrier, execute: { + _flushInterval = newValue + }) delegate?.flush(completion: nil) startFlushTimer() } get { - objc_sync_enter(self) - defer { objc_sync_exit(self) } - - return _flushInterval + flushIntervalReadWriteLock.sync { + return _flushInterval + } } } - required init(basePathIdentifier: String, lock: ReadWriteLock) { + required init(basePathIdentifier: String) { self.flushRequest = FlushRequest(basePathIdentifier: basePathIdentifier) - self.lock = lock + flushIntervalReadWriteLock = DispatchQueue(label: "com.mixpanel.flush_interval.lock", attributes: .concurrent) } func flushEventsQueue(_ eventsQueue: Queue, automaticEventsEnabled: Bool?) -> Queue? { diff --git a/Mixpanel/MixpanelInstance.swift b/Mixpanel/MixpanelInstance.swift index b30afc696..ae05154f1 100644 --- a/Mixpanel/MixpanelInstance.swift +++ b/Mixpanel/MixpanelInstance.swift @@ -282,7 +282,7 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele } self.name = name self.readWriteLock = ReadWriteLock(label: "globalLock") - flushInstance = Flush(basePathIdentifier: name, lock: self.readWriteLock) + flushInstance = Flush(basePathIdentifier: name) #if DECIDE decideInstance = Decide(basePathIdentifier: name, lock: self.readWriteLock) #endif // DECIDE @@ -357,7 +357,7 @@ open class MixpanelInstance: CustomDebugStringConvertible, FlushDelegate, AEDele } self.name = name self.readWriteLock = ReadWriteLock(label: "globalLock") - flushInstance = Flush(basePathIdentifier: name, lock: self.readWriteLock) + flushInstance = Flush(basePathIdentifier: name) let label = "com.mixpanel.\(self.apiToken)" trackingQueue = DispatchQueue(label: label) sessionMetadata = SessionMetadata(trackingQueue: trackingQueue) diff --git a/Mixpanel/Swizzle.swift b/Mixpanel/Swizzle.swift index 38cfc302b..b4d6a1bbb 100644 --- a/Mixpanel/Swizzle.swift +++ b/Mixpanel/Swizzle.swift @@ -8,19 +8,6 @@ import Foundation -extension DispatchQueue { - private static var _onceTracker = [String]() - - class func once(token: String, block: () -> Void) { - objc_sync_enter(self); defer { objc_sync_exit(self) } - if _onceTracker.contains(token) { - return - } - _onceTracker.append(token) - block() - } -} - class Swizzler { static var swizzles = [Method: Swizzle]() diff --git a/Mixpanel/WebSocketWrapper.swift b/Mixpanel/WebSocketWrapper.swift index 5c2b28c7c..70e01ef7f 100644 --- a/Mixpanel/WebSocketWrapper.swift +++ b/Mixpanel/WebSocketWrapper.swift @@ -41,11 +41,13 @@ class WebSocketWrapper: WebSocketDelegate { var connectivityIndiciatorWindow: UIWindow? = nil let connectCallback: (() -> Void)? let disconnectCallback: (() -> Void)? + let sessionObjectLock: DispatchQueue init(url: URL, keepTrying: Bool, connectCallback: (() -> Void)?, disconnectCallback: (() -> Void)?) { open = false connected = false session = [String: Any]() + sessionObjectLock = DispatchQueue(label: "com.mixpanel.session_object_lock", attributes: .concurrent) self.url = url self.connectCallback = connectCallback self.disconnectCallback = disconnectCallback @@ -65,17 +67,15 @@ class WebSocketWrapper: WebSocketDelegate { } func setSessionObjectSynchronized(with value: Any, for key: String) { - objc_sync_enter(self) - defer { objc_sync_exit(self) } - - session[key] = value + sessionObjectLock.sync(flags: .barrier, execute: { + session[key] = value + }) } func getSessionObjectSynchronized(for key: String) -> Any? { - objc_sync_enter(self) - defer { objc_sync_exit(self) } - - return session[key] + sessionObjectLock.sync { + return session[key] + } } func open(initiate: Bool, maxInterval: Int = 0, maxRetries: Int = 0) { diff --git a/MixpanelDemo/MixpanelDemoTests/MixpanelBaseTests.swift b/MixpanelDemo/MixpanelDemoTests/MixpanelBaseTests.swift index a0c2caef7..e9a2b6c17 100644 --- a/MixpanelDemo/MixpanelDemoTests/MixpanelBaseTests.swift +++ b/MixpanelDemo/MixpanelDemoTests/MixpanelBaseTests.swift @@ -57,14 +57,12 @@ class MixpanelBaseTests: XCTestCase, MixpanelDelegate { func deleteOptOutSettings(mixpanelInstance: MixpanelInstance) { - objc_sync_enter(self) let filePath = Persistence.filePathWithType(.optOutStatus, token: mixpanelInstance.apiToken) do { try FileManager.default.removeItem(atPath: filePath!) } catch { Logger.info(message: "Unable to remove file at path: \(filePath!)") } - objc_sync_exit(self) } func mixpanelWillFlush(_ mixpanel: MixpanelInstance) -> Bool {