// // GroupIMService.swift // QuickLocation // // Created by 八条 on 2026/6/4. // import Foundation import OpenIMSDK final class GroupIMService { static let shared = GroupIMService() private var isInited = false private var isLogining = false /// 登录进行中时排队的回调,登录结束统一回调,避免并发重复登录 private var pendingLoginCompletions: [(Bool) -> Void] = [] private init() {} // MARK: - Init SDK func initSDK() { // 幂等:SDK 全局只初始化一次 guard !isInited else { return } isInited = true let config = OIMInitConfig() config.apiAddr = URLManager.shared.openIM_API config.wsAddr = URLManager.shared.openIM_WS config.platform = .iPhone OIMManager.manager.initSDK(with: config, onConnecting: {}, onConnectFailure: { _, _ in }, onConnectSuccess: {}, onKickedOffline: {}, onUserTokenExpired: {}, onUserTokenInvalid: { _ in }) } // MARK: - Login /// 确保已登录(幂等):已登录直接回调,否则发起登录;登录中则排队等待 func ensureLogin(completion: @escaping (Bool) -> Void) { if OIMManager.manager.getLoginStatus() == .logged { completion(true) return } login(completion: completion) } func login(completion: @escaping (Bool) -> Void) { // 已登录,直接成功 if OIMManager.manager.getLoginStatus() == .logged { completion(true) return } guard let token = AppContextManager.shared.imToken, !token.isEmpty else { completion(false) return } let userId = AppContextManager.shared.userId guard !userId.isEmpty else { completion(false) return } // 登录中:排队,复用同一次登录结果,避免重复发起 pendingLoginCompletions.append(completion) guard !isLogining else { return } isLogining = true OIMManager.manager.login(userId, token: token) { [weak self] _ in self?.finishLogin(success: true) } onFailure: { [weak self] code, msg in print("OpenIM login failed: \(code) \(msg ?? "")") self?.finishLogin(success: false) } } private func finishLogin(success: Bool) { isLogining = false let callbacks = pendingLoginCompletions pendingLoginCompletions.removeAll() callbacks.forEach { $0(success) } } // MARK: - Logout func logout(completion: ((Bool) -> Void)? = nil) { OIMManager.manager.logoutWith(onSuccess: { (_: String?) in completion?(true) }, onFailure: { (code: Int, msg: String?) in print("OpenIM logout failed: \(code) \(msg ?? "")") completion?(false) }) } // MARK: - Get Joined Groups func getJoinedGroups(completion: @escaping ([OIMGroupInfo]) -> Void) { OIMManager.manager.getJoinedGroupListWith(onSuccess: { groups in completion(groups ?? []) }, onFailure: { code, msg in print("GroupIMService: getJoinedGroups failed: \(code) \(msg ?? "")") completion([]) }) } // MARK: - Conversation List (for unread count & last msg time) func getConversationList(completion: @escaping ([OIMConversationInfo]) -> Void) { OIMManager.manager.getAllConversationListWith(onSuccess: { list in completion(list ?? []) }, onFailure: { code, msg in completion([]) }) } // MARK: - Conversation Listener private var conversationListener: ConversationListenerProxy? func setConversationListener(_ handler: @escaping ([OIMConversationInfo]) -> Void) { conversationListener = ConversationListenerProxy(handler: handler) OIMManager.callbacker.addConversationListener(listener: conversationListener!) } // MARK: - Group Listener private var groupListener: GroupListenerProxy? /// 监听群组变化(加群/退群/群信息变更),用于 SDK 同步到新群时刷新列表 func setGroupListener(_ handler: @escaping () -> Void) { groupListener = GroupListenerProxy(handler: handler) OIMManager.callbacker.addGroupListener(listener: groupListener!) } } // MARK: - GroupListenerProxy private class GroupListenerProxy: NSObject, OIMGroupListener { private let handler: () -> Void init(handler: @escaping () -> Void) { self.handler = handler } func onJoinedGroupAdded(_ groupInfo: OIMGroupInfo) { handler() } func onJoinedGroupDeleted(_ groupInfo: OIMGroupInfo) { handler() } func onGroupInfoChanged(_ changeInfo: OIMGroupInfo) { handler() } } // MARK: - ConversationListenerProxy private class ConversationListenerProxy: NSObject, OIMConversationListener { private let handler: ([OIMConversationInfo]) -> Void init(handler: @escaping ([OIMConversationInfo]) -> Void) { self.handler = handler } func onConversationChanged(_ conversations: [OIMConversationInfo]) { handler(conversations) } func onNewConversation(_ conversations: [OIMConversationInfo]) { handler(conversations) } }