chore: sync local changes
这个提交包含在:
父节点
bc77850c3e
当前提交
5df6d70065
@ -6,6 +6,7 @@ let package = Package(
|
|||||||
platforms: [.iOS(.v16), .macOS(.v13)],
|
platforms: [.iOS(.v16), .macOS(.v13)],
|
||||||
products: [
|
products: [
|
||||||
.library(name: "XuqmSDK", targets: ["XuqmSDK"]),
|
.library(name: "XuqmSDK", targets: ["XuqmSDK"]),
|
||||||
|
.library(name: "XuqmWebViewSDK", targets: ["XuqmWebViewSDK"]),
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.target(
|
.target(
|
||||||
@ -13,6 +14,12 @@ let package = Package(
|
|||||||
path: "Sources/XuqmSDK",
|
path: "Sources/XuqmSDK",
|
||||||
swiftSettings: [.enableExperimentalFeature("StrictConcurrency")]
|
swiftSettings: [.enableExperimentalFeature("StrictConcurrency")]
|
||||||
),
|
),
|
||||||
|
.target(
|
||||||
|
name: "XuqmWebViewSDK",
|
||||||
|
dependencies: ["XuqmSDK"],
|
||||||
|
path: "Sources/XuqmWebViewSDK",
|
||||||
|
swiftSettings: [.enableExperimentalFeature("StrictConcurrency")]
|
||||||
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "XuqmSDKTests",
|
name: "XuqmSDKTests",
|
||||||
dependencies: ["XuqmSDK"],
|
dependencies: ["XuqmSDK"],
|
||||||
|
|||||||
39
README.md
39
README.md
@ -8,8 +8,9 @@
|
|||||||
XuqmGroup-iOSSDK/
|
XuqmGroup-iOSSDK/
|
||||||
├── Package.swift # SPM 包定义
|
├── Package.swift # SPM 包定义
|
||||||
├── XuqmSDK.podspec # CocoaPods 发版描述
|
├── XuqmSDK.podspec # CocoaPods 发版描述
|
||||||
|
├── XuqmWebViewSDK.podspec # WebView 独立模块发版描述
|
||||||
├── PUBLISH.md # 发版操作手册
|
├── PUBLISH.md # 发版操作手册
|
||||||
└── Sources/XuqmSDK/
|
├── Sources/XuqmSDK/
|
||||||
├── Core/
|
├── Core/
|
||||||
│ ├── XuqmSDK.swift # 入口:init / setToken
|
│ ├── XuqmSDK.swift # 入口:init / setToken
|
||||||
│ ├── SDKConfig.swift # 配置结构体
|
│ ├── SDKConfig.swift # 配置结构体
|
||||||
@ -21,6 +22,11 @@ XuqmGroup-iOSSDK/
|
|||||||
│ └── PushSDK.swift # APNs Token 注册
|
│ └── PushSDK.swift # APNs Token 注册
|
||||||
└── Update/
|
└── Update/
|
||||||
└── UpdateSDK.swift # 版本检查 / App 更新
|
└── UpdateSDK.swift # 版本检查 / App 更新
|
||||||
|
└── Sources/XuqmWebViewSDK/
|
||||||
|
├── XWebViewBridge.swift # 页面配置 / 控制器桥接
|
||||||
|
├── XWebViewPage.swift # 独立页面
|
||||||
|
├── XWebViewTypes.swift # 配置 / 控制器协议
|
||||||
|
└── XWebViewView.swift # 嵌入式组件
|
||||||
```
|
```
|
||||||
|
|
||||||
## 集成
|
## 集成
|
||||||
@ -39,7 +45,7 @@ dependencies: [
|
|||||||
.package(url: "https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK.git", from: "0.1.0")
|
.package(url: "https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK.git", from: "0.1.0")
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.target(name: "YourApp", dependencies: ["XuqmSDK"])
|
.target(name: "YourApp", dependencies: ["XuqmSDK", "XuqmWebViewSDK"])
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -51,6 +57,7 @@ source 'https://xuqinmin.com/xuqinmin12/xuqm-specs.git'
|
|||||||
source 'https://cdn.cocoapods.org/'
|
source 'https://cdn.cocoapods.org/'
|
||||||
|
|
||||||
pod 'XuqmSDK', '~> 0.1.0'
|
pod 'XuqmSDK', '~> 0.1.0'
|
||||||
|
pod 'XuqmWebViewSDK', '~> 0.1.0'
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -81,6 +88,32 @@ struct MyApp: App {
|
|||||||
await XuqmSDK.setToken(token)
|
await XuqmSDK.setToken(token)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 3. WebView 独立模块
|
||||||
|
|
||||||
|
`XuqmWebViewSDK` 不需要单独初始化,直接依赖即可使用。
|
||||||
|
|
||||||
|
```swift
|
||||||
|
import XuqmWebViewSDK
|
||||||
|
|
||||||
|
// 嵌入式组件:直接放到任意 SwiftUI 页面中
|
||||||
|
XWebViewView(
|
||||||
|
config: XWebViewConfig(
|
||||||
|
url: "https://example.com",
|
||||||
|
title: "嵌入式网页"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// 独立页面:在导航栈中直接 push
|
||||||
|
NavigationLink("打开页面模式") {
|
||||||
|
XWebViewPage(
|
||||||
|
config: XWebViewConfig(
|
||||||
|
url: "https://example.com",
|
||||||
|
title: "独立页面"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Core
|
## Core
|
||||||
@ -227,6 +260,8 @@ if result.needsUpdate, let info = result.info {
|
|||||||
|
|
||||||
`UpdateSDK` 这里只负责 iOS App 版本更新。RN 热更新如果需要,走独立的 RN SDK / RN 更新模块,不并入统一发版能力。
|
`UpdateSDK` 这里只负责 iOS App 版本更新。RN 热更新如果需要,走独立的 RN SDK / RN 更新模块,不并入统一发版能力。
|
||||||
|
|
||||||
|
`XuqmWebViewSDK` 提供独立的 WebView 组件和页面,可与 `XuqmSDK` 同时使用,也可单独依赖。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 发版
|
## 发版
|
||||||
|
|||||||
@ -31,7 +31,6 @@ public extension SDKConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension SDKConfig {
|
extension SDKConfig {
|
||||||
var appId: String { appKey }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SDKEndpoints {
|
enum SDKEndpoints {
|
||||||
|
|||||||
@ -11,13 +11,18 @@ public final class XuqmSDK: NSObject {
|
|||||||
|
|
||||||
private var userSig: String?
|
private var userSig: String?
|
||||||
private var cachedDeviceToken: String?
|
private var cachedDeviceToken: String?
|
||||||
|
private var lastInitializedAppKey: String?
|
||||||
|
|
||||||
private override init() {
|
private override init() {
|
||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func initialize(config: SDKConfig) {
|
public func initialize(config: SDKConfig) {
|
||||||
|
if let lastInitializedAppKey, lastInitializedAppKey == config.appKey {
|
||||||
|
return
|
||||||
|
}
|
||||||
self.config = config
|
self.config = config
|
||||||
|
self.lastInitializedAppKey = config.appKey
|
||||||
self.tokenStore = TokenStore()
|
self.tokenStore = TokenStore()
|
||||||
ApiClient.shared.configure(with: config)
|
ApiClient.shared.configure(with: config)
|
||||||
if config.autoRegisterPush {
|
if config.autoRegisterPush {
|
||||||
@ -35,6 +40,9 @@ public final class XuqmSDK: NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func login(userId: String, userSig: String) async {
|
public func login(userId: String, userSig: String) async {
|
||||||
|
if currentUserId == userId && self.userSig == userSig {
|
||||||
|
return
|
||||||
|
}
|
||||||
self.currentUserId = userId
|
self.currentUserId = userId
|
||||||
self.userSig = userSig
|
self.userSig = userSig
|
||||||
|
|
||||||
|
|||||||
@ -13,16 +13,16 @@ public final class ImClient: NSObject, URLSessionWebSocketDelegate, @unchecked S
|
|||||||
private var groupSubscriptions = Set<String>()
|
private var groupSubscriptions = Set<String>()
|
||||||
|
|
||||||
private let tokenOverride: String?
|
private let tokenOverride: String?
|
||||||
private let appIdOverride: String?
|
private let appKeyOverride: String?
|
||||||
|
|
||||||
private var activeWsURL: URL?
|
private var activeWsURL: URL?
|
||||||
private var activeToken: String?
|
private var activeToken: String?
|
||||||
private var activeAppId: String?
|
private var activeAppKey: String?
|
||||||
private var activeUserId: String?
|
private var activeUserId: String?
|
||||||
|
|
||||||
public init(token: String? = nil, appId: String? = nil) {
|
public init(token: String? = nil, appKey: String? = nil) {
|
||||||
self.tokenOverride = token
|
self.tokenOverride = token
|
||||||
self.appIdOverride = appId
|
self.appKeyOverride = appKey
|
||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ public final class ImClient: NSObject, URLSessionWebSocketDelegate, @unchecked S
|
|||||||
|
|
||||||
activeWsURL = SDKEndpoints.imWebSocketURL
|
activeWsURL = SDKEndpoints.imWebSocketURL
|
||||||
activeToken = tokenOverride
|
activeToken = tokenOverride
|
||||||
activeAppId = appIdOverride
|
activeAppKey = appKeyOverride
|
||||||
|
|
||||||
guard let activeWsURL, let activeToken else {
|
guard let activeWsURL, let activeToken else {
|
||||||
delegate?.imClientDidError("IM config or token not found")
|
delegate?.imClientDidError("IM config or token not found")
|
||||||
@ -73,7 +73,7 @@ public final class ImClient: NSObject, URLSessionWebSocketDelegate, @unchecked S
|
|||||||
let now = Int64(Date().timeIntervalSince1970 * 1000)
|
let now = Int64(Date().timeIntervalSince1970 * 1000)
|
||||||
let message = ImMessage(
|
let message = ImMessage(
|
||||||
id: messageId,
|
id: messageId,
|
||||||
appId: activeAppId ?? "",
|
appKey: activeAppKey ?? "",
|
||||||
fromUserId: activeUserId ?? "",
|
fromUserId: activeUserId ?? "",
|
||||||
fromId: activeUserId,
|
fromId: activeUserId,
|
||||||
toId: toId,
|
toId: toId,
|
||||||
@ -87,8 +87,8 @@ public final class ImClient: NSObject, URLSessionWebSocketDelegate, @unchecked S
|
|||||||
createdAt: now,
|
createdAt: now,
|
||||||
editedAt: nil
|
editedAt: nil
|
||||||
)
|
)
|
||||||
guard let activeAppId else {
|
guard let activeAppKey else {
|
||||||
delegate?.imClientDidError("IM appId not configured")
|
delegate?.imClientDidError("IM appKey not configured")
|
||||||
return message.failedCopy()
|
return message.failedCopy()
|
||||||
}
|
}
|
||||||
let sent = sendFrame(
|
let sent = sendFrame(
|
||||||
@ -98,7 +98,7 @@ public final class ImClient: NSObject, URLSessionWebSocketDelegate, @unchecked S
|
|||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
],
|
],
|
||||||
body: encodeJSONString([
|
body: encodeJSONString([
|
||||||
"appId": activeAppId,
|
"appKey": activeAppKey,
|
||||||
"messageId": messageId,
|
"messageId": messageId,
|
||||||
"toId": toId,
|
"toId": toId,
|
||||||
"chatType": chatType.rawValue,
|
"chatType": chatType.rawValue,
|
||||||
@ -111,8 +111,8 @@ public final class ImClient: NSObject, URLSessionWebSocketDelegate, @unchecked S
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func revoke(messageId: String) {
|
public func revoke(messageId: String) {
|
||||||
guard let activeAppId else {
|
guard let activeAppKey else {
|
||||||
delegate?.imClientDidError("IM appId not configured")
|
delegate?.imClientDidError("IM appKey not configured")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sendFrame(
|
sendFrame(
|
||||||
@ -122,15 +122,15 @@ public final class ImClient: NSObject, URLSessionWebSocketDelegate, @unchecked S
|
|||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
],
|
],
|
||||||
body: encodeJSONString([
|
body: encodeJSONString([
|
||||||
"appId": activeAppId,
|
"appKey": activeAppKey,
|
||||||
"messageId": messageId,
|
"messageId": messageId,
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func sync() {
|
public func sync() {
|
||||||
guard let activeAppId else {
|
guard let activeAppKey else {
|
||||||
delegate?.imClientDidError("IM appId not configured")
|
delegate?.imClientDidError("IM appKey not configured")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sendFrame(
|
sendFrame(
|
||||||
@ -139,7 +139,7 @@ public final class ImClient: NSObject, URLSessionWebSocketDelegate, @unchecked S
|
|||||||
"destination": "/app/chat.sync",
|
"destination": "/app/chat.sync",
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
],
|
],
|
||||||
body: encodeJSONString(["appId": activeAppId]),
|
body: encodeJSONString(["appKey": activeAppKey]),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,7 +321,7 @@ private extension ImMessage {
|
|||||||
func failedCopy() -> ImMessage {
|
func failedCopy() -> ImMessage {
|
||||||
ImMessage(
|
ImMessage(
|
||||||
id: id,
|
id: id,
|
||||||
appId: appId,
|
appKey: appKey,
|
||||||
fromUserId: fromUserId,
|
fromUserId: fromUserId,
|
||||||
fromId: fromId,
|
fromId: fromId,
|
||||||
toId: toId,
|
toId: toId,
|
||||||
|
|||||||
@ -12,6 +12,7 @@ public final class ImSDK {
|
|||||||
private weak var delegate: ImEventDelegate?
|
private weak var delegate: ImEventDelegate?
|
||||||
private weak var conversationDelegate: ConversationDelegate?
|
private weak var conversationDelegate: ConversationDelegate?
|
||||||
private var currentUserId: String?
|
private var currentUserId: String?
|
||||||
|
private var currentUserSig: String?
|
||||||
private var _conversations: [ConversationData] = []
|
private var _conversations: [ConversationData] = []
|
||||||
|
|
||||||
public private(set) var connectionState: ImConnectionState = .disconnected
|
public private(set) var connectionState: ImConnectionState = .disconnected
|
||||||
@ -31,11 +32,15 @@ public final class ImSDK {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func login(_ userId: String, _ userSig: String) async throws {
|
public func login(_ userId: String, _ userSig: String) async throws {
|
||||||
|
if currentUserId == userId && currentUserSig == userSig {
|
||||||
|
return
|
||||||
|
}
|
||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
currentUserId = userId
|
currentUserId = userId
|
||||||
|
currentUserSig = userSig
|
||||||
XuqmSDK.shared.tokenStore?.save(userSig)
|
XuqmSDK.shared.tokenStore?.save(userSig)
|
||||||
client?.disconnect()
|
client?.disconnect()
|
||||||
client = ImClient(token: userSig, appId: config.appId)
|
client = ImClient(token: userSig, appKey: config.appKey)
|
||||||
client?.setCurrentUserId(userId)
|
client?.setCurrentUserId(userId)
|
||||||
client?.delegate = self
|
client?.delegate = self
|
||||||
updateConnectionState(.connecting)
|
updateConnectionState(.connecting)
|
||||||
@ -328,7 +333,7 @@ public final class ImSDK {
|
|||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/messages/\(messageId)/revoke",
|
path: "/api/im/messages/\(messageId)/revoke",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,7 +342,7 @@ public final class ImSDK {
|
|||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/messages/\(messageId)",
|
path: "/api/im/messages/\(messageId)",
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)],
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)],
|
||||||
body: EditMessageRequest(content: content)
|
body: EditMessageRequest(content: content)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -359,7 +364,7 @@ public final class ImSDK {
|
|||||||
) -> ImMessage {
|
) -> ImMessage {
|
||||||
ImMessage(
|
ImMessage(
|
||||||
id: UUID().uuidString,
|
id: UUID().uuidString,
|
||||||
appId: XuqmSDK.shared.requireConfig().appId,
|
appKey: XuqmSDK.shared.requireConfig().appKey,
|
||||||
fromUserId: currentUserId ?? "",
|
fromUserId: currentUserId ?? "",
|
||||||
fromId: currentUserId,
|
fromId: currentUserId,
|
||||||
toId: toId,
|
toId: toId,
|
||||||
@ -471,14 +476,14 @@ public final class ImSDK {
|
|||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
let groups: [ImGroup] = try await ApiClient.shared.request(
|
let groups: [ImGroup] = try await ApiClient.shared.request(
|
||||||
path: "/api/im/groups",
|
path: "/api/im/groups",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
)
|
)
|
||||||
return groups
|
return groups
|
||||||
}
|
}
|
||||||
|
|
||||||
public func listPublicGroups(keyword: String? = nil) async throws -> [ImGroup] {
|
public func listPublicGroups(keyword: String? = nil) async throws -> [ImGroup] {
|
||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
var items = [URLQueryItem(name: "appId", value: config.appId)]
|
var items = [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
if let keyword { items.append(URLQueryItem(name: "keyword", value: keyword)) }
|
if let keyword { items.append(URLQueryItem(name: "keyword", value: keyword)) }
|
||||||
let groups: [ImGroup] = try await ApiClient.shared.request(
|
let groups: [ImGroup] = try await ApiClient.shared.request(
|
||||||
path: "/api/im/groups/public",
|
path: "/api/im/groups/public",
|
||||||
@ -492,7 +497,7 @@ public final class ImSDK {
|
|||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/admin/users/search",
|
path: "/api/im/admin/users/search",
|
||||||
queryItems: [
|
queryItems: [
|
||||||
URLQueryItem(name: "appId", value: config.appId),
|
URLQueryItem(name: "appKey", value: config.appKey),
|
||||||
URLQueryItem(name: "keyword", value: keyword),
|
URLQueryItem(name: "keyword", value: keyword),
|
||||||
URLQueryItem(name: "size", value: String(size)),
|
URLQueryItem(name: "size", value: String(size)),
|
||||||
]
|
]
|
||||||
@ -504,7 +509,7 @@ public final class ImSDK {
|
|||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/admin/groups/search",
|
path: "/api/im/admin/groups/search",
|
||||||
queryItems: [
|
queryItems: [
|
||||||
URLQueryItem(name: "appId", value: config.appId),
|
URLQueryItem(name: "appKey", value: config.appKey),
|
||||||
URLQueryItem(name: "keyword", value: keyword),
|
URLQueryItem(name: "keyword", value: keyword),
|
||||||
URLQueryItem(name: "size", value: String(size)),
|
URLQueryItem(name: "size", value: String(size)),
|
||||||
]
|
]
|
||||||
@ -522,7 +527,7 @@ public final class ImSDK {
|
|||||||
) async throws -> PageResult<ImMessage> {
|
) async throws -> PageResult<ImMessage> {
|
||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
var items: [URLQueryItem] = [
|
var items: [URLQueryItem] = [
|
||||||
URLQueryItem(name: "appId", value: config.appId),
|
URLQueryItem(name: "appKey", value: config.appKey),
|
||||||
URLQueryItem(name: "page", value: String(page)),
|
URLQueryItem(name: "page", value: String(page)),
|
||||||
URLQueryItem(name: "size", value: String(size)),
|
URLQueryItem(name: "size", value: String(size)),
|
||||||
]
|
]
|
||||||
@ -542,7 +547,7 @@ public final class ImSDK {
|
|||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/groups",
|
path: "/api/im/groups",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)],
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)],
|
||||||
body: ImCreateGroupRequest(name: name, memberIds: memberIds, groupType: groupType)
|
body: ImCreateGroupRequest(name: name, memberIds: memberIds, groupType: groupType)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -555,7 +560,7 @@ public final class ImSDK {
|
|||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/groups/\(groupId)/members",
|
path: "/api/im/groups/\(groupId)/members",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -564,7 +569,7 @@ public final class ImSDK {
|
|||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/groups/\(groupId)/members/search",
|
path: "/api/im/groups/\(groupId)/members/search",
|
||||||
queryItems: [
|
queryItems: [
|
||||||
URLQueryItem(name: "appId", value: config.appId),
|
URLQueryItem(name: "appKey", value: config.appKey),
|
||||||
URLQueryItem(name: "keyword", value: keyword),
|
URLQueryItem(name: "keyword", value: keyword),
|
||||||
URLQueryItem(name: "size", value: String(size))
|
URLQueryItem(name: "size", value: String(size))
|
||||||
]
|
]
|
||||||
@ -650,7 +655,7 @@ public final class ImSDK {
|
|||||||
|
|
||||||
public func sendGroupJoinRequest(groupId: String, remark: String? = nil) async throws -> GroupJoinRequest {
|
public func sendGroupJoinRequest(groupId: String, remark: String? = nil) async throws -> GroupJoinRequest {
|
||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
var items = [URLQueryItem(name: "appId", value: config.appId)]
|
var items = [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
if let remark { items.append(URLQueryItem(name: "remark", value: remark)) }
|
if let remark { items.append(URLQueryItem(name: "remark", value: remark)) }
|
||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/groups/\(groupId)/join-requests",
|
path: "/api/im/groups/\(groupId)/join-requests",
|
||||||
@ -663,7 +668,7 @@ public final class ImSDK {
|
|||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/groups/\(groupId)/join-requests",
|
path: "/api/im/groups/\(groupId)/join-requests",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -672,7 +677,7 @@ public final class ImSDK {
|
|||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/groups/\(groupId)/join-requests/\(requestId)/accept",
|
path: "/api/im/groups/\(groupId)/join-requests/\(requestId)/accept",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -681,7 +686,7 @@ public final class ImSDK {
|
|||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/groups/\(groupId)/join-requests/\(requestId)/reject",
|
path: "/api/im/groups/\(groupId)/join-requests/\(requestId)/reject",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -689,7 +694,7 @@ public final class ImSDK {
|
|||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/friends",
|
path: "/api/im/friends",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -699,7 +704,7 @@ public final class ImSDK {
|
|||||||
path: "/api/im/friends",
|
path: "/api/im/friends",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
queryItems: [
|
queryItems: [
|
||||||
URLQueryItem(name: "appId", value: config.appId),
|
URLQueryItem(name: "appKey", value: config.appKey),
|
||||||
URLQueryItem(name: "friendId", value: friendId),
|
URLQueryItem(name: "friendId", value: friendId),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -710,7 +715,7 @@ public final class ImSDK {
|
|||||||
let _: EmptyResponse = try await ApiClient.shared.request(
|
let _: EmptyResponse = try await ApiClient.shared.request(
|
||||||
path: "/api/im/friends",
|
path: "/api/im/friends",
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -719,13 +724,13 @@ public final class ImSDK {
|
|||||||
let _: EmptyResponse = try await ApiClient.shared.request(
|
let _: EmptyResponse = try await ApiClient.shared.request(
|
||||||
path: "/api/im/friends/\(friendId)",
|
path: "/api/im/friends/\(friendId)",
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func setFriendGroup(friendId: String, groupName: String? = nil) async throws {
|
public func setFriendGroup(friendId: String, groupName: String? = nil) async throws {
|
||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
var items = [URLQueryItem(name: "appId", value: config.appId)]
|
var items = [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
if let groupName {
|
if let groupName {
|
||||||
items.append(URLQueryItem(name: "groupName", value: groupName))
|
items.append(URLQueryItem(name: "groupName", value: groupName))
|
||||||
}
|
}
|
||||||
@ -740,7 +745,7 @@ public final class ImSDK {
|
|||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/friends/groups",
|
path: "/api/im/friends/groups",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -748,7 +753,7 @@ public final class ImSDK {
|
|||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/friends/groups/\(groupName)",
|
path: "/api/im/friends/groups/\(groupName)",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -757,7 +762,7 @@ public final class ImSDK {
|
|||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/friend-requests",
|
path: "/api/im/friend-requests",
|
||||||
queryItems: [
|
queryItems: [
|
||||||
URLQueryItem(name: "appId", value: config.appId),
|
URLQueryItem(name: "appKey", value: config.appKey),
|
||||||
URLQueryItem(name: "direction", value: direction),
|
URLQueryItem(name: "direction", value: direction),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -766,7 +771,7 @@ public final class ImSDK {
|
|||||||
public func sendFriendRequest(toUserId: String, remark: String? = nil) async throws -> FriendRequest {
|
public func sendFriendRequest(toUserId: String, remark: String? = nil) async throws -> FriendRequest {
|
||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
var items = [
|
var items = [
|
||||||
URLQueryItem(name: "appId", value: config.appId),
|
URLQueryItem(name: "appKey", value: config.appKey),
|
||||||
URLQueryItem(name: "toUserId", value: toUserId),
|
URLQueryItem(name: "toUserId", value: toUserId),
|
||||||
]
|
]
|
||||||
if let remark { items.append(URLQueryItem(name: "remark", value: remark)) }
|
if let remark { items.append(URLQueryItem(name: "remark", value: remark)) }
|
||||||
@ -782,7 +787,7 @@ public final class ImSDK {
|
|||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/friend-requests/\(requestId)/accept",
|
path: "/api/im/friend-requests/\(requestId)/accept",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -791,7 +796,7 @@ public final class ImSDK {
|
|||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/friend-requests/\(requestId)/reject",
|
path: "/api/im/friend-requests/\(requestId)/reject",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -799,7 +804,7 @@ public final class ImSDK {
|
|||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/blacklist",
|
path: "/api/im/blacklist",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -809,7 +814,7 @@ public final class ImSDK {
|
|||||||
path: "/api/im/blacklist",
|
path: "/api/im/blacklist",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
queryItems: [
|
queryItems: [
|
||||||
URLQueryItem(name: "appId", value: config.appId),
|
URLQueryItem(name: "appKey", value: config.appKey),
|
||||||
URLQueryItem(name: "blockedUserId", value: blockedUserId),
|
URLQueryItem(name: "blockedUserId", value: blockedUserId),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -821,7 +826,7 @@ public final class ImSDK {
|
|||||||
path: "/api/im/blacklist",
|
path: "/api/im/blacklist",
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
queryItems: [
|
queryItems: [
|
||||||
URLQueryItem(name: "appId", value: config.appId),
|
URLQueryItem(name: "appKey", value: config.appKey),
|
||||||
URLQueryItem(name: "blockedUserId", value: blockedUserId),
|
URLQueryItem(name: "blockedUserId", value: blockedUserId),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -832,7 +837,7 @@ public final class ImSDK {
|
|||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/blacklist/check",
|
path: "/api/im/blacklist/check",
|
||||||
queryItems: [
|
queryItems: [
|
||||||
URLQueryItem(name: "appId", value: config.appId),
|
URLQueryItem(name: "appKey", value: config.appKey),
|
||||||
URLQueryItem(name: "targetUserId", value: targetUserId),
|
URLQueryItem(name: "targetUserId", value: targetUserId),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -842,7 +847,7 @@ public final class ImSDK {
|
|||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/accounts/\(userId)",
|
path: "/api/im/accounts/\(userId)",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -853,7 +858,7 @@ public final class ImSDK {
|
|||||||
gender: String? = nil
|
gender: String? = nil
|
||||||
) async throws -> UserProfile {
|
) async throws -> UserProfile {
|
||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
var items = [URLQueryItem(name: "appId", value: config.appId)]
|
var items = [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
if let nickname { items.append(URLQueryItem(name: "nickname", value: nickname)) }
|
if let nickname { items.append(URLQueryItem(name: "nickname", value: nickname)) }
|
||||||
if let avatar { items.append(URLQueryItem(name: "avatar", value: avatar)) }
|
if let avatar { items.append(URLQueryItem(name: "avatar", value: avatar)) }
|
||||||
if let gender { items.append(URLQueryItem(name: "gender", value: gender)) }
|
if let gender { items.append(URLQueryItem(name: "gender", value: gender)) }
|
||||||
@ -868,7 +873,7 @@ public final class ImSDK {
|
|||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
let result: [ConversationData] = try await ApiClient.shared.request(
|
let result: [ConversationData] = try await ApiClient.shared.request(
|
||||||
path: "/api/im/conversations",
|
path: "/api/im/conversations",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
)
|
)
|
||||||
_conversations = result
|
_conversations = result
|
||||||
conversationDelegate?.conversationsDidChange(_conversations)
|
conversationDelegate?.conversationsDidChange(_conversations)
|
||||||
@ -881,7 +886,7 @@ public final class ImSDK {
|
|||||||
path: "/api/im/conversations/\(targetId)/read",
|
path: "/api/im/conversations/\(targetId)/read",
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
queryItems: [
|
queryItems: [
|
||||||
URLQueryItem(name: "appId", value: config.appId),
|
URLQueryItem(name: "appKey", value: config.appKey),
|
||||||
URLQueryItem(name: "chatType", value: chatType.rawValue),
|
URLQueryItem(name: "chatType", value: chatType.rawValue),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -893,7 +898,7 @@ public final class ImSDK {
|
|||||||
path: "/api/im/conversations/\(targetId)/pinned",
|
path: "/api/im/conversations/\(targetId)/pinned",
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
queryItems: [
|
queryItems: [
|
||||||
URLQueryItem(name: "appId", value: config.appId),
|
URLQueryItem(name: "appKey", value: config.appKey),
|
||||||
URLQueryItem(name: "chatType", value: chatType.rawValue),
|
URLQueryItem(name: "chatType", value: chatType.rawValue),
|
||||||
URLQueryItem(name: "pinned", value: String(pinned)),
|
URLQueryItem(name: "pinned", value: String(pinned)),
|
||||||
]
|
]
|
||||||
@ -906,7 +911,7 @@ public final class ImSDK {
|
|||||||
path: "/api/im/conversations/\(targetId)/muted",
|
path: "/api/im/conversations/\(targetId)/muted",
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
queryItems: [
|
queryItems: [
|
||||||
URLQueryItem(name: "appId", value: config.appId),
|
URLQueryItem(name: "appKey", value: config.appKey),
|
||||||
URLQueryItem(name: "chatType", value: chatType.rawValue),
|
URLQueryItem(name: "chatType", value: chatType.rawValue),
|
||||||
URLQueryItem(name: "muted", value: String(muted)),
|
URLQueryItem(name: "muted", value: String(muted)),
|
||||||
]
|
]
|
||||||
@ -919,7 +924,7 @@ public final class ImSDK {
|
|||||||
path: "/api/im/conversations/\(targetId)/hidden",
|
path: "/api/im/conversations/\(targetId)/hidden",
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
queryItems: [
|
queryItems: [
|
||||||
URLQueryItem(name: "appId", value: config.appId),
|
URLQueryItem(name: "appKey", value: config.appKey),
|
||||||
URLQueryItem(name: "chatType", value: chatType.rawValue),
|
URLQueryItem(name: "chatType", value: chatType.rawValue),
|
||||||
URLQueryItem(name: "hidden", value: String(hidden)),
|
URLQueryItem(name: "hidden", value: String(hidden)),
|
||||||
]
|
]
|
||||||
@ -929,7 +934,7 @@ public final class ImSDK {
|
|||||||
public func setConversationGroup(targetId: String, chatType: ChatType, groupName: String? = nil) async throws {
|
public func setConversationGroup(targetId: String, chatType: ChatType, groupName: String? = nil) async throws {
|
||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
var items = [
|
var items = [
|
||||||
URLQueryItem(name: "appId", value: config.appId),
|
URLQueryItem(name: "appKey", value: config.appKey),
|
||||||
URLQueryItem(name: "chatType", value: chatType.rawValue),
|
URLQueryItem(name: "chatType", value: chatType.rawValue),
|
||||||
]
|
]
|
||||||
if let groupName {
|
if let groupName {
|
||||||
@ -946,7 +951,7 @@ public final class ImSDK {
|
|||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/conversation-groups",
|
path: "/api/im/conversation-groups",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -954,7 +959,7 @@ public final class ImSDK {
|
|||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/conversation-groups/\(groupName)",
|
path: "/api/im/conversation-groups/\(groupName)",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -964,7 +969,7 @@ public final class ImSDK {
|
|||||||
path: "/api/im/conversations/\(targetId)/draft",
|
path: "/api/im/conversations/\(targetId)/draft",
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
queryItems: [
|
queryItems: [
|
||||||
URLQueryItem(name: "appId", value: config.appId),
|
URLQueryItem(name: "appKey", value: config.appKey),
|
||||||
URLQueryItem(name: "chatType", value: chatType.rawValue),
|
URLQueryItem(name: "chatType", value: chatType.rawValue),
|
||||||
URLQueryItem(name: "draft", value: draft),
|
URLQueryItem(name: "draft", value: draft),
|
||||||
]
|
]
|
||||||
@ -977,7 +982,7 @@ public final class ImSDK {
|
|||||||
path: "/api/im/conversations/\(targetId)",
|
path: "/api/im/conversations/\(targetId)",
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
queryItems: [
|
queryItems: [
|
||||||
URLQueryItem(name: "appId", value: config.appId),
|
URLQueryItem(name: "appKey", value: config.appKey),
|
||||||
URLQueryItem(name: "chatType", value: chatType.rawValue),
|
URLQueryItem(name: "chatType", value: chatType.rawValue),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -992,7 +997,7 @@ public final class ImSDK {
|
|||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
let result: [String: Int] = try await ApiClient.shared.request(
|
let result: [String: Int] = try await ApiClient.shared.request(
|
||||||
path: "/api/im/messages/offline/count",
|
path: "/api/im/messages/offline/count",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
)
|
)
|
||||||
return result["count"] ?? 0
|
return result["count"] ?? 0
|
||||||
}
|
}
|
||||||
@ -1002,7 +1007,7 @@ public final class ImSDK {
|
|||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/messages/offline",
|
path: "/api/im/messages/offline",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)]
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1011,7 +1016,7 @@ public final class ImSDK {
|
|||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/im/admin/groups/\(groupId)/read-receipts",
|
path: "/api/im/admin/groups/\(groupId)/read-receipts",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)],
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)],
|
||||||
body: ImGroupReadReceiptRequest(messageIds: messageIds)
|
body: ImGroupReadReceiptRequest(messageIds: messageIds)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1021,7 +1026,7 @@ public final class ImSDK {
|
|||||||
let _: EmptyResponse = try await ApiClient.shared.request(
|
let _: EmptyResponse = try await ApiClient.shared.request(
|
||||||
path: "/api/im/friends/batch",
|
path: "/api/im/friends/batch",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)],
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)],
|
||||||
body: BatchFriendIdsRequest(friendIds: friendIds)
|
body: BatchFriendIdsRequest(friendIds: friendIds)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1031,7 +1036,7 @@ public final class ImSDK {
|
|||||||
let _: EmptyResponse = try await ApiClient.shared.request(
|
let _: EmptyResponse = try await ApiClient.shared.request(
|
||||||
path: "/api/im/friends/batch/remove",
|
path: "/api/im/friends/batch/remove",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)],
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)],
|
||||||
body: BatchFriendIdsRequest(friendIds: friendIds)
|
body: BatchFriendIdsRequest(friendIds: friendIds)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1041,7 +1046,7 @@ public final class ImSDK {
|
|||||||
let _: EmptyResponse = try await ApiClient.shared.request(
|
let _: EmptyResponse = try await ApiClient.shared.request(
|
||||||
path: "/api/im/friend-requests/batch/accept",
|
path: "/api/im/friend-requests/batch/accept",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)],
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)],
|
||||||
body: BatchRequestIdsRequest(requestIds: requestIds)
|
body: BatchRequestIdsRequest(requestIds: requestIds)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1051,7 +1056,7 @@ public final class ImSDK {
|
|||||||
let _: EmptyResponse = try await ApiClient.shared.request(
|
let _: EmptyResponse = try await ApiClient.shared.request(
|
||||||
path: "/api/im/friend-requests/batch/reject",
|
path: "/api/im/friend-requests/batch/reject",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)],
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)],
|
||||||
body: BatchRequestIdsRequest(requestIds: requestIds)
|
body: BatchRequestIdsRequest(requestIds: requestIds)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1061,7 +1066,7 @@ public final class ImSDK {
|
|||||||
let _: EmptyResponse = try await ApiClient.shared.request(
|
let _: EmptyResponse = try await ApiClient.shared.request(
|
||||||
path: "/api/im/groups/\(groupId)/members/batch",
|
path: "/api/im/groups/\(groupId)/members/batch",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)],
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)],
|
||||||
body: BatchUserIdsRequest(userIds: userIds)
|
body: BatchUserIdsRequest(userIds: userIds)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1071,7 +1076,7 @@ public final class ImSDK {
|
|||||||
let _: EmptyResponse = try await ApiClient.shared.request(
|
let _: EmptyResponse = try await ApiClient.shared.request(
|
||||||
path: "/api/im/groups/\(groupId)/members/batch/remove",
|
path: "/api/im/groups/\(groupId)/members/batch/remove",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)],
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)],
|
||||||
body: BatchUserIdsRequest(userIds: userIds)
|
body: BatchUserIdsRequest(userIds: userIds)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1081,7 +1086,7 @@ public final class ImSDK {
|
|||||||
let _: EmptyResponse = try await ApiClient.shared.request(
|
let _: EmptyResponse = try await ApiClient.shared.request(
|
||||||
path: "/api/im/groups/\(groupId)/join-requests/batch/accept",
|
path: "/api/im/groups/\(groupId)/join-requests/batch/accept",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)],
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)],
|
||||||
body: BatchRequestIdsRequest(requestIds: requestIds)
|
body: BatchRequestIdsRequest(requestIds: requestIds)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1091,7 +1096,7 @@ public final class ImSDK {
|
|||||||
let _: EmptyResponse = try await ApiClient.shared.request(
|
let _: EmptyResponse = try await ApiClient.shared.request(
|
||||||
path: "/api/im/groups/\(groupId)/join-requests/batch/reject",
|
path: "/api/im/groups/\(groupId)/join-requests/batch/reject",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)],
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)],
|
||||||
body: BatchRequestIdsRequest(requestIds: requestIds)
|
body: BatchRequestIdsRequest(requestIds: requestIds)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1101,7 +1106,7 @@ public final class ImSDK {
|
|||||||
let _: EmptyResponse = try await ApiClient.shared.request(
|
let _: EmptyResponse = try await ApiClient.shared.request(
|
||||||
path: "/api/im/groups/\(groupId)/members/\(userId)/info",
|
path: "/api/im/groups/\(groupId)/members/\(userId)/info",
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)],
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)],
|
||||||
body: ModifyMemberInfoRequest(nickname: nickname, role: role)
|
body: ModifyMemberInfoRequest(nickname: nickname, role: role)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1110,6 +1115,8 @@ public final class ImSDK {
|
|||||||
client?.disconnect()
|
client?.disconnect()
|
||||||
client = nil
|
client = nil
|
||||||
updateConnectionState(.disconnected)
|
updateConnectionState(.disconnected)
|
||||||
|
currentUserId = nil
|
||||||
|
currentUserSig = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
private func fetchHistoryInternal(
|
private func fetchHistoryInternal(
|
||||||
@ -1125,7 +1132,7 @@ public final class ImSDK {
|
|||||||
) async throws -> [ImMessage] {
|
) async throws -> [ImMessage] {
|
||||||
let config = XuqmSDK.shared.requireConfig()
|
let config = XuqmSDK.shared.requireConfig()
|
||||||
var items = [
|
var items = [
|
||||||
URLQueryItem(name: "appId", value: config.appId),
|
URLQueryItem(name: "appKey", value: config.appKey),
|
||||||
URLQueryItem(name: "page", value: String(page)),
|
URLQueryItem(name: "page", value: String(page)),
|
||||||
URLQueryItem(name: "size", value: String(size)),
|
URLQueryItem(name: "size", value: String(size)),
|
||||||
]
|
]
|
||||||
|
|||||||
@ -40,7 +40,7 @@ public enum MsgStatus: String, Codable, Sendable {
|
|||||||
|
|
||||||
public struct ImMessage: Codable, Sendable {
|
public struct ImMessage: Codable, Sendable {
|
||||||
public let id: String
|
public let id: String
|
||||||
public let appId: String
|
public let appKey: String
|
||||||
public let fromUserId: String
|
public let fromUserId: String
|
||||||
public let fromId: String?
|
public let fromId: String?
|
||||||
public let toId: String
|
public let toId: String
|
||||||
@ -84,7 +84,7 @@ public extension ImEventDelegate {
|
|||||||
|
|
||||||
public struct ImGroup: Codable, Sendable {
|
public struct ImGroup: Codable, Sendable {
|
||||||
public let id: String
|
public let id: String
|
||||||
public let appId: String
|
public let appKey: String
|
||||||
public let name: String
|
public let name: String
|
||||||
public let groupType: String?
|
public let groupType: String?
|
||||||
public let creatorId: String
|
public let creatorId: String
|
||||||
@ -116,7 +116,7 @@ public struct ConversationGroupItem: Codable, Sendable {
|
|||||||
|
|
||||||
public struct FriendRequest: Codable, Sendable {
|
public struct FriendRequest: Codable, Sendable {
|
||||||
public let id: String
|
public let id: String
|
||||||
public let appId: String
|
public let appKey: String
|
||||||
public let fromUserId: String
|
public let fromUserId: String
|
||||||
public let toUserId: String
|
public let toUserId: String
|
||||||
public let remark: String?
|
public let remark: String?
|
||||||
@ -127,7 +127,7 @@ public struct FriendRequest: Codable, Sendable {
|
|||||||
|
|
||||||
public struct GroupJoinRequest: Codable, Sendable {
|
public struct GroupJoinRequest: Codable, Sendable {
|
||||||
public let id: String
|
public let id: String
|
||||||
public let appId: String
|
public let appKey: String
|
||||||
public let groupId: String
|
public let groupId: String
|
||||||
public let requesterId: String
|
public let requesterId: String
|
||||||
public let remark: String?
|
public let remark: String?
|
||||||
@ -138,7 +138,7 @@ public struct GroupJoinRequest: Codable, Sendable {
|
|||||||
|
|
||||||
public struct BlacklistEntry: Codable, Sendable {
|
public struct BlacklistEntry: Codable, Sendable {
|
||||||
public let id: String
|
public let id: String
|
||||||
public let appId: String
|
public let appKey: String
|
||||||
public let userId: String
|
public let userId: String
|
||||||
public let blockedUserId: String
|
public let blockedUserId: String
|
||||||
public let createdAt: Int64
|
public let createdAt: Int64
|
||||||
@ -161,7 +161,7 @@ public struct GroupReadReceiptSummary: Codable, Sendable {
|
|||||||
|
|
||||||
public struct UserProfile: Codable, Sendable {
|
public struct UserProfile: Codable, Sendable {
|
||||||
public let id: String?
|
public let id: String?
|
||||||
public let appId: String?
|
public let appKey: String?
|
||||||
public let userId: String
|
public let userId: String
|
||||||
public let nickname: String?
|
public let nickname: String?
|
||||||
public let avatar: String?
|
public let avatar: String?
|
||||||
|
|||||||
@ -71,7 +71,7 @@ public final class PushSDK: NSObject, UNUserNotificationCenterDelegate {
|
|||||||
path: "/api/push/register",
|
path: "/api/push/register",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
queryItems: [
|
queryItems: [
|
||||||
URLQueryItem(name: "appId", value: config.appId),
|
URLQueryItem(name: "appKey", value: config.appKey),
|
||||||
URLQueryItem(name: "userId", value: userId),
|
URLQueryItem(name: "userId", value: userId),
|
||||||
URLQueryItem(name: "vendor", value: vendor.rawValue),
|
URLQueryItem(name: "vendor", value: vendor.rawValue),
|
||||||
URLQueryItem(name: "token", value: token),
|
URLQueryItem(name: "token", value: token),
|
||||||
@ -110,7 +110,7 @@ public final class PushSDK: NSObject, UNUserNotificationCenterDelegate {
|
|||||||
path: "/api/push/unregister",
|
path: "/api/push/unregister",
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
queryItems: [
|
queryItems: [
|
||||||
URLQueryItem(name: "appId", value: config.appId),
|
URLQueryItem(name: "appKey", value: config.appKey),
|
||||||
URLQueryItem(name: "userId", value: userId),
|
URLQueryItem(name: "userId", value: userId),
|
||||||
URLQueryItem(name: "vendor", value: vendor.rawValue),
|
URLQueryItem(name: "vendor", value: vendor.rawValue),
|
||||||
URLQueryItem(name: "deviceId", value: deviceId),
|
URLQueryItem(name: "deviceId", value: deviceId),
|
||||||
@ -166,7 +166,7 @@ public final class PushSDK: NSObject, UNUserNotificationCenterDelegate {
|
|||||||
let response: SdkRuntimeConfigResponse = try await ApiClient.shared.request(
|
let response: SdkRuntimeConfigResponse = try await ApiClient.shared.request(
|
||||||
path: "/api/sdk/config",
|
path: "/api/sdk/config",
|
||||||
queryItems: [
|
queryItems: [
|
||||||
URLQueryItem(name: "appId", value: config.appId),
|
URLQueryItem(name: "appKey", value: config.appKey),
|
||||||
URLQueryItem(name: "platform", value: "IOS"),
|
URLQueryItem(name: "platform", value: "IOS"),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@ -23,7 +23,7 @@ public final class UpdateSDK {
|
|||||||
return try await ApiClient.shared.request(
|
return try await ApiClient.shared.request(
|
||||||
path: "/api/v1/updates/app/check",
|
path: "/api/v1/updates/app/check",
|
||||||
queryItems: [
|
queryItems: [
|
||||||
URLQueryItem(name: "appId", value: config.appId),
|
URLQueryItem(name: "appKey", value: config.appKey),
|
||||||
URLQueryItem(name: "platform", value: "IOS"),
|
URLQueryItem(name: "platform", value: "IOS"),
|
||||||
URLQueryItem(name: "currentVersionCode", value: "\(currentVersionCode)"),
|
URLQueryItem(name: "currentVersionCode", value: "\(currentVersionCode)"),
|
||||||
]
|
]
|
||||||
|
|||||||
@ -0,0 +1,32 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
public final class XWebViewBridge {
|
||||||
|
public static let shared = XWebViewBridge()
|
||||||
|
|
||||||
|
private var config = XWebViewConfig()
|
||||||
|
private weak var controller: AnyObject?
|
||||||
|
|
||||||
|
private init() {}
|
||||||
|
|
||||||
|
public func open(_ config: XWebViewConfig) {
|
||||||
|
self.config = config
|
||||||
|
}
|
||||||
|
|
||||||
|
public func currentConfig() -> XWebViewConfig {
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
public func setController(_ controller: (any XWebViewController)?) {
|
||||||
|
self.controller = controller
|
||||||
|
}
|
||||||
|
|
||||||
|
public func currentController() -> (any XWebViewController)? {
|
||||||
|
controller as? any XWebViewController
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
public func openXWebView(_ config: XWebViewConfig) {
|
||||||
|
XWebViewBridge.shared.open(config)
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
public struct XWebViewPage: View {
|
||||||
|
private let config: XWebViewConfig
|
||||||
|
|
||||||
|
public init(config: XWebViewConfig? = nil) {
|
||||||
|
self.config = config ?? XWebViewBridge.shared.currentConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
public var body: some View {
|
||||||
|
NavigationStack {
|
||||||
|
Group {
|
||||||
|
#if canImport(UIKit)
|
||||||
|
XWebViewView(config: config)
|
||||||
|
.navigationTitle(config.title.isEmpty ? "WebView" : config.title)
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
.toolbar {
|
||||||
|
if !config.hideToolbar {
|
||||||
|
ToolbarItem(placement: .topBarLeading) {
|
||||||
|
Button {
|
||||||
|
if let controller = XWebViewBridge.shared.currentController(), controller.canGoBack() {
|
||||||
|
controller.goBack()
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "chevron.left")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
XWebViewView(config: config)
|
||||||
|
.navigationTitle(config.title.isEmpty ? "WebView" : config.title)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public struct XWebViewConfig: Sendable {
|
||||||
|
public var url: String
|
||||||
|
public var title: String
|
||||||
|
public var hideToolbar: Bool
|
||||||
|
public var hideStatusBar: Bool
|
||||||
|
public var userAgent: String?
|
||||||
|
|
||||||
|
public init(
|
||||||
|
url: String = "",
|
||||||
|
title: String = "",
|
||||||
|
hideToolbar: Bool = false,
|
||||||
|
hideStatusBar: Bool = false,
|
||||||
|
userAgent: String? = nil
|
||||||
|
) {
|
||||||
|
self.url = url
|
||||||
|
self.title = title
|
||||||
|
self.hideToolbar = hideToolbar
|
||||||
|
self.hideStatusBar = hideStatusBar
|
||||||
|
self.userAgent = userAgent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public protocol XWebViewController: AnyObject {
|
||||||
|
func canGoBack() -> Bool
|
||||||
|
func canGoForward() -> Bool
|
||||||
|
func currentUrl() -> String?
|
||||||
|
func goBack()
|
||||||
|
func goForward()
|
||||||
|
func reload()
|
||||||
|
func load(url: String)
|
||||||
|
}
|
||||||
@ -0,0 +1,111 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
#if canImport(UIKit)
|
||||||
|
import WebKit
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
public struct XWebViewView: UIViewRepresentable {
|
||||||
|
private let config: XWebViewConfig
|
||||||
|
|
||||||
|
public init(config: XWebViewConfig? = nil) {
|
||||||
|
self.config = config ?? XWebViewBridge.shared.currentConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func makeCoordinator() -> Coordinator {
|
||||||
|
Coordinator()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func makeUIView(context: Context) -> WKWebView {
|
||||||
|
let webView = WKWebView(frame: .zero)
|
||||||
|
webView.navigationDelegate = context.coordinator
|
||||||
|
webView.uiDelegate = context.coordinator
|
||||||
|
if let userAgent = config.userAgent {
|
||||||
|
webView.customUserAgent = userAgent
|
||||||
|
}
|
||||||
|
context.coordinator.attach(webView)
|
||||||
|
XWebViewBridge.shared.setController(context.coordinator)
|
||||||
|
if !config.url.isEmpty, let url = URL(string: config.url) {
|
||||||
|
webView.load(URLRequest(url: url))
|
||||||
|
}
|
||||||
|
return webView
|
||||||
|
}
|
||||||
|
|
||||||
|
public func updateUIView(_ webView: WKWebView, context: Context) {
|
||||||
|
context.coordinator.attach(webView)
|
||||||
|
if webView.url == nil, !config.url.isEmpty, let url = URL(string: config.url) {
|
||||||
|
webView.load(URLRequest(url: url))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func dismantleUIView(_ uiView: WKWebView, coordinator: Coordinator) {
|
||||||
|
coordinator.detach()
|
||||||
|
if XWebViewBridge.shared.currentController() === coordinator {
|
||||||
|
XWebViewBridge.shared.setController(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
public final class Coordinator: NSObject, WKNavigationDelegate, WKUIDelegate, XWebViewController {
|
||||||
|
private weak var webView: WKWebView?
|
||||||
|
|
||||||
|
func attach(_ webView: WKWebView) {
|
||||||
|
self.webView = webView
|
||||||
|
}
|
||||||
|
|
||||||
|
func detach() {
|
||||||
|
webView = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
public func canGoBack() -> Bool {
|
||||||
|
webView?.canGoBack == true
|
||||||
|
}
|
||||||
|
|
||||||
|
public func canGoForward() -> Bool {
|
||||||
|
webView?.canGoForward == true
|
||||||
|
}
|
||||||
|
|
||||||
|
public func currentUrl() -> String? {
|
||||||
|
webView?.url?.absoluteString
|
||||||
|
}
|
||||||
|
|
||||||
|
public func goBack() {
|
||||||
|
webView?.goBack()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func goForward() {
|
||||||
|
webView?.goForward()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func reload() {
|
||||||
|
webView?.reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func load(url: String) {
|
||||||
|
guard let url = URL(string: url) else { return }
|
||||||
|
webView?.load(URLRequest(url: url))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
public struct XWebViewView: View {
|
||||||
|
private let config: XWebViewConfig
|
||||||
|
|
||||||
|
public init(config: XWebViewConfig? = nil) {
|
||||||
|
self.config = config ?? XWebViewBridge.shared.currentConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
public var body: some View {
|
||||||
|
VStack(spacing: 12) {
|
||||||
|
Text(config.title.isEmpty ? "WebView" : config.title)
|
||||||
|
.font(.headline)
|
||||||
|
Text("XWebView is available on iOS with UIKit-backed WebView.")
|
||||||
|
.font(.subheadline)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -8,7 +8,7 @@ final class SmokeTests: XCTestCase {
|
|||||||
XCTAssertEqual(config.appKey, "ak_test")
|
XCTAssertEqual(config.appKey, "ak_test")
|
||||||
XCTAssertTrue(config.debug)
|
XCTAssertTrue(config.debug)
|
||||||
XCTAssertFalse(config.autoRegisterPush)
|
XCTAssertFalse(config.autoRegisterPush)
|
||||||
XCTAssertEqual(config.appId, "ak_test")
|
XCTAssertEqual(config.appKey, "ak_test")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTokenStoreSaveGetClear() {
|
func testTokenStoreSaveGetClear() {
|
||||||
@ -22,7 +22,7 @@ final class SmokeTests: XCTestCase {
|
|||||||
func testImMessageCoding() throws {
|
func testImMessageCoding() throws {
|
||||||
let msg = ImMessage(
|
let msg = ImMessage(
|
||||||
id: "msg_1",
|
id: "msg_1",
|
||||||
appId: "ak_test",
|
appKey: "ak_test",
|
||||||
fromUserId: "user_a",
|
fromUserId: "user_a",
|
||||||
fromId: "user_a",
|
fromId: "user_a",
|
||||||
toId: "user_b",
|
toId: "user_b",
|
||||||
|
|||||||
@ -15,6 +15,7 @@ let package = Package(
|
|||||||
name: "XuqmDemo",
|
name: "XuqmDemo",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.product(name: "XuqmSDK", package: "XuqmGroup-iOSSDK"),
|
.product(name: "XuqmSDK", package: "XuqmGroup-iOSSDK"),
|
||||||
|
.product(name: "XuqmWebViewSDK", package: "XuqmGroup-iOSSDK"),
|
||||||
],
|
],
|
||||||
path: "Sources"
|
path: "Sources"
|
||||||
),
|
),
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import XuqmSDK
|
|||||||
|
|
||||||
enum AppRoute: Hashable {
|
enum AppRoute: Hashable {
|
||||||
case chat(targetId: String, targetName: String)
|
case chat(targetId: String, targetName: String)
|
||||||
|
case webView
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DemoUser: Identifiable {
|
struct DemoUser: Identifiable {
|
||||||
|
|||||||
@ -27,8 +27,8 @@ final class AuthViewModel: ObservableObject {
|
|||||||
let res: DemoLoginResponse = try await ApiClient.shared.request(
|
let res: DemoLoginResponse = try await ApiClient.shared.request(
|
||||||
path: "/api/demo/auth/login",
|
path: "/api/demo/auth/login",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
queryItems: [URLQueryItem(name: "appId", value: config.appId)],
|
queryItems: [URLQueryItem(name: "appKey", value: config.appKey)],
|
||||||
body: ["appId": config.appId, "userId": userId, "password": password]
|
body: ["appKey": config.appKey, "userId": userId, "password": password]
|
||||||
)
|
)
|
||||||
await XuqmSDK.shared.login(userId: res.profile.userId, userSig: res.imToken)
|
await XuqmSDK.shared.login(userId: res.profile.userId, userSig: res.imToken)
|
||||||
currentUserId = res.profile.userId
|
currentUserId = res.profile.userId
|
||||||
|
|||||||
@ -0,0 +1,159 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import XuqmWebViewSDK
|
||||||
|
#if canImport(UIKit)
|
||||||
|
import UIKit
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct WebViewDemoView: View {
|
||||||
|
@Binding var path: NavigationPath
|
||||||
|
private enum WebViewMode: String, CaseIterable, Identifiable {
|
||||||
|
case embedded = "嵌入式"
|
||||||
|
case page = "页面模式"
|
||||||
|
|
||||||
|
var id: String { rawValue }
|
||||||
|
}
|
||||||
|
|
||||||
|
@State private var url = "https://example.com"
|
||||||
|
@State private var title = "示例网页"
|
||||||
|
@State private var mode: WebViewMode = .embedded
|
||||||
|
@State private var statusVersion = 0
|
||||||
|
|
||||||
|
private var config: XWebViewConfig {
|
||||||
|
XWebViewConfig(
|
||||||
|
url: url.trimmingCharacters(in: .whitespacesAndNewlines),
|
||||||
|
title: title.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var currentUrl: String {
|
||||||
|
let controllerUrl = XWebViewBridge.shared.currentController()?.currentUrl()
|
||||||
|
return (controllerUrl?.isEmpty == false ? controllerUrl : nil) ?? config.url
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ScrollView {
|
||||||
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
Text("XWebView")
|
||||||
|
.font(.title2)
|
||||||
|
.fontWeight(.bold)
|
||||||
|
|
||||||
|
Text("可输入 URL,并在嵌入式组件和独立页面之间切换。")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
|
TextField("URL", text: $url)
|
||||||
|
|
||||||
|
TextField("标题", text: $title)
|
||||||
|
|
||||||
|
Picker("模式", selection: $mode) {
|
||||||
|
ForEach(WebViewMode.allCases) { item in
|
||||||
|
Text(item.rawValue).tag(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.pickerStyle(.segmented)
|
||||||
|
}
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
|
Text("嵌入式组件")
|
||||||
|
.font(.headline)
|
||||||
|
|
||||||
|
if mode == .embedded {
|
||||||
|
XWebViewView(config: config)
|
||||||
|
.frame(height: 320)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 16))
|
||||||
|
} else {
|
||||||
|
Text("当前处于页面模式,点击下面按钮会 push 到独立页面。")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
|
Text("页面控制")
|
||||||
|
.font(.headline)
|
||||||
|
|
||||||
|
Text("当前 URL: \(currentUrl.isEmpty ? "未加载" : currentUrl)")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
|
||||||
|
HStack(spacing: 12) {
|
||||||
|
Button {
|
||||||
|
XWebViewBridge.shared.currentController()?.goBack()
|
||||||
|
statusVersion += 1
|
||||||
|
} label: {
|
||||||
|
Text("后退")
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
.background(Color.gray.opacity(0.12))
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 10))
|
||||||
|
}
|
||||||
|
.disabled(!(XWebViewBridge.shared.currentController()?.canGoBack() ?? false))
|
||||||
|
|
||||||
|
Button {
|
||||||
|
XWebViewBridge.shared.currentController()?.goForward()
|
||||||
|
statusVersion += 1
|
||||||
|
} label: {
|
||||||
|
Text("前进")
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
.background(Color.gray.opacity(0.12))
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 10))
|
||||||
|
}
|
||||||
|
.disabled(!(XWebViewBridge.shared.currentController()?.canGoForward() ?? false))
|
||||||
|
}
|
||||||
|
|
||||||
|
HStack(spacing: 12) {
|
||||||
|
Button {
|
||||||
|
XWebViewBridge.shared.currentController()?.reload()
|
||||||
|
statusVersion += 1
|
||||||
|
} label: {
|
||||||
|
Text("刷新")
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
.background(Color.gray.opacity(0.12))
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
#if canImport(UIKit)
|
||||||
|
UIPasteboard.general.string = currentUrl
|
||||||
|
#endif
|
||||||
|
statusVersion += 1
|
||||||
|
} label: {
|
||||||
|
Text("复制 URL")
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
.background(Color.gray.opacity(0.12))
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 10))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
XWebViewBridge.shared.currentController()?.load(url: url.trimmingCharacters(in: .whitespacesAndNewlines))
|
||||||
|
statusVersion += 1
|
||||||
|
} label: {
|
||||||
|
Text("重新加载输入 URL")
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.padding()
|
||||||
|
.background(Color.accentColor)
|
||||||
|
.foregroundStyle(.white)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 12))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
openXWebView(config)
|
||||||
|
path.append(AppRoute.webView)
|
||||||
|
} label: {
|
||||||
|
Text("打开页面模式")
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.padding()
|
||||||
|
.background(Color.accentColor)
|
||||||
|
.foregroundStyle(.white)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 12))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
.navigationTitle("网页")
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import XuqmSDK
|
import XuqmSDK
|
||||||
|
import XuqmWebViewSDK
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct XuqmDemoApp: App {
|
struct XuqmDemoApp: App {
|
||||||
@ -30,7 +31,6 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
|||||||
private func initializeSDK() {
|
private func initializeSDK() {
|
||||||
let config = SDKConfig(
|
let config = SDKConfig(
|
||||||
appKey: "ak_demo_chat",
|
appKey: "ak_demo_chat",
|
||||||
appSecret: "demo_secret",
|
|
||||||
debug: true
|
debug: true
|
||||||
)
|
)
|
||||||
XuqmSDK.shared.initialize(config: config)
|
XuqmSDK.shared.initialize(config: config)
|
||||||
@ -82,17 +82,31 @@ struct MainTabView: View {
|
|||||||
}
|
}
|
||||||
.tag(1)
|
.tag(1)
|
||||||
|
|
||||||
|
WebViewDemoView(path: $path)
|
||||||
|
.tabItem {
|
||||||
|
Image(systemName: "globe")
|
||||||
|
Text("网页")
|
||||||
|
}
|
||||||
|
.tag(2)
|
||||||
|
|
||||||
ProfileView(authViewModel: authViewModel)
|
ProfileView(authViewModel: authViewModel)
|
||||||
.tabItem {
|
.tabItem {
|
||||||
Image(systemName: "person.fill")
|
Image(systemName: "person.fill")
|
||||||
Text("我的")
|
Text("我的")
|
||||||
}
|
}
|
||||||
.tag(2)
|
.tag(3)
|
||||||
}
|
}
|
||||||
.navigationDestination(for: AppRoute.self) { route in
|
.navigationDestination(for: AppRoute.self) { route in
|
||||||
switch route {
|
switch route {
|
||||||
case .chat(let targetId, let targetName):
|
case .chat(let targetId, let targetName):
|
||||||
ChatView(targetId: targetId, targetName: targetName, currentUserId: authViewModel.currentUserId)
|
ChatView(targetId: targetId, targetName: targetName, currentUserId: authViewModel.currentUserId)
|
||||||
|
case .webView:
|
||||||
|
XWebViewPage(
|
||||||
|
config: XWebViewConfig(
|
||||||
|
url: "https://example.com",
|
||||||
|
title: "独立页面"
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
XuqmWebViewSDK.podspec
普通文件
13
XuqmWebViewSDK.podspec
普通文件
@ -0,0 +1,13 @@
|
|||||||
|
Pod::Spec.new do |s|
|
||||||
|
s.name = 'XuqmWebViewSDK'
|
||||||
|
s.version = '0.1.0'
|
||||||
|
s.summary = 'XuqmGroup iOS SDK — WebView module'
|
||||||
|
s.homepage = 'https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK'
|
||||||
|
s.license = { :type => 'MIT' }
|
||||||
|
s.author = { 'XuqmGroup' => 'dev@xuqm.com' }
|
||||||
|
s.source = { :git => 'https://xuqinmin.com/xuqinmin12/XuqmGroup-iOSSDK.git', :tag => s.version.to_s }
|
||||||
|
s.ios.deployment_target = '16.0'
|
||||||
|
s.swift_version = '5.9'
|
||||||
|
s.source_files = 'Sources/XuqmWebViewSDK/**/*.swift'
|
||||||
|
s.dependency 'XuqmSDK'
|
||||||
|
end
|
||||||
正在加载...
在新工单中引用
屏蔽一个用户