이 페이지는 현재 영어로만 제공되며 한국어 버전은 곧 제공될 예정입니다. 기다려 주셔서 감사드립니다.

iOS

Description of the Feature

The co-mic feature for audiences is a real-time interactive communication method. Through the co-mic feature, a host can interact in real time with up to 9 audience members, whether it's answering questions, sharing experiences, or engaging in entertainment interactions, greatly enhancing the audience's sense of participation and satisfaction. This direct interaction and communication provide a more convenient and efficient channel for commercial operations, while also offering audiences a more personalized and customized shopping experience. The audience co-mic feature is suitable for multiple scenarios, including e-commerce live streaming, entertainment live streaming, and online teaching etc.
Single-person co-mic
Multi-person co-mic







Use Instructions

Audience initiates join microphone application.

Click the co-mic request button
Choose the way to connect the microphone
Send a connection request and wait for the host to agree
After the host agrees, the connection is successful













The host handles the audience's mic connection requests.

Received the audience's connection request
Click on the connected mic user to open the connection dashboard
After clicking agree, the connection is successful










feature customization

Self Definition Host end mic connection management panel view

If you need the Self Definition Host end mic connection management panel view, please refer to the following path for changes.
// File Location:TUILiveKit/Source/View/LiveRoom/View/Anchor/LivingView

Panel // Directory of Anchor mic connection related views
└── AnchorLinkControlPanel.swift // Mic connection management panel: can accept audience mic connection, reject audience mic connection, hang up mic connection

Self Definition Audience end mic connection application panel view

If you need the Self Definition Audience end mic connection application panel view, please refer to the following path for changes.
Swift
// File Location:TUILiveKit/Source/View/LiveRoom/View/Audience/LivingView

Panel // Directory of Audience mic connection related views
├── LinkMicTypePanel.swift // The view that pops up for the audience to choose between voice mic connection or video mic connection
└── VideoLinkSettingPanel.swift // The configuration panel view for related parameters during video mic connection

Key code

Mic Connect

TUILiveKit audience mic connection feature is mainly implemented through SeatService. In SeatService, you can use the takeSeat interface to realize the audience mic connection feature. Taking audience B applying to co-anchor with anchor A as an example, the specific interaction sequence can be referred to in the diagram below:




Audience sends a mic connection request

Swift
// File location: TUILiveKit/iOS/TUILiveKit/Source/Service/SeatService.swift

func takeSeat(index: Int?, requestCallback:@escaping RequestClosure) -> AnyPublisher<TakeSeatResult, InternalError> {
return Future<TakeSeatResult, InternalError> { [weak self] promise in
guard let self = self else { return }
let request = roomEngine.takeSeat(index ?? kRandomSeatIndex , timeout: kTimeoutValue) { requestId, operateUserId in
// Callback for the anchor agreeing to the mic connection request
} onRejected: { requestId, operateUserId, message in
// Callback for the anchor rejecting the mic connection request
} onCancelled: { requestId, operateUserId in
// Callback for the audience actively canceling the mic connection request
} onTimeout: { requestId, operateUserId in
// Callback for the audience's mic connection request timeout
} onError: { requestId, operateUserId, err, message in
// Callback for failed mic connection request
}
requestCallback(request)
}
.eraseToAnyPublisher()
}

The anchor receives the mic connection request

Swift
// File location: TUILiveKit/iOS/TUILiveKit/Source/Service/EngineServiceCenter.swift

func onRequestReceived(request: TUIRequest) {
guard let store = self.store else { return }
switch request.requestAction {
case .takeSeat:
let seatApplication = SeatApplication(request: request)
store.dispatch(action: SeatActions.addSeatApplication(payload: seatApplication))
let actions: [ActionTemplate<User>] = [SeatActions.addSeatApplicationUser]
let param = generateActionTemplateParamTuple(param: request.userId, actions: actions)
store.dispatch(action: UserActions.fetchUserInfo(payload: param))
case .remoteUserOnSeat:
store.dispatch(action: SeatActions.updateReceivedSeatInvitation(payload: SeatInvitation(request: request)))
default:
break
}
}

The audience cancels the mic connection request

Swift
// File location: TUILiveKit/iOS/TUILiveKit/Source/Service/SeatService.swift

func cancelRequest(requestId: String) -> AnyPublisher<Void, InternalError> {
return Future { [weak self] promise in
guard let self = self else { return }
roomEngine.cancelRequest(requestId) {
promise(.success(()))
} onError: { err, message in
let error = InternalError(error: err, message: message)
promise(.failure(error))
}
}.eraseToAnyPublisher()
}

The anchor receives the cancel mic connection request

Swift
// File location: TUILiveKit/iOS/TUILiveKit/Source/Service/EngineServiceCenter.swift

func onRequestCancelled(requestId: String, userId: String) {
guard let store = self.store else { return }
let isContainApplicationRequest = store.selectCurrent(SeatSelectors.getSeatApplications).contains { $0.id == requestId }
if isContainApplicationRequest {
store.dispatch(action: SeatActions.removeSeatApplication(payload: requestId))
}
if store.selectCurrent(SeatSelectors.getReceivedSeatInvitation).id == requestId {
store.dispatch(action: SeatActions.updateReceivedSeatInvitation(payload: SeatInvitation()))
store.dispatch(action: ViewActions.toastEvent(payload: ToastInfo(message: .inviteCancelText)))
}
}

The anchor handles the mic connection request

Swift
// File location: TUILiveKit/iOS/TUILiveKit/Source/Service/SeatService.swift

func responseRemoteRequest(isAgree: Bool, requestId: String) -> AnyPublisher <Void, InternalError> {
return Future { [weak self] promise in
guard let self = self else { return }
roomEngine.responseRemoteRequest(requestId, agree: isAgree) {
promise(.success(()))
} onError: { err, message in
let error = InternalError(error: err, message: message)
promise(.failure(error))
}
}
.eraseToAnyPublisher()
}

After the audience successfully connects, the host hangs up the mic connection

Swift
// File location: TUILiveKit/iOS/TUILiveKit/Source/Service/SeatService.swift

func kickSeat(seat: SeatInfo) -> AnyPublisher<Void, InternalError> {
return Future<Void, InternalError> { [weak self] promise in
guard let self = self else { return }
roomEngine.kickUserOffSeatByAdmin(seat.index, userId: seat.userId) {
promise(.success(()))
} onError: { err, message in
let error = InternalError(error: err, message: message)
promise(.failure(error))
}
}
.eraseToAnyPublisher()
}

After the audience successfully connects, the audience ends the mic connection

Swift
// File location: TUILiveKit/iOS/TUILiveKit/Source/Service/SeatService.swift

func leaveSeat() -> AnyPublisher<Void, InternalError> {
return Future<Void, InternalError> { [weak self] promise in
guard let self = self else { return }
roomEngine.leaveSeat {
promise(.success(()))
} onError: { err, message in
let error = InternalError(error: err, message: message)
promise(.failure(error))
}
}
.eraseToAnyPublisher()
}