Skip to content

Commit

Permalink
Handle call sync between callViewModel, callKitService and different …
Browse files Browse the repository at this point in the history
…devices (#415)
  • Loading branch information
ipavlidakis authored May 28, 2024
1 parent b970994 commit bc46a4b
Show file tree
Hide file tree
Showing 10 changed files with 305 additions and 147 deletions.
46 changes: 28 additions & 18 deletions Sources/StreamVideo/CallKit/CallKitService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import Foundation
/// facilitating VoIP calls in an application.
open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {

@Injected(\.callCache) private var callCache
@Injected(\.uuidFactory) private var uuidFactory

/// Represents a call that is being managed by the service.
final class CallEntry: Equatable, @unchecked Sendable {
var call: Call
Expand Down Expand Up @@ -157,20 +160,21 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
///
/// - Parameter response: The call accepted event.
open func callAccepted(_ response: CallAcceptedEvent) {
/// The call was accepted somewhere else (e.g the incoming call on the same device or another
/// device). No action is required.
guard
let newCallEntry = storage.first(where: { $0.value.call.cId == response.callCid })?.value,
newCallEntry.callUUID != active // Ensure that the new call isn't the currently active one.
else {
return
}
Task {
do {
// Update call state to inCall and send the answer call action.
try await requestTransaction(CXAnswerCallAction(call: newCallEntry.callUUID))
} catch {
log.error(error)
}
}
callProvider.reportCall(
with: newCallEntry.callUUID,
endedAt: nil,
reason: .answeredElsewhere
)
storage[newCallEntry.callUUID] = nil
callCache.remove(for: newCallEntry.call.cId)
}

/// Handles the event when a call is rejected.
Expand All @@ -192,14 +196,13 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
else {
return
}
Task {
do {
// End the call if rejected.
try await requestTransaction(CXEndCallAction(call: newCallEntry.callUUID))
} catch {
log.error(error)
}
}
callProvider.reportCall(
with: newCallEntry.callUUID,
endedAt: nil,
reason: .declinedElsewhere
)
storage[newCallEntry.callUUID] = nil
callCache.remove(for: newCallEntry.call.cId)
}

/// Handles the event when a call ends.
Expand Down Expand Up @@ -294,6 +297,7 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
) {
ringingTimerCancellable?.cancel()
ringingTimerCancellable = nil
let currentCallWasEnded = action.callUUID == active

guard let stackEntry = storage[action.callUUID] else {
action.fail()
Expand All @@ -310,7 +314,9 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
} catch {
log.error(error)
}
stackEntry.call.leave()
if currentCallWasEnded {
stackEntry.call.leave()
}
storage[action.callUUID] = nil
action.fulfill()
}
Expand Down Expand Up @@ -372,6 +378,10 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {

// MARK: - Private helpers

/// Subscription to event should **never** perform an accept or joining a call action. Those actions
/// are only being performed explicitly from the component that receives the user action.
/// Subscribing to events is being used to reject/stop calls that have been accepted/rejected
/// on other devices or components (e.g. incoming callScreen, CallKitService)
private func subscribeToCallEvents() {
callEventsSubscription?.cancel()
callEventsSubscription = nil
Expand Down Expand Up @@ -435,7 +445,7 @@ open class CallKitService: NSObject, CXProviderDelegate, @unchecked Sendable {
) -> (UUID, CXCallUpdate) {
let update = CXCallUpdate()
let idComponents = cid.components(separatedBy: ":")
let uuid = UUID()
let uuid = uuidFactory.get()
if idComponents.count >= 2, let call = streamVideo?.call(callType: idComponents[0], callId: idComponents[1]) {
storage[uuid] = .init(call: call, callUUID: uuid)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ extension StreamCallStateMachine.Stage {
Task {
do {
let response = try await actionBlock()
if let cId = call?.cId {
callCache.remove(for: cId)
}
try transition?(.rejected(call, response: response))
} catch {
do {
Expand Down
24 changes: 24 additions & 0 deletions Sources/StreamVideo/Utils/UUIDProviding/StreamUUIDFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// Copyright © 2024 Stream.io Inc. All rights reserved.
//

import Foundation

protocol UUIDProviding {
func get() -> UUID
}

enum UUIDProviderKey: InjectionKey {
static var currentValue: UUIDProviding = StreamUUIDFactory()
}

extension InjectedValues {
var uuidFactory: UUIDProviding {
get { Self[UUIDProviderKey.self] }
set { Self[UUIDProviderKey.self] = newValue }
}
}

struct StreamUUIDFactory: UUIDProviding {
func get() -> UUID { .init() }
}
2 changes: 1 addition & 1 deletion Sources/StreamVideo/Utils/Utils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func postNotification(
)
}

func callCid(from callId: String, callType: String) -> String {
public func callCid(from callId: String, callType: String) -> String {
"\(callType):\(callId)"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class CallParticipantsInfoViewModel: ObservableObject {
isDestructive: false
)

private var call: Call?
private weak var call: Call?

var inviteParticipantsButtonShown: Bool {
call?.currentUserHasCapability(.updateCallMember) == true
Expand Down
Loading

0 comments on commit bc46a4b

Please sign in to comment.