import CocoaLumberjackSwift
import Foundation
/// Keeps track of CallIDs for calls that happened in the past two weeks to allow distinguishing between late arriving
/// hangup messages and hangup messages for actually missed calls.
/// Actually missed calls can be identified by never having received or sent a call offer message for the CallID
/// associated with the call.
public final class CallHistoryManager {
private let identity: String
private let businessInjector: BusinessInjectorProtocol
public init(identity: String, businessInjector: BusinessInjectorProtocol) {
self.identity = identity
self.businessInjector = businessInjector
}
public func store(callID: VoIPCallID, date: Date) async {
DDLogNotice(
"VoipCallService: [cid=\(callID)]: Start removeCallsOlderThanChatServerTimeout"
)
await CallHistoryManager.removeCallsOlderThanChatServerTimeout(businessInjector: businessInjector)
DDLogNotice(
"VoipCallService: [cid=\(callID)]: End removeCallsOlderThanChatServerTimeout"
)
return await withCheckedContinuation { (continuation: CheckedContinuation<Void, Never>) in
businessInjector.entityManager.perform {
guard let contactEntity = self.businessInjector.entityManager.entityFetcher
.contactEntity(for: self.identity)
else {
DDLogNotice(
"VoipCallService: [cid=\(callID)]: Contact not found, resuming."
)
continuation.resume()
return
}
let call = self.businessInjector.entityManager.entityCreator.callEntity(contactEntity: contactEntity)
call.callID = NSNumber(value: callID.callID)
call.date = date
DDLogNotice(
"VoipCallService: [cid=\(callID)]: Resume"
)
continuation.resume()
}
}
}
public func isMissedCall(from identity: String, callID: VoIPCallID) async -> Bool {
await CallHistoryManager.removeCallsOlderThanChatServerTimeout(businessInjector: businessInjector)
let callList = await fetchCallList(identity: identity, callID: callID)
return callList.isEmpty
}
private func fetchCallList(identity: String, callID: VoIPCallID) async -> [CallEntity] {
await withCheckedContinuation { continuation in
businessInjector.entityManager.perform {
guard let calls = self.businessInjector.entityManager.entityFetcher
.callEntities(with: identity, and: callID.callID) else {
DDLogError("Could not fetch calls.")
continuation.resume(returning: [CallEntity]())
return
}
return continuation.resume(returning: calls)
}
}
}
}
extension CallHistoryManager {
public static func removeCallsOlderThanChatServerTimeout(businessInjector: BusinessInjectorProtocol) {
businessInjector.entityManager.performSave {
businessInjector.entityManager.entityDestroyer.deleteMissedCallsCacheOlderThanTwoWeeks()
}
}
public static func removeCallsOlderThanChatServerTimeout(businessInjector: BusinessInjectorProtocol) async {
await withCheckedContinuation { (continuation: CheckedContinuation<Void, Never>) in
businessInjector.entityManager.performSave {
businessInjector.entityManager.entityDestroyer.deleteMissedCallsCacheOlderThanTwoWeeks()
continuation.resume()
}
}
}
}
¤ Dauer der Verarbeitung: 0.1 Sekunden
(vorverarbeitet am 2026-11-04)
¤
*© Formatika GbR, Deutschland