feature(agora): rtm version 1.4.10 -> 2.2.4

This commit is contained in:
Yu Sung
2025-10-30 15:58:57 +09:00
parent c7d165989c
commit 1159e5e53a
5 changed files with 423 additions and 298 deletions

View File

@@ -225,6 +225,8 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
private var blockedMemberIdList = Set<Int>()
private var hasInvokedJoinChannel = false
func getBlockedMemberIdList() {
userRepository.getBlockedMemberIdList()
.sink { result in
@@ -265,7 +267,7 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
func initAgoraEngine() {
agora.rtcEngineDelegate = self
agora.rtmDelegate = self
agora.rtmClientDelegate = self
agora.initialize()
}
@@ -370,15 +372,23 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
self.isActiveRoulette = data.isActiveRoulette
self.isLoading = true
self.agora.joinRtcChannel(rtcToken: data.rtcToken, channelName: data.channelName)
self.agora.rtmLogin(
creatorId: data.creatorId,
rtmToken: data.rtmToken,
channelName: data.channelName,
rtmChannelDelegate: self,
onConnectSuccess: self.agoraConnectSuccess,
onConnectFail: self.agoraConnectFail
)
let rtcState = self.agora.getRtcConnectionState()
let rtcConnected = rtcState == AgoraConnectionState.connected
let rtmLoggedIn = self.agora.isRtmLoggedIn()
if (!hasInvokedJoinChannel && !(rtcConnected && rtmLoggedIn)) {
hasInvokedJoinChannel = true
self.agora.joinRtcChannel(rtcToken: data.rtcToken, channelName: data.channelName)
self.agora.rtmLogin(
creatorId: data.creatorId,
rtmToken: data.rtmToken,
channelName: data.channelName,
onConnectSuccess: self.agoraConnectSuccess,
onConnectFail: self.agoraConnectFail
)
} else {
DEBUG_LOG("joinChannel - skip (rtcConnected=\(rtcConnected), rtmLoggedIn=\(rtmLoggedIn), hasInvokedJoinChannel=\(hasInvokedJoinChannel))")
}
getTotalDonationCan()
getTotalHeartCount()
@@ -435,20 +445,17 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
self.popupContent = "\(remainingNoChattingTime)초 동안 채팅하실 수 없습니다"
self.isShowPopup = true
} else if chatMessage.count > 0 {
agora.sendMessageToGroup(textMessage: chatMessage, completion: { [unowned self] errorCode in
if errorCode == .errorOk {
agora.sendMessageToGroup(textMessage: chatMessage) { _, error in
if error == nil {
let (nickname, profileUrl) = self.getUserNicknameAndProfileUrl(accountId: UserDefaults.int(forKey: .userId))
let rank = getUserRank(userId: UserDefaults.int(forKey: .userId))
let rank = self.getUserRank(userId: UserDefaults.int(forKey: .userId))
self.messages.append(LiveRoomNormalChat(userId: UserDefaults.int(forKey: .userId), profileUrl: profileUrl, nickname: nickname, rank: rank, chat: chatMessage))
self.messageChangeFlag.toggle()
if self.messages.count > 100 {
self.messages.remove(at: 0)
}
self.invalidateChat()
}
onSuccess()
})
}
}
}
}
@@ -501,68 +508,54 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
UserDefaults.set(UserDefaults.int(forKey: .can) - can, forKey: .can)
if isSecret {
agora.sendRawMessageToPeer(
peerId: String(liveRoomInfo!.creatorId), rawMessage: donationRawMessage,
completion: { [unowned self] errorCode in
if errorCode == .ok {
let (nickname, profileUrl) = self.getUserNicknameAndProfileUrl(accountId: UserDefaults.int(forKey: .userId))
self.messages.append(
LiveRoomDonationChat(
memberId: UserDefaults.int(forKey: .userId),
profileUrl: profileUrl,
nickname: nickname,
chat: rawMessage,
can: can,
donationMessage: message
)
agora.sendRawMessageToPeer(peerId: String(liveRoomInfo!.creatorId), rawMessage: donationRawMessage) { [unowned self] _, error in
if error == nil {
let (nickname, profileUrl) = self.getUserNicknameAndProfileUrl(accountId: UserDefaults.int(forKey: .userId))
self.messages.append(
LiveRoomDonationChat(
memberId: UserDefaults.int(forKey: .userId),
profileUrl: profileUrl,
nickname: nickname,
chat: rawMessage,
can: can,
donationMessage: message
)
addSignature(signature: decoded.data)
self.messageChangeFlag.toggle()
if self.messages.count > 100 {
self.messages.remove(at: 0)
}
} else {
refundDonation()
}
},
fail: { [unowned self] in
)
addSignature(signature: decoded.data)
self.invalidateChat()
} else {
refundDonation()
}
)
} fail: { [unowned self] in
refundDonation()
}
} else {
agora.sendRawMessageToGroup(
rawMessage: donationRawMessage,
completion: { [unowned self] errorCode in
if errorCode == .errorOk {
let (nickname, profileUrl) = self.getUserNicknameAndProfileUrl(accountId: UserDefaults.int(forKey: .userId))
self.messages.append(
LiveRoomDonationChat(
memberId: UserDefaults.int(forKey: .userId),
profileUrl: profileUrl,
nickname: nickname,
chat: rawMessage,
can: can,
donationMessage: message
)
agora.sendRawMessageToGroup(rawMessage: donationRawMessage) { [unowned self] _, error in
if error == nil {
let (nickname, profileUrl) = self.getUserNicknameAndProfileUrl(accountId: UserDefaults.int(forKey: .userId))
self.messages.append(
LiveRoomDonationChat(
memberId: UserDefaults.int(forKey: .userId),
profileUrl: profileUrl,
nickname: nickname,
chat: rawMessage,
can: can,
donationMessage: message
)
totalDonationCan += can
addSignature(signature: decoded.data)
self.messageChangeFlag.toggle()
if self.messages.count > 100 {
self.messages.remove(at: 0)
}
} else {
refundDonation()
}
},
fail: { [unowned self] in
)
totalDonationCan += can
addSignature(signature: decoded.data)
self.invalidateChat()
} else {
refundDonation()
}
)
} fail: { [unowned self] in
refundDonation()
}
}
} else {
if let message = decoded.message {
@@ -625,17 +618,17 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
}
func inviteSpeaker(peerId: Int) {
agora.sendMessageToPeer(peerId: String(peerId), rawMessage: LiveRoomRequestType.INVITE_SPEAKER.rawValue.data(using: .utf8)!, completion: { [unowned self] errorCode in
if errorCode == .ok {
agora.sendMessageToPeer(peerId: String(peerId), rawMessage: LiveRoomRequestType.INVITE_SPEAKER.rawValue.data(using: .utf8)!) { [unowned self] _, error in
if error == nil {
self.popupContent = "스피커 요청을 보냈습니다.\n잠시만 기다려 주세요."
self.isShowPopup = true
}
})
}
}
func changeListener(peerId: Int, isFromManager: Bool = false) {
agora.sendMessageToPeer(peerId: String(peerId), rawMessage: LiveRoomRequestType.CHANGE_LISTENER.rawValue.data(using: .utf8)!, completion: { [unowned self] errorCode in
if errorCode == .ok {
agora.sendMessageToPeer(peerId: String(peerId), rawMessage: LiveRoomRequestType.CHANGE_LISTENER.rawValue.data(using: .utf8)!) { [unowned self] _, error in
if error == nil {
if isFromManager {
getRoomInfo()
setManagerMessage()
@@ -647,7 +640,7 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
self.isShowPopup = true
}
})
}
}
private func getUserNicknameAndProfileUrl(accountId: Int) -> (nickname: String, profileUrl: String) {
@@ -977,12 +970,10 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
.store(in: &subscription)
let nickname = getUserNicknameAndProfileUrl(accountId: kickOutId).nickname
agora.sendMessageToPeer(peerId: String(kickOutId), rawMessage: LiveRoomRequestType.KICK_OUT.rawValue.data(using: .utf8)!, completion: { [unowned self] errorCode in
if errorCode == .ok {
self.popupContent = "\(nickname)님을 내보냈습니다."
self.isShowPopup = true
}
})
agora.sendMessageToPeer(peerId: String(kickOutId), rawMessage: LiveRoomRequestType.KICK_OUT.rawValue.data(using: .utf8)!) { [unowned self] _, error in
self.popupContent = "\(nickname)님을 내보냈습니다."
self.isShowPopup = true
}
}
if let index = muteSpeakers.firstIndex(of: UInt(kickOutId)) {
@@ -1424,22 +1415,18 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
}
func setNoChatting() {
agora.sendMessageToPeer(
peerId: String(noChattingUserId),
rawMessage: LiveRoomRequestType.NO_CHATTING.rawValue.data(using: .utf8)!,
completion: { [unowned self] errorCode in
if errorCode == .ok {
self.popupContent = "\(noChattingUserNickname)님을 3분간 채팅금지를 하였습니다."
self.isShowPopup = true
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.noChattingUserId = 0
self.noChattingUserNickname = ""
self.noChattingUserProfileUrl = ""
}
agora.sendMessageToPeer(peerId: String(noChattingUserId), rawMessage: LiveRoomRequestType.NO_CHATTING.rawValue.data(using: .utf8)!) { [unowned self] _, error in
if error == nil {
self.popupContent = "\(noChattingUserNickname)님을 3분간 채팅금지를 하였습니다."
self.isShowPopup = true
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.noChattingUserId = 0
self.noChattingUserNickname = ""
self.noChattingUserProfileUrl = ""
}
}
)
}
}
private func setManagerMessage() {
@@ -1627,7 +1614,8 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
private func removeNoChatRoom() {
var noChatRoomList = getNoChatRoomListFromUserDefaults()
if let index = noChatRoomList.firstIndex(of: liveRoomInfo!.roomId) {
guard let roomId = liveRoomInfo?.roomId else { return }
if let index = noChatRoomList.firstIndex(of: roomId) {
noChatRoomList.remove(at: index)
}
saveNoChatRoomListToUserDefaults(noChatRoomList: noChatRoomList)
@@ -1760,36 +1748,29 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
donationMessage: ""
)
self.agora.sendRawMessageToGroup(
rawMessage: rouletteRawMessage,
completion: { [unowned self] errorCode in
if errorCode == .errorOk {
let (nickname, profileUrl) = self.getUserNicknameAndProfileUrl(accountId: UserDefaults.int(forKey: .userId))
self.messages.append(
LiveRoomRouletteDonationChat(
profileUrl: profileUrl,
nickname: nickname,
rouletteResult: rouletteSelectedItem
)
agora.sendRawMessageToGroup(rawMessage: rouletteRawMessage) { [unowned self] _, error in
if error == nil {
let (nickname, profileUrl) = self.getUserNicknameAndProfileUrl(accountId: UserDefaults.int(forKey: .userId))
self.messages.append(
LiveRoomRouletteDonationChat(
profileUrl: profileUrl,
nickname: nickname,
rouletteResult: rouletteSelectedItem
)
totalDonationCan += rouletteCan
self.rouletteItems.removeAll()
self.rouletteSelectedItem = ""
self.rouletteCan = 0
self.messageChangeFlag.toggle()
if self.messages.count > 100 {
self.messages.remove(at: 0)
}
} else {
self.refundRouletteDonation()
}
},
fail: { [unowned self] in
)
totalDonationCan += rouletteCan
self.rouletteItems.removeAll()
self.rouletteSelectedItem = ""
self.rouletteCan = 0
self.invalidateChat()
} else {
self.refundRouletteDonation()
}
)
} fail: { [unowned self] in
self.refundRouletteDonation()
}
}
func stopPeriodicPlaybackValidation() {
@@ -1949,28 +1930,21 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
donationMessage: nil
)
agora.sendRawMessageToGroup(
rawMessage: donationRawMessage,
completion: { [unowned self] errorCode in
if errorCode == .errorOk {
let (nickname, _) = self.getUserNicknameAndProfileUrl(accountId: UserDefaults.int(forKey: .userId))
self.addHeartMessage(nickname: nickname)
totalHeartCount += 1
addHeart()
self.messageChangeFlag.toggle()
if self.messages.count > 100 {
self.messages.remove(at: 0)
}
} else {
refundDonation()
}
},
fail: { [unowned self] in
refundDonation()
agora.sendRawMessageToGroup(rawMessage: donationRawMessage) { [unowned self] _, error in
if error == nil {
let (nickname, _) = self.getUserNicknameAndProfileUrl(accountId: UserDefaults.int(forKey: .userId))
self.addHeartMessage(nickname: nickname)
totalHeartCount += 1
addHeart()
self.invalidateChat()
} else {
self.refundDonation()
}
)
} fail: { [unowned self] in
self.refundDonation()
}
}
} catch {
refundDonation()
@@ -2073,6 +2047,13 @@ final class LiveRoomViewModel: NSObject, ObservableObject {
heartTimer?.cancel()
heartTimer = nil
}
private func invalidateChat() {
messageChangeFlag.toggle()
if messages.count > 100 {
messages.remove(at: 0)
}
}
}
extension LiveRoomViewModel: AgoraRtcEngineDelegate {
@@ -2081,7 +2062,6 @@ extension LiveRoomViewModel: AgoraRtcEngineDelegate {
.filter { $0.volume > 0 }
.map { $0.uid }
DEBUG_LOG("activeSpeakerIds::: \(activeSpeakerIds)")
activeSpeakers.removeAll()
activeSpeakers.append(contentsOf: activeSpeakerIds)
}
@@ -2115,10 +2095,17 @@ extension LiveRoomViewModel: AgoraRtcEngineDelegate {
}
}
extension LiveRoomViewModel: AgoraRtmDelegate {
func rtmKit(_ kit: AgoraRtmKit, messageReceived message: AgoraRtmMessage, fromPeer peerId: String) {
if message.type == .raw, let rawMessage = message as? AgoraRtmRawMessage {
let rawMessageString = String(data: rawMessage.rawData, encoding: .utf8)
extension LiveRoomViewModel: AgoraRtmClientDelegate {
func rtmKit(_ rtmKit: AgoraRtmClientKit, didReceiveMessageEvent event: AgoraRtmMessageEvent) {
DEBUG_LOG("Message received.\n channel: \(event.channelName), publisher: \(event.publisher)")
let rawMessage = event.message.rawData
let textMessage = event.message.stringData
let publisher = event.publisher
let (nickname, profileUrl) = getUserNicknameAndProfileUrl(accountId: Int(publisher)!)
if let message = rawMessage {
let rawMessageString = String(data: message, encoding: .utf8)
DispatchQueue.main.async { [unowned self] in
if rawMessageString == LiveRoomRequestType.CHANGE_LISTENER.rawValue {
@@ -2127,7 +2114,7 @@ extension LiveRoomViewModel: AgoraRtmDelegate {
}
if rawMessageString == LiveRoomRequestType.REQUEST_SPEAKER.rawValue {
self.popupContent = "\(getUserNicknameAndProfileUrl(accountId: Int(peerId)!).nickname)님이 스피커 요청을 했어요!\n스퍼커로 초대할까요?"
self.popupContent = "\(nickname)님이 스피커 요청을 했어요!\n스퍼커로 초대할까요?"
self.popupCancelTitle = "건너뛰기"
self.popupCancelAction = {
self.isShowPopup = false
@@ -2136,7 +2123,7 @@ extension LiveRoomViewModel: AgoraRtmDelegate {
self.popupConfirmAction = {
self.isShowPopup = false
if self.liveRoomInfo!.speakerList.count <= 5 {
self.requestSpeakerAllow(peerId)
self.requestSpeakerAllow(publisher)
} else {
self.errorMessage = "스피커 정원이 초과되었습니다."
self.isShowErrorPopup = true
@@ -2214,13 +2201,12 @@ extension LiveRoomViewModel: AgoraRtmDelegate {
do {
let jsonDecoder = JSONDecoder()
let decoded = try jsonDecoder.decode(LiveRoomChatRawMessage.self, from: rawMessage.rawData)
let (nickname, profileUrl) = getUserNicknameAndProfileUrl(accountId: Int(peerId)!)
let decoded = try jsonDecoder.decode(LiveRoomChatRawMessage.self, from: message)
if decoded.type == .SECRET_DONATION {
self.messages.append(
LiveRoomDonationChat(
memberId: Int(peerId)!,
memberId: Int(publisher)!,
profileUrl: profileUrl,
nickname: nickname,
chat: decoded.message,
@@ -2234,98 +2220,86 @@ extension LiveRoomViewModel: AgoraRtmDelegate {
} else if let imageUrl = decoded.signatureImageUrl {
self.addSignatureImage(imageUrl: imageUrl)
}
} else if decoded.type == .DONATION {
self.messages.append(
LiveRoomDonationChat(
memberId: Int(publisher)!,
profileUrl: profileUrl,
nickname: nickname,
chat: decoded.message,
can: decoded.can,
donationMessage: decoded.donationMessage ?? ""
)
)
self.totalDonationCan += decoded.can
if let signature = decoded.signature {
self.addSignature(signature: signature)
} else if let imageUrl = decoded.signatureImageUrl {
self.addSignatureImage(imageUrl: imageUrl)
}
} else if decoded.type == .ROULETTE_DONATION {
self.messages.append(
LiveRoomRouletteDonationChat(
profileUrl: profileUrl,
nickname: nickname,
rouletteResult: decoded.message
)
)
self.totalDonationCan += decoded.can
} else if decoded.type == .TOGGLE_ROULETTE && decoded.isActiveRoulette != nil {
self.isActiveRoulette = decoded.isActiveRoulette!
} else if decoded.type == .EDIT_ROOM_INFO || decoded.type == .SET_MANAGER {
self.getRoomInfo()
} else if decoded.type == .HEART_DONATION {
self.addHeartMessage(nickname: nickname)
self.totalHeartCount += decoded.can
self.addHeart()
}
} catch {
}
}
}
}
}
extension LiveRoomViewModel: AgoraRtmChannelDelegate {
func channel(_ channel: AgoraRtmChannel, messageReceived message: AgoraRtmMessage, from member: AgoraRtmMember) {
let (nickname, profileUrl) = getUserNicknameAndProfileUrl(accountId: Int(member.userId)!)
if message.type == .raw, let rawMessage = message as? AgoraRtmRawMessage {
do {
let jsonDecoder = JSONDecoder()
let decoded = try jsonDecoder.decode(LiveRoomChatRawMessage.self, from: rawMessage.rawData)
if decoded.type == .DONATION {
self.messages.append(
LiveRoomDonationChat(
memberId: Int(member.userId)!,
profileUrl: profileUrl,
nickname: nickname,
chat: decoded.message,
can: decoded.can,
donationMessage: decoded.donationMessage ?? ""
)
)
self.totalDonationCan += decoded.can
if let signature = decoded.signature {
self.addSignature(signature: signature)
} else if let imageUrl = decoded.signatureImageUrl {
self.addSignatureImage(imageUrl: imageUrl)
}
} else if decoded.type == .ROULETTE_DONATION {
self.messages.append(
LiveRoomRouletteDonationChat(
profileUrl: profileUrl,
nickname: nickname,
rouletteResult: decoded.message
)
)
self.totalDonationCan += decoded.can
} else if decoded.type == .TOGGLE_ROULETTE && decoded.isActiveRoulette != nil {
self.isActiveRoulette = decoded.isActiveRoulette!
} else if decoded.type == .EDIT_ROOM_INFO || decoded.type == .SET_MANAGER {
self.getRoomInfo()
} else if decoded.type == .HEART_DONATION {
self.addHeartMessage(nickname: nickname)
self.totalHeartCount += decoded.can
self.addHeart()
}
} catch {
}
} else {
let memberId = Int(member.userId) ?? 0
let chat = message.text
if let message = textMessage {
let memberId = Int(publisher) ?? 0
let rank = getUserRank(userId: memberId)
if !chat.trimmingCharacters(in: .whitespaces).isEmpty && !blockedMemberIdList.contains(memberId) {
messages.append(LiveRoomNormalChat(userId: memberId, profileUrl: profileUrl, nickname: nickname, rank: rank, chat: chat))
if !message.trimmingCharacters(in: .whitespaces).isEmpty && !blockedMemberIdList.contains(memberId) {
messages.append(LiveRoomNormalChat(userId: memberId, profileUrl: profileUrl, nickname: nickname, rank: rank, chat: message))
}
}
DispatchQueue.main.async { [unowned self] in
self.messageChangeFlag.toggle()
if self.messages.count > 100 {
self.messages.remove(at: 0)
}
self.invalidateChat()
}
}
func channel(_ channel: AgoraRtmChannel, memberJoined member: AgoraRtmMember) {
getRoomInfo(userId: Int(member.userId)!) { [unowned self] nickname in
if !nickname.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && isEntryMessageEnabled {
DispatchQueue.main.async { [unowned self] in
self.messages.append(LiveRoomJoinChat(nickname: nickname))
self.messageChangeFlag.toggle()
if self.messages.count > 100 {
self.messages.remove(at: 0)
func rtmKit(_ rtmKit: AgoraRtmClientKit, didReceivePresenceEvent event: AgoraRtmPresenceEvent) {
DEBUG_LOG("didReceivePresenceEvent - \(event.type) - \(String(describing: event.publisher))")
let eventType = event.type
if let memberId = event.publisher {
if eventType == .remoteJoinChannel {
getRoomInfo(userId: Int(memberId)!) { [unowned self] nickname in
if !nickname.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && isEntryMessageEnabled {
DispatchQueue.main.async { [unowned self] in
self.messages.append(LiveRoomJoinChat(nickname: nickname))
self.invalidateChat()
}
}
}
} else if eventType == .remoteLeaveChannel {
if let liveRoomInfo = liveRoomInfo, liveRoomInfo.creatorId != Int(memberId)! {
getRoomInfo()
}
}
}
}
func channel(_ channel: AgoraRtmChannel, memberLeft member: AgoraRtmMember) {
if let liveRoomInfo = liveRoomInfo, liveRoomInfo.creatorId != Int(member.userId)! {
getRoomInfo()
}
func rtmKit(_ rtmKit: AgoraRtmClientKit, didReceiveLinkStateEvent event: AgoraRtmLinkStateEvent) {
DEBUG_LOG("Signaling link state change current state is: \(event.currentState.rawValue) previous state is :\(event.previousState.rawValue)")
}
}