Skip to content

Commit aada72d

Browse files
committed
Fix concurrency warning in URLSessionMockManager
1 parent 719f8a8 commit aada72d

File tree

1 file changed

+33
-27
lines changed

1 file changed

+33
-27
lines changed

Sources/Pulse/NetworkLogger/URLSessionMockManager.swift

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,48 @@
44

55
import Foundation
66

7-
// One more option is to add a delay on connection to make sure logger
8-
// is connected before the app runs.
9-
final class URLSessionMockManager {
7+
final class URLSessionMockManager: @unchecked Sendable {
108
private var mocks: [UUID: URLSessionMock] = [:]
119

10+
// Number of handled requests per mock.
11+
private var numberOfHandledRequests: [UUID: Int] = [:]
12+
private var mockedTaskIDs: Set<Int> = []
13+
14+
private let lock = NSLock()
15+
1216
static let shared = URLSessionMockManager()
1317

1418
func getMock(for request: URLRequest) -> URLSessionMock? {
15-
mocks.lazy.map(\.value).first {
19+
lock.lock()
20+
defer { lock.unlock() }
21+
22+
return mocks.lazy.map(\.value).first {
1623
$0.isMatch(request)
1724
}
1825
}
1926

27+
func shouldMock(_ request: URLRequest) -> Bool {
28+
lock.lock()
29+
defer { lock.unlock() }
30+
31+
guard let mock = getMock(for: request) else {
32+
return false
33+
}
34+
defer { numberOfHandledRequests[mock.mockID, default: 0] += 1 }
35+
let count = numberOfHandledRequests[mock.mockID, default: 0]
36+
if count < (mock.skip ?? 0) {
37+
return false // Skip the first N requests
38+
}
39+
if let maxCount = mock.count, count - (mock.skip ?? 0) >= maxCount {
40+
return false // Mock for N number of times
41+
}
42+
return RemoteLogger.shared.connectionState == .connected
43+
}
44+
2045
func update(_ mocks: [URLSessionMock]) {
46+
lock.lock()
47+
defer { lock.unlock() }
48+
2149
self.mocks.removeAll()
2250
for mock in mocks {
2351
self.mocks[mock.mockID] = mock
@@ -27,9 +55,6 @@ final class URLSessionMockManager {
2755

2856
final class URLSessionMockingProtocol: URLProtocol {
2957
override func startLoading() {
30-
lock.lock()
31-
defer { lock.unlock() }
32-
3358
guard let mock = URLSessionMockManager.shared.getMock(for: request) else {
3459
client?.urlProtocol(self, didFailWithError: URLError(.unknown)) // Should never happen
3560
return
@@ -70,27 +95,8 @@ final class URLSessionMockingProtocol: URLProtocol {
7095
}
7196

7297
override class func canInit(with request: URLRequest) -> Bool {
73-
lock.lock()
74-
defer { lock.unlock() }
75-
76-
guard let mock = URLSessionMockManager.shared.getMock(for: request) else {
77-
return false
78-
}
79-
defer { numberOfHandledRequests[mock.mockID, default: 0] += 1 }
80-
let count = numberOfHandledRequests[mock.mockID, default: 0]
81-
if count < (mock.skip ?? 0) {
82-
return false // Skip the first N requests
83-
}
84-
if let maxCount = mock.count, count - (mock.skip ?? 0) >= maxCount {
85-
return false // Mock for N number of times
86-
}
87-
return RemoteLogger.shared.connectionState == .connected
98+
URLSessionMockManager.shared.shouldMock(request)
8899
}
89100

90101
static let requestMockedHeaderName = "X-PulseRequestMocked"
91102
}
92-
93-
// Number of handled requests per mock.
94-
private var numberOfHandledRequests: [UUID: Int] = [:]
95-
private var mockedTaskIDs: Set<Int> = []
96-
private let lock = NSLock()

0 commit comments

Comments
 (0)