dsh-agora 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +88 -0
- package/assets/agora/SKILL.md +113 -0
- package/assets/agora/references/cli/README.md +161 -0
- package/assets/agora/references/cli/automation.md +189 -0
- package/assets/agora/references/cli/doctor.md +129 -0
- package/assets/agora/references/cli/env.md +158 -0
- package/assets/agora/references/cli/install-auth.md +152 -0
- package/assets/agora/references/cli/projects.md +116 -0
- package/assets/agora/references/cli/quickstarts.md +117 -0
- package/assets/agora/references/cloud-recording/README.md +86 -0
- package/assets/agora/references/conversational-ai/README.md +285 -0
- package/assets/agora/references/conversational-ai/agent-client-toolkit-react.md +182 -0
- package/assets/agora/references/conversational-ai/agent-samples.md +101 -0
- package/assets/agora/references/conversational-ai/agent-toolkit-android.md +209 -0
- package/assets/agora/references/conversational-ai/agent-toolkit-ios.md +208 -0
- package/assets/agora/references/conversational-ai/agent-toolkit.md +201 -0
- package/assets/agora/references/conversational-ai/agent-ui-kit.md +63 -0
- package/assets/agora/references/conversational-ai/architecture.md +221 -0
- package/assets/agora/references/conversational-ai/auth-flow.md +154 -0
- package/assets/agora/references/conversational-ai/conversational-ai-studio.md +173 -0
- package/assets/agora/references/conversational-ai/go-sdk.md +184 -0
- package/assets/agora/references/conversational-ai/integration-from-quickstart.md +203 -0
- package/assets/agora/references/conversational-ai/python-sdk.md +122 -0
- package/assets/agora/references/conversational-ai/quickstarts.md +710 -0
- package/assets/agora/references/conversational-ai/server-custom-llm.md +45 -0
- package/assets/agora/references/conversational-ai/server-mcp.md +40 -0
- package/assets/agora/references/conversational-ai/server-sdk-rename.md +78 -0
- package/assets/agora/references/conversational-ai/server-sdks.md +128 -0
- package/assets/agora/references/doc-fetching.md +67 -0
- package/assets/agora/references/integration-patterns.md +201 -0
- package/assets/agora/references/mcp-tools.md +49 -0
- package/assets/agora/references/rtc/README.md +104 -0
- package/assets/agora/references/rtc/android.md +344 -0
- package/assets/agora/references/rtc/cross-platform-coordination.md +61 -0
- package/assets/agora/references/rtc/flutter.md +282 -0
- package/assets/agora/references/rtc/ios.md +306 -0
- package/assets/agora/references/rtc/nextjs.md +87 -0
- package/assets/agora/references/rtc/react-native.md +266 -0
- package/assets/agora/references/rtc/react.md +186 -0
- package/assets/agora/references/rtc/web.md +506 -0
- package/assets/agora/references/rtm/README.md +80 -0
- package/assets/agora/references/rtm/android.md +277 -0
- package/assets/agora/references/rtm/ios.md +231 -0
- package/assets/agora/references/rtm/web.md +348 -0
- package/assets/agora/references/server/README.md +22 -0
- package/assets/agora/references/server/tokens.md +74 -0
- package/assets/agora/references/server-gateway/README.md +80 -0
- package/assets/agora/references/server-gateway/linux-cpp.md +251 -0
- package/assets/agora/references/testing-guidance/SKILL.md +65 -0
- package/assets/agora/references/testing-guidance/completeness-gate.md +28 -0
- package/assets/agora/references/testing-guidance/convoai-rest.md +83 -0
- package/assets/agora/references/testing-guidance/mobile-rtm-and-renewal.md +109 -0
- package/assets/agora/references/testing-guidance/rtc-android.md +70 -0
- package/assets/agora/references/testing-guidance/rtc-ios.md +73 -0
- package/assets/agora/references/testing-guidance/rtc-react.md +51 -0
- package/assets/agora/references/testing-guidance/rtc-web.md +94 -0
- package/cordis.patch.yml +5 -0
- package/index.js +53 -0
- package/package.json +49 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# Agora Signaling SDK v2 — Android (Kotlin)
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- [Installation](#installation)
|
|
6
|
+
- [Initialization and Login](#initialization-and-login)
|
|
7
|
+
- [Channel Subscription](#channel-subscription)
|
|
8
|
+
- [Sending Messages](#sending-messages)
|
|
9
|
+
- [Receiving Messages](#receiving-messages)
|
|
10
|
+
- [Presence Events](#presence-events)
|
|
11
|
+
- [Cleanup](#cleanup)
|
|
12
|
+
- [Complete Example](#complete-example)
|
|
13
|
+
|
|
14
|
+
API Reference: <https://api-ref.agora.io/en/signaling/android/2.x/API/rtm_api_overview.html>
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
Add to `build.gradle`:
|
|
19
|
+
|
|
20
|
+
```groovy
|
|
21
|
+
dependencies {
|
|
22
|
+
implementation 'io.agora:agora-rtm:2.2.+'
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Add permissions to `AndroidManifest.xml`:
|
|
27
|
+
|
|
28
|
+
```xml
|
|
29
|
+
<uses-permission android:name="android.permission.INTERNET" />
|
|
30
|
+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Initialization and Login
|
|
34
|
+
|
|
35
|
+
```kotlin
|
|
36
|
+
import io.agora.rtm.RtmClient
|
|
37
|
+
import io.agora.rtm.RtmConfig
|
|
38
|
+
import io.agora.rtm.RtmEventListener
|
|
39
|
+
import io.agora.rtm.ResultCallback
|
|
40
|
+
import io.agora.rtm.ErrorInfo
|
|
41
|
+
|
|
42
|
+
val config = RtmConfig.Builder("your-app-id", "user-id-string")
|
|
43
|
+
.eventListener(rtmEventListener)
|
|
44
|
+
.build()
|
|
45
|
+
|
|
46
|
+
val rtmClient = RtmClient.create(config)
|
|
47
|
+
|
|
48
|
+
// Login — always with a server-generated token
|
|
49
|
+
rtmClient.login("YOUR_RTM_TOKEN", object : ResultCallback<Void> {
|
|
50
|
+
override fun onSuccess(responseInfo: Void?) {
|
|
51
|
+
Log.d("RTM", "Logged in")
|
|
52
|
+
}
|
|
53
|
+
override fun onFailure(errorInfo: ErrorInfo) {
|
|
54
|
+
Log.e("RTM", "Login failed: ${errorInfo.errorReason}")
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Channel Subscription
|
|
60
|
+
|
|
61
|
+
```kotlin
|
|
62
|
+
import io.agora.rtm.SubscribeOptions
|
|
63
|
+
import io.agora.rtm.RtmConstants
|
|
64
|
+
|
|
65
|
+
val options = SubscribeOptions().apply {
|
|
66
|
+
withMessage = true
|
|
67
|
+
withPresence = true
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
rtmClient.subscribe("channel-name", options, object : ResultCallback<Void> {
|
|
71
|
+
override fun onSuccess(responseInfo: Void?) { }
|
|
72
|
+
override fun onFailure(errorInfo: ErrorInfo) {
|
|
73
|
+
Log.e("RTM", "Subscribe failed: ${errorInfo.errorReason}")
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
// Unsubscribe
|
|
78
|
+
rtmClient.unsubscribe("channel-name", object : ResultCallback<Void> {
|
|
79
|
+
override fun onSuccess(responseInfo: Void?) { }
|
|
80
|
+
override fun onFailure(errorInfo: ErrorInfo) { }
|
|
81
|
+
})
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Sending Messages
|
|
85
|
+
|
|
86
|
+
```kotlin
|
|
87
|
+
import io.agora.rtm.PublishOptions
|
|
88
|
+
import io.agora.rtm.RtmConstants
|
|
89
|
+
|
|
90
|
+
// Publish to a channel
|
|
91
|
+
val options = PublishOptions().apply {
|
|
92
|
+
customType = "chat.message"
|
|
93
|
+
}
|
|
94
|
+
rtmClient.publish("channel-name", "Hello!", options, object : ResultCallback<Void> {
|
|
95
|
+
override fun onSuccess(responseInfo: Void?) { }
|
|
96
|
+
override fun onFailure(errorInfo: ErrorInfo) { }
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
// Publish JSON data
|
|
100
|
+
val json = JSONObject().apply {
|
|
101
|
+
put("type", "control")
|
|
102
|
+
put("action", "mute-all")
|
|
103
|
+
}.toString()
|
|
104
|
+
rtmClient.publish("channel-name", json, options, object : ResultCallback<Void> {
|
|
105
|
+
override fun onSuccess(responseInfo: Void?) { }
|
|
106
|
+
override fun onFailure(errorInfo: ErrorInfo) { }
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
// Peer-to-peer (user channel)
|
|
110
|
+
val p2pOptions = PublishOptions().apply {
|
|
111
|
+
setChannelType(RtmConstants.RtmChannelType.USER)
|
|
112
|
+
customType = "user.transcription"
|
|
113
|
+
}
|
|
114
|
+
rtmClient.publish("target-user-id", message, p2pOptions, object : ResultCallback<Void> {
|
|
115
|
+
override fun onSuccess(responseInfo: Void?) { }
|
|
116
|
+
override fun onFailure(errorInfo: ErrorInfo) { }
|
|
117
|
+
})
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Receiving Messages
|
|
121
|
+
|
|
122
|
+
Implement `RtmEventListener`:
|
|
123
|
+
|
|
124
|
+
```kotlin
|
|
125
|
+
val rtmEventListener = object : RtmEventListener {
|
|
126
|
+
override fun onMessageEvent(event: MessageEvent?) {
|
|
127
|
+
event ?: return
|
|
128
|
+
val message = event.message.data as? String ?: return
|
|
129
|
+
Log.d("RTM", "From: ${event.publisherId}, Channel: ${event.channelName}, Message: $message")
|
|
130
|
+
// Parse JSON if needed
|
|
131
|
+
try {
|
|
132
|
+
val json = JSONObject(message)
|
|
133
|
+
} catch (e: Exception) { }
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Presence Events
|
|
139
|
+
|
|
140
|
+
```kotlin
|
|
141
|
+
val rtmEventListener = object : RtmEventListener {
|
|
142
|
+
override fun onPresenceEvent(event: PresenceEvent?) {
|
|
143
|
+
event ?: return
|
|
144
|
+
when (event.eventType) {
|
|
145
|
+
RtmConstants.RtmPresenceEventType.SNAPSHOT -> {
|
|
146
|
+
for (user in event.snapshot) {
|
|
147
|
+
Log.d("RTM", "Online: ${user.userId}, states: ${user.states}")
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
RtmConstants.RtmPresenceEventType.REMOTE_JOIN -> {
|
|
151
|
+
Log.d("RTM", "User joined: ${event.publisherId}")
|
|
152
|
+
}
|
|
153
|
+
RtmConstants.RtmPresenceEventType.REMOTE_LEAVE,
|
|
154
|
+
RtmConstants.RtmPresenceEventType.REMOTE_TIMEOUT -> {
|
|
155
|
+
Log.d("RTM", "User left: ${event.publisherId}")
|
|
156
|
+
}
|
|
157
|
+
RtmConstants.RtmPresenceEventType.REMOTE_STATE_CHANGED -> {
|
|
158
|
+
Log.d("RTM", "State changed: ${event.publisherId}, ${event.stateItems}")
|
|
159
|
+
}
|
|
160
|
+
else -> {}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Cleanup
|
|
167
|
+
|
|
168
|
+
```kotlin
|
|
169
|
+
fun cleanup() {
|
|
170
|
+
rtmClient.unsubscribe("channel-name", object : ResultCallback<Void> {
|
|
171
|
+
override fun onSuccess(responseInfo: Void?) {
|
|
172
|
+
rtmClient.logout(object : ResultCallback<Void> {
|
|
173
|
+
override fun onSuccess(responseInfo: Void?) {
|
|
174
|
+
rtmClient.release()
|
|
175
|
+
}
|
|
176
|
+
override fun onFailure(errorInfo: ErrorInfo) {
|
|
177
|
+
rtmClient.release()
|
|
178
|
+
}
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
override fun onFailure(errorInfo: ErrorInfo) { }
|
|
182
|
+
})
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Complete Example
|
|
187
|
+
|
|
188
|
+
```kotlin
|
|
189
|
+
class SignalingActivity : AppCompatActivity() {
|
|
190
|
+
private lateinit var rtmClient: RtmClient
|
|
191
|
+
private val channelName = "my-channel"
|
|
192
|
+
|
|
193
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
194
|
+
super.onCreate(savedInstanceState)
|
|
195
|
+
|
|
196
|
+
val config = RtmConfig.Builder("your-app-id", "user-123")
|
|
197
|
+
.eventListener(rtmEventListener)
|
|
198
|
+
.build()
|
|
199
|
+
rtmClient = RtmClient.create(config)
|
|
200
|
+
|
|
201
|
+
rtmClient.login("YOUR_RTM_TOKEN", object : ResultCallback<Void> {
|
|
202
|
+
override fun onSuccess(responseInfo: Void?) {
|
|
203
|
+
subscribeToChannel()
|
|
204
|
+
}
|
|
205
|
+
override fun onFailure(errorInfo: ErrorInfo) {
|
|
206
|
+
Log.e("RTM", "Login failed: ${errorInfo.errorReason}")
|
|
207
|
+
}
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private fun subscribeToChannel() {
|
|
212
|
+
val options = SubscribeOptions().apply {
|
|
213
|
+
withMessage = true
|
|
214
|
+
withPresence = true
|
|
215
|
+
}
|
|
216
|
+
rtmClient.subscribe(channelName, options, object : ResultCallback<Void> {
|
|
217
|
+
override fun onSuccess(responseInfo: Void?) { }
|
|
218
|
+
override fun onFailure(errorInfo: ErrorInfo) {
|
|
219
|
+
Log.e("RTM", "Subscribe failed: ${errorInfo.errorReason}")
|
|
220
|
+
}
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
fun sendMessage(text: String) {
|
|
225
|
+
val options = PublishOptions().apply { customType = "chat.message" }
|
|
226
|
+
rtmClient.publish(channelName, text, options, object : ResultCallback<Void> {
|
|
227
|
+
override fun onSuccess(responseInfo: Void?) { }
|
|
228
|
+
override fun onFailure(errorInfo: ErrorInfo) { }
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private val rtmEventListener = object : RtmEventListener {
|
|
233
|
+
override fun onMessageEvent(event: MessageEvent?) {
|
|
234
|
+
event ?: return
|
|
235
|
+
val message = event.message.data as? String ?: return
|
|
236
|
+
runOnUiThread {
|
|
237
|
+
Log.d("RTM", "${event.publisherId}: $message")
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
override fun onPresenceEvent(event: PresenceEvent?) {
|
|
242
|
+
event ?: return
|
|
243
|
+
if (event.eventType == RtmConstants.RtmPresenceEventType.REMOTE_JOIN) {
|
|
244
|
+
Log.d("RTM", "User joined: ${event.publisherId}")
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
override fun onDestroy() {
|
|
250
|
+
super.onDestroy()
|
|
251
|
+
rtmClient.unsubscribe(channelName, object : ResultCallback<Void> {
|
|
252
|
+
override fun onSuccess(responseInfo: Void?) {
|
|
253
|
+
rtmClient.logout(object : ResultCallback<Void> {
|
|
254
|
+
override fun onSuccess(responseInfo: Void?) { rtmClient.release() }
|
|
255
|
+
override fun onFailure(errorInfo: ErrorInfo) { rtmClient.release() }
|
|
256
|
+
})
|
|
257
|
+
}
|
|
258
|
+
override fun onFailure(errorInfo: ErrorInfo) { }
|
|
259
|
+
})
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Notes
|
|
265
|
+
|
|
266
|
+
- Android RTM UIDs are **strings**. When pairing with RTC (which uses `Int`), use `rtcUid.toString()` as the RTM userId.
|
|
267
|
+
- Login must complete before any subscribe/publish operations.
|
|
268
|
+
- Call `release()` only after logout completes to avoid resource leaks.
|
|
269
|
+
|
|
270
|
+
For test setup and mocking patterns, see [references/testing-guidance/SKILL.md](../testing-guidance/SKILL.md).
|
|
271
|
+
|
|
272
|
+
## Official Documentation
|
|
273
|
+
|
|
274
|
+
For APIs or features not covered above:
|
|
275
|
+
|
|
276
|
+
- Quick-start guide: <https://docs-md.agora.io/en/signaling/get-started/sdk-quickstart.md?platform=android>
|
|
277
|
+
- API Reference: <https://api-ref.agora.io/en/signaling/android/2.x/API/rtm_api_overview.html>
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# Agora Signaling SDK v2 — iOS (Swift)
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
|
|
5
|
+
- [Installation](#installation)
|
|
6
|
+
- [Initialization and Login](#initialization-and-login)
|
|
7
|
+
- [Channel Subscription](#channel-subscription)
|
|
8
|
+
- [Sending Messages](#sending-messages)
|
|
9
|
+
- [Receiving Messages](#receiving-messages)
|
|
10
|
+
- [Presence Events](#presence-events)
|
|
11
|
+
- [Cleanup](#cleanup)
|
|
12
|
+
- [Complete Example](#complete-example)
|
|
13
|
+
|
|
14
|
+
API Reference: <https://api-ref.agora.io/en/signaling/ios/2.x/API/rtm_api_overview.html>
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
**CocoaPods** — add to your `Podfile`:
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
pod 'AgoraRtm', '~> 2.2'
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Run `pod install`.
|
|
25
|
+
|
|
26
|
+
**Swift Package Manager** — add `https://github.com/AgoraIO/AgoraRtm_iOS` as a package dependency.
|
|
27
|
+
|
|
28
|
+
## Initialization and Login
|
|
29
|
+
|
|
30
|
+
```swift
|
|
31
|
+
import AgoraRtmKit
|
|
32
|
+
|
|
33
|
+
var rtmKit: AgoraRtmClientKit?
|
|
34
|
+
|
|
35
|
+
// Initialize
|
|
36
|
+
let config = AgoraRtmClientConfig(appId: "your-app-id", userId: "user-id-string")
|
|
37
|
+
do {
|
|
38
|
+
rtmKit = try AgoraRtmClientKit(config, delegate: self)
|
|
39
|
+
} catch {
|
|
40
|
+
print("RTM init failed: \(error)")
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Login — always with a server-generated token
|
|
44
|
+
rtmKit?.login(token: "YOUR_RTM_TOKEN") { response, error in
|
|
45
|
+
if let error = error {
|
|
46
|
+
print("Login failed: \(error)")
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
print("RTM logged in")
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Channel Subscription
|
|
54
|
+
|
|
55
|
+
```swift
|
|
56
|
+
// Subscribe to a channel
|
|
57
|
+
let options = AgoraRtmSubscribeOptions()
|
|
58
|
+
options.features = [.message, .presence]
|
|
59
|
+
|
|
60
|
+
rtmKit?.subscribe(channelName: "channel-name", option: options) { response, error in
|
|
61
|
+
if let error = error {
|
|
62
|
+
print("Subscribe failed: \(error)")
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Unsubscribe
|
|
67
|
+
rtmKit?.unsubscribe("channel-name") { response, error in }
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Sending Messages
|
|
71
|
+
|
|
72
|
+
```swift
|
|
73
|
+
// Publish to a channel
|
|
74
|
+
let publishOptions = AgoraRtmPublishOptions()
|
|
75
|
+
publishOptions.customType = "chat.message"
|
|
76
|
+
|
|
77
|
+
rtmKit?.publish(channelName: "channel-name", message: "Hello!", option: publishOptions) { response, error in
|
|
78
|
+
if let error = error { print("Publish failed: \(error)") }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Publish JSON data
|
|
82
|
+
let payload = try! JSONSerialization.data(withJSONObject: ["type": "control", "action": "mute-all"])
|
|
83
|
+
let jsonString = String(data: payload, encoding: .utf8)!
|
|
84
|
+
rtmKit?.publish(channelName: "channel-name", message: jsonString, option: publishOptions) { _, _ in }
|
|
85
|
+
|
|
86
|
+
// Peer-to-peer (user channel)
|
|
87
|
+
let p2pOptions = AgoraRtmPublishOptions()
|
|
88
|
+
p2pOptions.customType = "user.transcription"
|
|
89
|
+
p2pOptions.channelType = .user
|
|
90
|
+
|
|
91
|
+
rtmKit?.publish(channelName: "target-user-id", message: message, option: p2pOptions) { _, _ in }
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Receiving Messages
|
|
95
|
+
|
|
96
|
+
Implement `AgoraRtmClientDelegate`:
|
|
97
|
+
|
|
98
|
+
```swift
|
|
99
|
+
extension YourClass: AgoraRtmClientDelegate {
|
|
100
|
+
func rtmKit(_ rtmKit: AgoraRtmClientKit, didReceiveMessageEvent event: AgoraRtmMessageEvent) {
|
|
101
|
+
print("From: \(event.publisher)")
|
|
102
|
+
print("Channel: \(event.channelName)")
|
|
103
|
+
print("Message: \(event.message.stringData ?? "")")
|
|
104
|
+
print("Type: \(event.customType ?? "")")
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Presence Events
|
|
110
|
+
|
|
111
|
+
```swift
|
|
112
|
+
func rtmKit(_ rtmKit: AgoraRtmClientKit, didReceivePresenceEvent event: AgoraRtmPresenceEvent) {
|
|
113
|
+
switch event.type {
|
|
114
|
+
case .snapshot:
|
|
115
|
+
// Initial state on subscribe
|
|
116
|
+
for user in event.snapshot {
|
|
117
|
+
print("Online: \(user.userId), states: \(user.states)")
|
|
118
|
+
}
|
|
119
|
+
case .remoteJoinChannel:
|
|
120
|
+
print("User joined: \(event.publisher ?? "")")
|
|
121
|
+
case .remoteLeaveChannel, .remoteConnectionTimeout:
|
|
122
|
+
print("User left: \(event.publisher ?? "")")
|
|
123
|
+
case .remoteStateChanged:
|
|
124
|
+
print("State changed: \(event.publisher ?? ""), \(event.states)")
|
|
125
|
+
default:
|
|
126
|
+
break
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Connection Status
|
|
132
|
+
|
|
133
|
+
```swift
|
|
134
|
+
func rtmKit(_ rtmKit: AgoraRtmClientKit, channel: String, connectionChangedToState state: AgoraRtmClientConnectionState, reason: AgoraRtmClientConnectionChangeReason) {
|
|
135
|
+
print("RTM connection state: \(state.rawValue)")
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Cleanup
|
|
140
|
+
|
|
141
|
+
```swift
|
|
142
|
+
func cleanup() {
|
|
143
|
+
rtmKit?.unsubscribe("channel-name") { _, _ in }
|
|
144
|
+
rtmKit?.logout { _, _ in }
|
|
145
|
+
rtmKit?.destroy()
|
|
146
|
+
rtmKit = nil
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Complete Example
|
|
151
|
+
|
|
152
|
+
```swift
|
|
153
|
+
import UIKit
|
|
154
|
+
import AgoraRtmKit
|
|
155
|
+
|
|
156
|
+
class SignalingViewController: UIViewController {
|
|
157
|
+
var rtmKit: AgoraRtmClientKit?
|
|
158
|
+
let channelName = "my-channel"
|
|
159
|
+
|
|
160
|
+
override func viewDidLoad() {
|
|
161
|
+
super.viewDidLoad()
|
|
162
|
+
setupRTM()
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
func setupRTM() {
|
|
166
|
+
let config = AgoraRtmClientConfig(appId: "your-app-id", userId: "user-123")
|
|
167
|
+
do {
|
|
168
|
+
rtmKit = try AgoraRtmClientKit(config, delegate: self)
|
|
169
|
+
} catch {
|
|
170
|
+
print("RTM init failed: \(error)")
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
rtmKit?.login(token: "YOUR_RTM_TOKEN") { [weak self] _, error in
|
|
175
|
+
guard error == nil else { return }
|
|
176
|
+
self?.subscribeToChannel()
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
func subscribeToChannel() {
|
|
181
|
+
let options = AgoraRtmSubscribeOptions()
|
|
182
|
+
options.features = [.message, .presence]
|
|
183
|
+
|
|
184
|
+
rtmKit?.subscribe(channelName: channelName, option: options) { _, error in
|
|
185
|
+
if let error = error {
|
|
186
|
+
print("Subscribe failed: \(error)")
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
func sendMessage(_ text: String) {
|
|
192
|
+
let options = AgoraRtmPublishOptions()
|
|
193
|
+
options.customType = "chat.message"
|
|
194
|
+
rtmKit?.publish(channelName: channelName, message: text, option: options) { _, _ in }
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
override func viewDidDisappear(_ animated: Bool) {
|
|
198
|
+
super.viewDidDisappear(animated)
|
|
199
|
+
rtmKit?.unsubscribe(channelName) { _, _ in }
|
|
200
|
+
rtmKit?.logout { _, _ in }
|
|
201
|
+
rtmKit?.destroy()
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
extension SignalingViewController: AgoraRtmClientDelegate {
|
|
206
|
+
func rtmKit(_ rtmKit: AgoraRtmClientKit, didReceiveMessageEvent event: AgoraRtmMessageEvent) {
|
|
207
|
+
print("\(event.publisher ?? ""): \(event.message.stringData ?? "")")
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
func rtmKit(_ rtmKit: AgoraRtmClientKit, didReceivePresenceEvent event: AgoraRtmPresenceEvent) {
|
|
211
|
+
if event.type == .remoteJoinChannel {
|
|
212
|
+
print("User joined: \(event.publisher ?? "")")
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Notes
|
|
219
|
+
|
|
220
|
+
- iOS RTM UIDs are **strings**. When pairing with RTC (which uses `UInt`), use `String(rtcUid)` as the RTM userId.
|
|
221
|
+
- The RTM constructor can throw — always wrap in `do/catch`.
|
|
222
|
+
- Always call `login()` and wait for the callback before subscribing or publishing.
|
|
223
|
+
|
|
224
|
+
For test setup and mocking patterns, see [references/testing-guidance/SKILL.md](../testing-guidance/SKILL.md).
|
|
225
|
+
|
|
226
|
+
## Official Documentation
|
|
227
|
+
|
|
228
|
+
For APIs or features not covered above:
|
|
229
|
+
|
|
230
|
+
- Quick-start guide: <https://docs-md.agora.io/en/signaling/get-started/sdk-quickstart.md?platform=ios>
|
|
231
|
+
- API Reference: <https://api-ref.agora.io/en/signaling/ios/2.x/API/rtm_api_overview.html>
|