@@ -44,9 +44,23 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
44
44
}
45
45
46
46
/// The unique identifier for the call.
47
- open var callId : String { active. map { storage [ $0] ? . call. callId ?? " " } ?? " " }
47
+ open var callId : String {
48
+ if let active, let callEntry = callEntry ( for: active) {
49
+ return callEntry. call. callId
50
+ } else {
51
+ return " "
52
+ }
53
+ }
54
+
48
55
/// The type of call.
49
- open var callType : String { active. map { storage [ $0] ? . call. callType ?? " " } ?? " " }
56
+ open var callType : String {
57
+ if let active, let callEntry = callEntry ( for: active) {
58
+ return callEntry. call. callType
59
+ } else {
60
+ return " "
61
+ }
62
+ }
63
+
50
64
/// The icon data for the call template.
51
65
open var iconTemplateImageData : Data ?
52
66
/// Whether the call can be held on its own or swapped with another call.
@@ -65,8 +79,10 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
65
79
/// The call provider responsible for handling call-related actions.
66
80
open internal( set) lazy var callProvider = buildProvider ( )
67
81
68
- private( set) var storage : [ UUID : CallEntry ] = [ : ]
82
+ private var _storage : [ UUID : CallEntry ] = [ : ]
83
+ private let storageAccessQueue : UnfairQueue = . init( )
69
84
private var active : UUID ?
85
+ var callCount : Int { storageAccessQueue. sync { _storage. count } }
70
86
71
87
private var callEventsSubscription : Task < Void , Error > ?
72
88
private var callEndedNotificationCancellable : AnyCancellable ?
@@ -120,7 +136,7 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
120
136
"""
121
137
)
122
138
123
- guard let streamVideo, let callEntry = storage [ callUUID] else {
139
+ guard let streamVideo, let callEntry = callEntry ( for : callUUID) else {
124
140
log. warning (
125
141
"""
126
142
CallKit operation:reportIncomingCall cannot be fulfilled because
@@ -188,7 +204,7 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
188
204
/// The call was accepted somewhere else (e.g the incoming call on the same device or another
189
205
/// device). No action is required.
190
206
guard
191
- let newCallEntry = storage . first ( where : { $0 . value . call . cId == response. callCid } ) ? . value ,
207
+ let newCallEntry = callEntry ( for : response. callCid) ,
192
208
newCallEntry. callUUID != active // Ensure that the new call isn't the currently active one.
193
209
else {
194
210
return
@@ -200,7 +216,7 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
200
216
)
201
217
ringingTimerCancellable? . cancel ( )
202
218
ringingTimerCancellable = nil
203
- storage [ newCallEntry. callUUID] = nil
219
+ set ( nil , for : newCallEntry. callUUID)
204
220
callCache. remove ( for: newCallEntry. call. cId)
205
221
}
206
222
@@ -209,7 +225,7 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
209
225
/// - Parameter response: The call rejected event.
210
226
open func callRejected( _ response: CallRejectedEvent ) {
211
227
guard
212
- let newCallEntry = storage . first ( where : { $0 . value . call . cId == response. callCid } ) ? . value ,
228
+ let newCallEntry = callEntry ( for : response. callCid) ,
213
229
newCallEntry. callUUID != active // Ensure that the new call isn't the currently active one.
214
230
else {
215
231
return
@@ -230,13 +246,13 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
230
246
)
231
247
ringingTimerCancellable? . cancel ( )
232
248
ringingTimerCancellable = nil
233
- storage [ newCallEntry. callUUID] = nil
249
+ set ( nil , for : newCallEntry. callUUID)
234
250
callCache. remove ( for: newCallEntry. call. cId)
235
251
}
236
252
237
253
/// Handles the event when a call ends.
238
254
open func callEnded( _ cId: String ) {
239
- guard let callEndedEntry = storage . first ( where : { $0 . value . call . cId == cId } ) ? . value else {
255
+ guard let callEndedEntry = callEntry ( for : cId) else {
240
256
return
241
257
}
242
258
Task {
@@ -262,7 +278,7 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
262
278
/// We listen for the event so in the case we are the only ones remaining
263
279
/// in the call, we leave.
264
280
Task { @MainActor in
265
- if let call = storage . first ( where : { $0 . value . call . cId == response. callCid } ) ? . value . call,
281
+ if let call = callEntry ( for : response. callCid) ? . call,
266
282
call. state. participants. count == 1 {
267
283
callEnded ( response. callCid)
268
284
}
@@ -276,8 +292,10 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
276
292
/// This callback can be treated as a request to end all calls without the need to respond to any actions
277
293
open func providerDidReset( _ provider: CXProvider ) {
278
294
log. debug ( " CXProvider didReset. " )
279
- for (_, entry) in storage {
280
- entry. call. leave ( )
295
+ storageAccessQueue. sync {
296
+ for (_, entry) in _storage {
297
+ entry. call. leave ( )
298
+ }
281
299
}
282
300
}
283
301
@@ -287,7 +305,7 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
287
305
) {
288
306
guard
289
307
action. callUUID != active,
290
- let callToJoinEntry = storage [ action. callUUID]
308
+ let callToJoinEntry = callEntry ( for : action. callUUID)
291
309
else {
292
310
return action. fail ( )
293
311
}
@@ -313,7 +331,7 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
313
331
action. fulfill ( )
314
332
} catch {
315
333
callToJoinEntry. call. leave ( )
316
- storage [ action. callUUID] = nil
334
+ set ( nil , for : action. callUUID)
317
335
log. error ( error)
318
336
action. fail ( )
319
337
}
@@ -328,7 +346,7 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
328
346
ringingTimerCancellable = nil
329
347
let currentCallWasEnded = action. callUUID == active
330
348
331
- guard let stackEntry = storage [ action. callUUID] else {
349
+ guard let stackEntry = callEntry ( for : action. callUUID) else {
332
350
action. fail ( )
333
351
return
334
352
}
@@ -363,7 +381,7 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
363
381
if currentCallWasEnded {
364
382
stackEntry. call. leave ( )
365
383
}
366
- storage [ action. callUUID] = nil
384
+ set ( nil , for : action. callUUID)
367
385
action. fulfill ( )
368
386
}
369
387
}
@@ -503,7 +521,7 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
503
521
callType: idComponents [ 0 ] ,
504
522
callId: idComponents [ 1 ]
505
523
) {
506
- storage [ uuid ] = . init( call: call, callUUID: uuid)
524
+ set ( . init( call: call, callUUID: uuid ) , for : uuid)
507
525
}
508
526
509
527
update. localizedCallerName = localizedCallerName
@@ -524,6 +542,26 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
524
542
525
543
return ( uuid, update)
526
544
}
545
+
546
+ // MARK: - Storage Access
547
+
548
+ private func set( _ value: CallEntry ? , for key: UUID ) {
549
+ storageAccessQueue. sync {
550
+ _storage [ key] = value
551
+ }
552
+ }
553
+
554
+ private func callEntry( for cId: String ) -> CallEntry ? {
555
+ storageAccessQueue. sync {
556
+ _storage
557
+ . first { $0. value. call. cId == cId } ?
558
+ . value
559
+ }
560
+ }
561
+
562
+ private func callEntry( for uuid: UUID ) -> CallEntry ? {
563
+ storageAccessQueue. sync { _storage [ uuid] }
564
+ }
527
565
}
528
566
529
567
extension CallKitService : InjectionKey {
0 commit comments