import SMeeting
import SRTC
import SwiftUI
@MainActor
final class MeetingViewModel: ObservableObject {
/// SDK 主入口,整个 App 生命周期内只创建一个实例
let meeting = SMeeting(logLevel: .info)
@Published var users: [MeetingUserInfo] = []
@Published var isInMeeting = false
@Published var isCameraOn = false
/// 1. 登录:token 来自业务后端
func login(token: String) async throws {
try await meeting.login(token: token)
meeting.delegates.add(delegate: self)
}
/// 2. 创建一场即时会议并进入
func createAndEnter(nickname: String) async throws {
var createReq = MeetingCreateReq(title: "\(nickname) 的会议", meetingMode: .normal)
createReq.meetingType = .instant
let (_, meetingId) = try await meeting.createRoom(createReq)
try await meeting.enterRoom(MeetingEnterReq(nickname: nickname, meetingId: meetingId))
isInMeeting = true
users = meeting.getUsersInfoList()
}
/// 3. 用房间号进入已有会议
func enter(roomNo: String, nickname: String, password: String? = nil) async throws {
var enterReq = MeetingEnterReq(nickname: nickname, roomNo: roomNo)
enterReq.password = password
try await meeting.enterRoom(enterReq)
isInMeeting = true
users = meeting.getUsersInfoList()
}
/// 4. 打开本地麦克风与摄像头(SwiftUI 场景不需要传预览 view)
func openLocalMedia() async throws {
try await meeting.requestOpenMic()
try await meeting.requestOpenCamera()
isCameraOn = true // 触发界面刷新,让本端画面显示出来
}
/// 5. 退出会议
func exit() async {
await meeting.closeCamera()
await meeting.closeMic()
await meeting.exitRoom()
isCameraOn = false
isInMeeting = false
users = []
}
}
// 事件回调在主线程派发;@MainActor 类型需要把协议方法声明为 nonisolated
extension MeetingViewModel: SMeetingDelegate {
nonisolated func meeting(_ meeting: SMeeting, userDidEnter user: MeetingUserInfo) {
DispatchQueue.main.async { self.users = meeting.getUsersInfoList() }
}
nonisolated func meeting(_ meeting: SMeeting, userDidExit data: UserExitEventData) {
DispatchQueue.main.async { self.users = meeting.getUsersInfoList() }
}
}
struct MeetingView: View {
@StateObject private var vm = MeetingViewModel()
var body: some View {
VStack(spacing: 8) {
// 本端画面:把本地摄像头轨道交给 SRTCVideoView
if vm.isCameraOn, let cameraTrack = vm.meeting.cameraTrack {
SRTCVideoView(track: cameraTrack).frame(height: 180)
}
// 远端画面:组件内部负责订阅与退订
ForEach(vm.users.filter { $0.uid != vm.meeting.currentUserId }, id: \.uid) { user in
SMeetingRemoteVideoView(meeting: vm.meeting, uid: user.uid, trackDesc: .cameraBig)
.frame(height: 180)
}
}
}
}