@cometchat/skills 4.0.0 → 4.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/README.md +42 -2
- package/bin/install.js +448 -57
- package/package.json +1 -1
- package/skills/cometchat/SKILL.md +337 -80
- package/skills/cometchat-a11y/SKILL.md +476 -0
- package/skills/cometchat-android-v5/SKILL.md +1 -1
- package/skills/cometchat-android-v5-calls/SKILL.md +413 -0
- package/skills/cometchat-android-v5-calls/references/README.md +53 -0
- package/skills/cometchat-android-v5-calls/references/add-calls-to-existing-chat.md +195 -0
- package/skills/cometchat-android-v5-calls/references/audio-controls.md +91 -0
- package/skills/cometchat-android-v5-calls/references/background-handling.md +168 -0
- package/skills/cometchat-android-v5-calls/references/call-layouts.md +123 -0
- package/skills/cometchat-android-v5-calls/references/call-logs.md +113 -0
- package/skills/cometchat-android-v5-calls/references/call-session.md +274 -0
- package/skills/cometchat-android-v5-calls/references/custom-ui.md +150 -0
- package/skills/cometchat-android-v5-calls/references/event-listeners.md +123 -0
- package/skills/cometchat-android-v5-calls/references/group-calls.md +205 -0
- package/skills/cometchat-android-v5-calls/references/idle-timeout.md +111 -0
- package/skills/cometchat-android-v5-calls/references/in-call-chat.md +134 -0
- package/skills/cometchat-android-v5-calls/references/join-session.md +120 -0
- package/skills/cometchat-android-v5-calls/references/migration-v4-to-v5.md +206 -0
- package/skills/cometchat-android-v5-calls/references/participant-management.md +110 -0
- package/skills/cometchat-android-v5-calls/references/picture-in-picture.md +106 -0
- package/skills/cometchat-android-v5-calls/references/raise-hand.md +242 -0
- package/skills/cometchat-android-v5-calls/references/recording.md +101 -0
- package/skills/cometchat-android-v5-calls/references/ringing-integration.md +152 -0
- package/skills/cometchat-android-v5-calls/references/screen-sharing.md +82 -0
- package/skills/cometchat-android-v5-calls/references/server-fcm-voip.md +215 -0
- package/skills/cometchat-android-v5-calls/references/session-settings.md +121 -0
- package/skills/cometchat-android-v5-calls/references/setup.md +137 -0
- package/skills/cometchat-android-v5-calls/references/share-invite.md +151 -0
- package/skills/cometchat-android-v5-calls/references/video-controls.md +87 -0
- package/skills/cometchat-android-v5-calls/references/voip-calling.md +526 -0
- package/skills/cometchat-android-v5-components/SKILL.md +1 -1
- package/skills/cometchat-android-v5-core/SKILL.md +1 -1
- package/skills/cometchat-android-v5-customization/SKILL.md +1 -1
- package/skills/cometchat-android-v5-extensions/SKILL.md +1 -1
- package/skills/cometchat-android-v5-features/SKILL.md +1 -1
- package/skills/cometchat-android-v5-placement/SKILL.md +1 -1
- package/skills/cometchat-android-v5-production/SKILL.md +1 -1
- package/skills/cometchat-android-v5-push/SKILL.md +1 -1
- package/skills/cometchat-android-v5-testing/SKILL.md +1 -1
- package/skills/cometchat-android-v5-theming/SKILL.md +1 -1
- package/skills/cometchat-android-v5-troubleshooting/SKILL.md +1 -1
- package/skills/cometchat-android-v6/SKILL.md +1 -1
- package/skills/cometchat-android-v6-builder-settings/SKILL.md +1 -1
- package/skills/cometchat-android-v6-calls/SKILL.md +375 -0
- package/skills/cometchat-android-v6-calls/references/add-calls-to-existing-chat.md +97 -0
- package/skills/cometchat-android-v6-calls/references/call-layouts.md +91 -0
- package/skills/cometchat-android-v6-calls/references/call-session.md +163 -0
- package/skills/cometchat-android-v6-calls/references/device-management.md +171 -0
- package/skills/cometchat-android-v6-calls/references/group-calls.md +191 -0
- package/skills/cometchat-android-v6-calls/references/idle-timeout.md +107 -0
- package/skills/cometchat-android-v6-calls/references/in-call-chat.md +136 -0
- package/skills/cometchat-android-v6-calls/references/raise-hand.md +186 -0
- package/skills/cometchat-android-v6-calls/references/ringing-integration.md +176 -0
- package/skills/cometchat-android-v6-calls/references/server-fcm-voip.md +127 -0
- package/skills/cometchat-android-v6-calls/references/share-invite.md +105 -0
- package/skills/cometchat-android-v6-compose-components/SKILL.md +2 -2
- package/skills/cometchat-android-v6-compose-customization/SKILL.md +1 -1
- package/skills/cometchat-android-v6-compose-placement/SKILL.md +1 -1
- package/skills/cometchat-android-v6-compose-theming/SKILL.md +1 -1
- package/skills/cometchat-android-v6-core/SKILL.md +1 -1
- package/skills/cometchat-android-v6-events/SKILL.md +1 -1
- package/skills/cometchat-android-v6-extensions/SKILL.md +1 -1
- package/skills/cometchat-android-v6-features/SKILL.md +1 -1
- package/skills/cometchat-android-v6-kotlin-components/SKILL.md +1 -1
- package/skills/cometchat-android-v6-kotlin-customization/SKILL.md +1 -1
- package/skills/cometchat-android-v6-kotlin-placement/SKILL.md +1 -1
- package/skills/cometchat-android-v6-kotlin-theming/SKILL.md +1 -1
- package/skills/cometchat-android-v6-migration/SKILL.md +398 -0
- package/skills/cometchat-android-v6-production/SKILL.md +1 -1
- package/skills/cometchat-android-v6-push/SKILL.md +1 -1
- package/skills/cometchat-android-v6-testing/SKILL.md +1 -1
- package/skills/cometchat-android-v6-troubleshooting/SKILL.md +1 -1
- package/skills/cometchat-angular-calls/SKILL.md +511 -0
- package/skills/cometchat-angular-calls/references/add-calls-to-existing-chat.md +95 -0
- package/skills/cometchat-angular-calls/references/call-layouts.md +106 -0
- package/skills/cometchat-angular-calls/references/call-session.md +167 -0
- package/skills/cometchat-angular-calls/references/custom-ui.md +231 -0
- package/skills/cometchat-angular-calls/references/device-management.md +135 -0
- package/skills/cometchat-angular-calls/references/group-calls.md +412 -0
- package/skills/cometchat-angular-calls/references/idle-timeout.md +145 -0
- package/skills/cometchat-angular-calls/references/in-call-chat.md +148 -0
- package/skills/cometchat-angular-calls/references/lazy-loading-pitfalls.md +205 -0
- package/skills/cometchat-angular-calls/references/migration-v4-to-v5.md +53 -0
- package/skills/cometchat-angular-calls/references/ngzone-and-async-callbacks.md +183 -0
- package/skills/cometchat-angular-calls/references/picture-in-picture.md +285 -0
- package/skills/cometchat-angular-calls/references/raise-hand.md +167 -0
- package/skills/cometchat-angular-calls/references/recording-screen-share.md +267 -0
- package/skills/cometchat-angular-calls/references/ringing-integration.md +131 -0
- package/skills/cometchat-angular-calls/references/server-web-push-vapid.md +100 -0
- package/skills/cometchat-angular-calls/references/share-invite.md +116 -0
- package/skills/cometchat-angular-components/SKILL.md +1 -1
- package/skills/cometchat-angular-core/SKILL.md +1 -1
- package/skills/cometchat-angular-customization/SKILL.md +1 -1
- package/skills/cometchat-angular-features/SKILL.md +1 -1
- package/skills/cometchat-angular-patterns/SKILL.md +1 -1
- package/skills/cometchat-angular-placement/SKILL.md +1 -1
- package/skills/cometchat-angular-production/SKILL.md +1 -1
- package/skills/cometchat-angular-push/SKILL.md +403 -0
- package/skills/cometchat-angular-testing/SKILL.md +333 -0
- package/skills/cometchat-angular-theming/SKILL.md +1 -1
- package/skills/cometchat-angular-troubleshooting/SKILL.md +1 -1
- package/skills/cometchat-astro-patterns/SKILL.md +1 -1
- package/skills/cometchat-calls/SKILL.md +358 -0
- package/skills/cometchat-calls/references/use-case-broadcast.md +194 -0
- package/skills/cometchat-calls/references/use-case-marketplace.md +158 -0
- package/skills/cometchat-calls/references/use-case-support.md +190 -0
- package/skills/cometchat-calls/references/use-case-team.md +169 -0
- package/skills/cometchat-calls/references/use-case-telehealth.md +154 -0
- package/skills/cometchat-components/SKILL.md +1 -1
- package/skills/cometchat-core/SKILL.md +1 -1
- package/skills/cometchat-customization/SKILL.md +2 -2
- package/skills/cometchat-features/SKILL.md +2 -2
- package/skills/cometchat-flutter-v5/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-calls/SKILL.md +325 -94
- package/skills/cometchat-flutter-v5-calls/references/add-calls-to-existing-chat.md +169 -0
- package/skills/cometchat-flutter-v5-calls/references/call-layouts.md +98 -0
- package/skills/cometchat-flutter-v5-calls/references/call-session.md +330 -0
- package/skills/cometchat-flutter-v5-calls/references/device-management.md +120 -0
- package/skills/cometchat-flutter-v5-calls/references/group-calls.md +187 -0
- package/skills/cometchat-flutter-v5-calls/references/idle-timeout.md +97 -0
- package/skills/cometchat-flutter-v5-calls/references/in-call-chat.md +132 -0
- package/skills/cometchat-flutter-v5-calls/references/migration-v4-to-v5.md +160 -0
- package/skills/cometchat-flutter-v5-calls/references/raise-hand.md +244 -0
- package/skills/cometchat-flutter-v5-calls/references/ringing-integration.md +193 -0
- package/skills/cometchat-flutter-v5-calls/references/server-push-bridge.md +101 -0
- package/skills/cometchat-flutter-v5-calls/references/share-invite.md +142 -0
- package/skills/cometchat-flutter-v5-conversations/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-core/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-customization/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-events/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-messages/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-production/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-push/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-testing/SKILL.md +390 -0
- package/skills/cometchat-flutter-v5-theming/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-troubleshooting/SKILL.md +1 -1
- package/skills/cometchat-flutter-v5-users-groups/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-calls/SKILL.md +308 -243
- package/skills/cometchat-flutter-v6-calls/references/add-calls-to-existing-chat.md +134 -0
- package/skills/cometchat-flutter-v6-calls/references/call-layouts.md +102 -0
- package/skills/cometchat-flutter-v6-calls/references/call-session.md +229 -0
- package/skills/cometchat-flutter-v6-calls/references/device-management.md +99 -0
- package/skills/cometchat-flutter-v6-calls/references/group-calls.md +190 -0
- package/skills/cometchat-flutter-v6-calls/references/idle-timeout.md +80 -0
- package/skills/cometchat-flutter-v6-calls/references/in-call-chat.md +122 -0
- package/skills/cometchat-flutter-v6-calls/references/raise-hand.md +268 -0
- package/skills/cometchat-flutter-v6-calls/references/ringing-integration.md +171 -0
- package/skills/cometchat-flutter-v6-calls/references/server-push-bridge.md +93 -0
- package/skills/cometchat-flutter-v6-calls/references/share-invite.md +102 -0
- package/skills/cometchat-flutter-v6-components/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-conversations/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-core/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-customization/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-events/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-features/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-messages/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-migration/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-placement/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-production/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-push/SKILL.md +452 -0
- package/skills/cometchat-flutter-v6-testing/SKILL.md +319 -0
- package/skills/cometchat-flutter-v6-theming/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-troubleshooting/SKILL.md +1 -1
- package/skills/cometchat-flutter-v6-users-groups/SKILL.md +1 -1
- package/skills/cometchat-i18n/SKILL.md +358 -0
- package/skills/cometchat-ios/SKILL.md +5 -5
- package/skills/cometchat-ios-calls/SKILL.md +409 -0
- package/skills/cometchat-ios-calls/references/add-calls-to-existing-chat.md +172 -0
- package/skills/cometchat-ios-calls/references/avaudiosession-routing.md +189 -0
- package/skills/cometchat-ios-calls/references/call-layouts.md +136 -0
- package/skills/cometchat-ios-calls/references/call-session.md +221 -0
- package/skills/cometchat-ios-calls/references/callkit-and-pushkit.md +353 -0
- package/skills/cometchat-ios-calls/references/custom-ui.md +322 -0
- package/skills/cometchat-ios-calls/references/device-management.md +171 -0
- package/skills/cometchat-ios-calls/references/group-calls.md +396 -0
- package/skills/cometchat-ios-calls/references/idle-timeout.md +185 -0
- package/skills/cometchat-ios-calls/references/in-call-chat.md +183 -0
- package/skills/cometchat-ios-calls/references/migration-v4-to-v5.md +138 -0
- package/skills/cometchat-ios-calls/references/picture-in-picture.md +246 -0
- package/skills/cometchat-ios-calls/references/raise-hand.md +239 -0
- package/skills/cometchat-ios-calls/references/recording-screen-share.md +339 -0
- package/skills/cometchat-ios-calls/references/ringing-integration.md +142 -0
- package/skills/cometchat-ios-calls/references/server-apns-pushkit.md +267 -0
- package/skills/cometchat-ios-calls/references/share-invite.md +152 -0
- package/skills/cometchat-ios-calls/references/swiftui-uikit-hosting.md +246 -0
- package/skills/cometchat-ios-calls/references/voip-cert-setup.md +166 -0
- package/skills/cometchat-ios-components/SKILL.md +1 -1
- package/skills/cometchat-ios-core/SKILL.md +1 -1
- package/skills/cometchat-ios-customization/SKILL.md +1 -1
- package/skills/cometchat-ios-features/SKILL.md +1 -1
- package/skills/cometchat-ios-placement/SKILL.md +1 -1
- package/skills/cometchat-ios-production/SKILL.md +1 -1
- package/skills/cometchat-ios-push/SKILL.md +1 -1
- package/skills/cometchat-ios-testing/SKILL.md +371 -0
- package/skills/cometchat-ios-theming/SKILL.md +1 -1
- package/skills/cometchat-ios-troubleshooting/SKILL.md +1 -1
- package/skills/cometchat-native-bare-patterns/SKILL.md +45 -3
- package/skills/cometchat-native-calls/SKILL.md +599 -0
- package/skills/cometchat-native-calls/references/add-calls-to-existing-chat.md +203 -0
- package/skills/cometchat-native-calls/references/call-layouts.md +124 -0
- package/skills/cometchat-native-calls/references/call-session.md +182 -0
- package/skills/cometchat-native-calls/references/custom-ui.md +250 -0
- package/skills/cometchat-native-calls/references/device-management.md +135 -0
- package/skills/cometchat-native-calls/references/expo-vs-bare.md +179 -0
- package/skills/cometchat-native-calls/references/group-calls.md +291 -0
- package/skills/cometchat-native-calls/references/idle-timeout.md +128 -0
- package/skills/cometchat-native-calls/references/in-call-chat.md +143 -0
- package/skills/cometchat-native-calls/references/migration-v4-to-v5.md +72 -0
- package/skills/cometchat-native-calls/references/picture-in-picture.md +276 -0
- package/skills/cometchat-native-calls/references/raise-hand.md +203 -0
- package/skills/cometchat-native-calls/references/recording-screen-share.md +290 -0
- package/skills/cometchat-native-calls/references/ringing-integration.md +118 -0
- package/skills/cometchat-native-calls/references/server-push-bridge.md +135 -0
- package/skills/cometchat-native-calls/references/server-push-payloads.md +193 -0
- package/skills/cometchat-native-calls/references/share-invite.md +144 -0
- package/skills/cometchat-native-calls/references/voip-push-end-to-end.md +317 -0
- package/skills/cometchat-native-components/SKILL.md +1 -1
- package/skills/cometchat-native-core/SKILL.md +1 -1
- package/skills/cometchat-native-customization/SKILL.md +1 -1
- package/skills/cometchat-native-expo-patterns/SKILL.md +18 -1
- package/skills/cometchat-native-features/SKILL.md +1 -1
- package/skills/cometchat-native-placement/SKILL.md +1 -1
- package/skills/cometchat-native-production/SKILL.md +1 -1
- package/skills/cometchat-native-push/SKILL.md +1 -1
- package/skills/cometchat-native-testing/SKILL.md +1 -1
- package/skills/cometchat-native-theming/SKILL.md +1 -1
- package/skills/cometchat-native-troubleshooting/SKILL.md +1 -1
- package/skills/cometchat-nextjs-patterns/SKILL.md +1 -1
- package/skills/cometchat-placement/SKILL.md +29 -1
- package/skills/cometchat-production/SKILL.md +1 -1
- package/skills/cometchat-react-calls/SKILL.md +539 -0
- package/skills/cometchat-react-calls/references/add-calls-to-existing-chat.md +145 -0
- package/skills/cometchat-react-calls/references/call-layouts.md +161 -0
- package/skills/cometchat-react-calls/references/call-session.md +235 -0
- package/skills/cometchat-react-calls/references/custom-ui.md +201 -0
- package/skills/cometchat-react-calls/references/device-management.md +206 -0
- package/skills/cometchat-react-calls/references/group-calls.md +262 -0
- package/skills/cometchat-react-calls/references/idle-timeout.md +175 -0
- package/skills/cometchat-react-calls/references/in-call-chat.md +212 -0
- package/skills/cometchat-react-calls/references/migration-v4-to-v5.md +172 -0
- package/skills/cometchat-react-calls/references/picture-in-picture.md +245 -0
- package/skills/cometchat-react-calls/references/raise-hand.md +238 -0
- package/skills/cometchat-react-calls/references/recording-screen-share.md +143 -0
- package/skills/cometchat-react-calls/references/ringing-integration.md +255 -0
- package/skills/cometchat-react-calls/references/server-web-push-vapid.md +241 -0
- package/skills/cometchat-react-calls/references/share-invite.md +176 -0
- package/skills/cometchat-react-calls/references/testing-calls-on-web.md +198 -0
- package/skills/cometchat-react-calls/references/voip-and-web-push.md +165 -0
- package/skills/cometchat-react-patterns/SKILL.md +56 -8
- package/skills/cometchat-react-push/SKILL.md +448 -0
- package/skills/cometchat-react-router-patterns/SKILL.md +1 -1
- package/skills/cometchat-react-testing/SKILL.md +404 -0
- package/skills/cometchat-theming/SKILL.md +3 -3
- package/skills/cometchat-troubleshooting/SKILL.md +2 -2
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# Picture-in-Picture on iOS
|
|
2
|
+
|
|
3
|
+
iOS supports PiP for video calls via `AVPictureInPictureController` (iOS 14+) or via CallKit's auto-PiP (iOS 14+ when CallKit reports a video call). For most production calling apps, **CallKit handles PiP for free** — if you've integrated CallKit (rule 1.7 in the SKILL.md), iOS auto-PiPs your call when the user backgrounds the app.
|
|
4
|
+
|
|
5
|
+
The `AVPictureInPictureController` path is only needed for non-CallKit foreground-only calls (rare — most production apps use CallKit).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## CallKit auto-PiP — the simplest path
|
|
10
|
+
|
|
11
|
+
If your app integrates CallKit (rule 1.7, mandatory for VoIP push standalone mode), iOS auto-handles PiP. When the user presses Home during an active call:
|
|
12
|
+
|
|
13
|
+
1. iOS automatically captures the call's video stream
|
|
14
|
+
2. The CallKit-managed call enters PiP mode
|
|
15
|
+
3. The PiP window floats above all apps, even system UI
|
|
16
|
+
4. Tapping the PiP window foregrounds your app
|
|
17
|
+
|
|
18
|
+
No code needed beyond CallKit setup. The skill defaults to this path for standalone-mode calls.
|
|
19
|
+
|
|
20
|
+
### Required entitlements
|
|
21
|
+
|
|
22
|
+
```xml
|
|
23
|
+
<!-- Info.plist — already required by CallKit -->
|
|
24
|
+
<key>UIBackgroundModes</key>
|
|
25
|
+
<array>
|
|
26
|
+
<string>audio</string>
|
|
27
|
+
<string>voip</string>
|
|
28
|
+
<string>remote-notification</string>
|
|
29
|
+
</array>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`audio` background mode is what lets the call's audio continue while in PiP. Without it, video PiP works but audio cuts.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## SDK-managed PiP layout — `CallSession.shared.enablePictureInPictureLayout()`
|
|
37
|
+
|
|
38
|
+
> **Audit 2026-05-14, confirmed against `https://www.cometchat.com/docs/calls/ios/picture-in-picture.md`:** The iOS Calls SDK v5.x exposes two methods to toggle the SDK's INTERNAL compact layout — it does NOT expose video frames or a `CMSampleBuffer` pipeline to drive a host-app `AVPictureInPictureController`. An earlier draft of this doc cited `CometChatCalls.attachPictureInPictureLayer(layer)` / `detachPictureInPictureLayer()` — **those symbols do not exist on CometChatCallsSDK v5.x**.
|
|
39
|
+
|
|
40
|
+
### What works today
|
|
41
|
+
|
|
42
|
+
| Goal | API |
|
|
43
|
+
|---|---|
|
|
44
|
+
| Switch the SDK's call surface to compact "PiP-style" layout | `CallSession.shared.enablePictureInPictureLayout()` |
|
|
45
|
+
| Switch back to full layout | `CallSession.shared.disablePictureInPictureLayout()` |
|
|
46
|
+
|
|
47
|
+
```swift
|
|
48
|
+
import CometChatCallsSDK
|
|
49
|
+
|
|
50
|
+
// When the user taps "minimize" in the call UI
|
|
51
|
+
CallSession.shared.enablePictureInPictureLayout()
|
|
52
|
+
|
|
53
|
+
// When they tap to expand back, or before hangup
|
|
54
|
+
CallSession.shared.disablePictureInPictureLayout()
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
This is **NOT iOS system-level PiP** (the floating window that survives backgrounding). It's an in-app compact layout. For true OS-level PiP (floating call window across all apps + lock screen), see the CallKit path above.
|
|
58
|
+
|
|
59
|
+
### Custom system-level PiP via `AVPictureInPictureController` — NOT directly supported
|
|
60
|
+
|
|
61
|
+
Driving iOS system-level PiP via `AVPictureInPictureController` requires feeding video frames to an `AVSampleBufferDisplayLayer`. The Calls SDK does not expose its WebRTC video track for this — the peer-connection handle is internal. Three options:
|
|
62
|
+
|
|
63
|
+
1. **Recommended: use CallKit** (rule 1.7 SKILL.md). CallKit-managed VoIP calls get auto-PiP for free — no `AVPictureInPictureController` plumbing needed.
|
|
64
|
+
2. **Use the SDK's compact layout API** (above) for in-app minimization. Acceptable UX for foreground-only flows.
|
|
65
|
+
3. **Roll your own WebRTC bridge.** Requires forking/patching the SDK to expose the underlying `RTCMediaStream` — not recommended.
|
|
66
|
+
|
|
67
|
+
### Required capability (CallKit path)
|
|
68
|
+
|
|
69
|
+
In Xcode → Signing & Capabilities → Background Modes → ensure **Audio, AirPlay, and Picture in Picture** is checked. CallKit needs this; without it, CallKit's auto-PiP doesn't fire even with a CallKit-managed call.
|
|
70
|
+
|
|
71
|
+
### Toggle button for SDK compact layout
|
|
72
|
+
|
|
73
|
+
```swift
|
|
74
|
+
@IBAction func toggleCompactLayout(_ sender: UIButton) {
|
|
75
|
+
if isCompact {
|
|
76
|
+
CallSession.shared.disablePictureInPictureLayout()
|
|
77
|
+
} else {
|
|
78
|
+
CallSession.shared.enablePictureInPictureLayout()
|
|
79
|
+
}
|
|
80
|
+
isCompact.toggle()
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
This switches the SDK's call surface between full and compact layouts. The compact view shows local + remote video tiles in a smaller container; useful when you want to free screen space for other UI but keep the call visible.
|
|
85
|
+
|
|
86
|
+
> **Note**: `AVPictureInPictureController` + KVO on `isPictureInPicturePossible` patterns (previously documented here) are not applicable because the SDK does not expose the video frames `AVPictureInPictureController` requires. Use the CallKit path for system-level PiP, or this compact-layout API for in-app PiP-style minimization.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## SwiftUI integration
|
|
91
|
+
|
|
92
|
+
Wrap the UIKit view controller as a `UIViewControllerRepresentable`:
|
|
93
|
+
|
|
94
|
+
```swift
|
|
95
|
+
struct PiPCallView: UIViewControllerRepresentable {
|
|
96
|
+
let sessionID: String
|
|
97
|
+
|
|
98
|
+
func makeUIViewController(context: Context) -> CustomOngoingCallViewController {
|
|
99
|
+
let vc = CustomOngoingCallViewController(sessionID: sessionID)
|
|
100
|
+
return vc
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
func updateUIViewController(_ vc: CustomOngoingCallViewController, context: Context) {}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Use in your SwiftUI app:
|
|
108
|
+
|
|
109
|
+
```swift
|
|
110
|
+
struct CallScreen: View {
|
|
111
|
+
let sessionID: String
|
|
112
|
+
|
|
113
|
+
var body: some View {
|
|
114
|
+
PiPCallView(sessionID: sessionID)
|
|
115
|
+
.ignoresSafeArea()
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Auto-PiP on backgrounding (manual path)
|
|
123
|
+
|
|
124
|
+
iOS 14+ supports auto-PiP from inline AVKit playback. For active calls:
|
|
125
|
+
|
|
126
|
+
```swift
|
|
127
|
+
pipController?.canStartPictureInPictureAutomaticallyFromInline = true
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Combined with the `audio` background mode, this gives you the same behavior as CallKit auto-PiP: press Home → call enters PiP → audio + video continue.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Delegate methods
|
|
135
|
+
|
|
136
|
+
```swift
|
|
137
|
+
extension CustomOngoingCallViewController: AVPictureInPictureControllerDelegate {
|
|
138
|
+
func pictureInPictureControllerWillStartPictureInPicture(_ pip: AVPictureInPictureController) {
|
|
139
|
+
// Hide your in-page video (it duplicates with PiP)
|
|
140
|
+
sampleBufferDisplayLayer?.isHidden = true
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
func pictureInPictureControllerDidStartPictureInPicture(_ pip: AVPictureInPictureController) {
|
|
144
|
+
// PiP is active
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
func pictureInPictureController(_ pip: AVPictureInPictureController,
|
|
148
|
+
failedToStartPictureInPictureWithError error: Error) {
|
|
149
|
+
print("PiP failed:", error)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
func pictureInPictureControllerDidStopPictureInPicture(_ pip: AVPictureInPictureController) {
|
|
153
|
+
sampleBufferDisplayLayer?.isHidden = false
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
func pictureInPictureController(_ pip: AVPictureInPictureController,
|
|
157
|
+
restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completion: @escaping (Bool) -> Void) {
|
|
158
|
+
// User tapped the PiP window — restore the full call screen
|
|
159
|
+
if let nav = self.navigationController {
|
|
160
|
+
nav.popToRootViewController(animated: true)
|
|
161
|
+
}
|
|
162
|
+
completion(true)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
`restoreUserInterfaceForPictureInPictureStopWithCompletionHandler` is critical — without it, tapping the PiP window restores nothing.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Lock screen + PiP
|
|
172
|
+
|
|
173
|
+
When the device is locked during a call:
|
|
174
|
+
|
|
175
|
+
- **CallKit-managed call:** PiP appears on the lock screen as part of the CallKit incoming-call card. Audio continues; video shows in the lock-screen UI.
|
|
176
|
+
- **Manual `AVPictureInPictureController`:** PiP cannot show on the lock screen. The video pauses; audio continues if `audio` background mode is set.
|
|
177
|
+
|
|
178
|
+
Standalone-mode VoIP apps must use CallKit for the lock-screen video story to work.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Hangup cleanup
|
|
183
|
+
|
|
184
|
+
When the call ends:
|
|
185
|
+
|
|
186
|
+
```swift
|
|
187
|
+
func endCall() {
|
|
188
|
+
// Switch SDK back to default layout BEFORE ending the session, otherwise
|
|
189
|
+
// a re-init of the SDK can inherit compact-layout state on the next call.
|
|
190
|
+
CallSession.shared.disablePictureInPictureLayout()
|
|
191
|
+
|
|
192
|
+
CallSession.shared.leaveSession() // ends the WebRTC session
|
|
193
|
+
|
|
194
|
+
do {
|
|
195
|
+
try AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
|
|
196
|
+
} catch {}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
If you ALSO drove a host-app `AVPictureInPictureController` (against an external video source), call `pipController?.stopPictureInPicture()` before tearing down. The CometChat SDK has no awareness of that controller.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Anti-patterns
|
|
205
|
+
|
|
206
|
+
1. **Using `CometChatCalls.attachPictureInPictureLayer(layer)` / `detachPictureInPictureLayer()`** — these symbols **do not exist** on `CometChatCallsSDK` v5.x. Confirmed against the SDK docs 2026-05-14. Use `CallSession.shared.enable/disablePictureInPictureLayout()` instead.
|
|
207
|
+
2. **Manual `AVPictureInPictureController` for VoIP apps without CallKit.** Lock-screen ringing breaks; the SDK does not expose its video pipeline so the PiP window won't render the call. Use CallKit.
|
|
208
|
+
3. **No `audio` background mode.** Audio cuts when the app backgrounds, regardless of PiP path.
|
|
209
|
+
4. **Mixing SDK compact-layout with host-driven `AVPictureInPictureController`.** Two competing PiP surfaces — the SDK's compact layout fights the user's manual PiP, both look broken. Pick one.
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Verification checklist
|
|
214
|
+
|
|
215
|
+
- [ ] CallKit integrated (rule 1.7 in SKILL.md) — gives you PiP for free
|
|
216
|
+
- [ ] `audio` in `UIBackgroundModes` (Info.plist)
|
|
217
|
+
- [ ] Background Modes capability includes "Audio, AirPlay, and Picture in Picture"
|
|
218
|
+
|
|
219
|
+
**SDK compact-layout path (in-app PiP, no system-level PiP):**
|
|
220
|
+
- [ ] `CallSession.shared.enablePictureInPictureLayout()` called to enter compact mode
|
|
221
|
+
- [ ] `CallSession.shared.disablePictureInPictureLayout()` called BEFORE `leaveSession()` on hangup
|
|
222
|
+
- [ ] UI button to toggle between full/compact layouts
|
|
223
|
+
- [ ] NOT used together with a host-app `AVPictureInPictureController` (see anti-pattern #4)
|
|
224
|
+
|
|
225
|
+
**Real-device smoke (CallKit path):**
|
|
226
|
+
- [ ] Press Home during call → PiP appears (auto via CallKit)
|
|
227
|
+
- [ ] Audio continues through PiP transition
|
|
228
|
+
- [ ] Tap PiP window → app foregrounds, full call screen restored
|
|
229
|
+
- [ ] Lock device → PiP visible on lock screen (CallKit only)
|
|
230
|
+
- [ ] Hangup ends call cleanly; no stuck PiP window
|
|
231
|
+
|
|
232
|
+
**Real-device smoke (SDK compact-layout path):**
|
|
233
|
+
- [ ] Tap "minimize" → SDK switches to compact layout in-app
|
|
234
|
+
- [ ] Tap "expand" → SDK switches back to full layout
|
|
235
|
+
- [ ] Hangup from compact layout cleanly ends call
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Pointers
|
|
240
|
+
|
|
241
|
+
- `references/callkit-and-pushkit.md` — CallKit gives PiP for free
|
|
242
|
+
- `references/avaudiosession-routing.md` — audio routing during PiP transitions
|
|
243
|
+
- `references/swiftui-uikit-hosting.md` — SwiftUI hosting of the UIKit PiP view controller
|
|
244
|
+
- `cometchat-native-calls/references/picture-in-picture.md` — sister reference (RN bridges to the same iOS APIs)
|
|
245
|
+
- `cometchat-react-calls/references/picture-in-picture.md` — Web PiP (different APIs but same UX shape)
|
|
246
|
+
- `cometchat-ios-calls` SKILL.md — base hard rules
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Raise hand on iOS
|
|
2
|
+
|
|
3
|
+
Native CometChatCallsSDK exposes the same conceptual API as the JS SDK — `raiseHand()` / `lowerHand()` plus delegate callbacks. The wow piece is wiring it into UIKit / SwiftUI cleanly + the system-level UX (CallKit doesn't show raised-hand state on the lock screen, so the in-app surface is the canonical place).
|
|
4
|
+
|
|
5
|
+
**Canonical docs:** https://www.cometchat.com/docs/calls/ios/raise-hand
|
|
6
|
+
**Read first:** `cometchat-react-calls/references/raise-hand.md` — UX shape + anti-patterns are identical; this reference is the iOS-specific Swift wiring.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## SDK API
|
|
11
|
+
|
|
12
|
+
```swift
|
|
13
|
+
import CometChatCallsSDK
|
|
14
|
+
|
|
15
|
+
// Local user
|
|
16
|
+
CometChatCalls.raiseHand()
|
|
17
|
+
CometChatCalls.lowerHand()
|
|
18
|
+
|
|
19
|
+
// Listener — set via the call event listener attached at session start
|
|
20
|
+
class CallListener: NSObject, CometChatCallsEventsListener {
|
|
21
|
+
func onParticipantHandRaised(_ participant: Participant) {
|
|
22
|
+
// participant.uid, participant.name
|
|
23
|
+
}
|
|
24
|
+
func onParticipantHandLowered(_ participant: Participant) {
|
|
25
|
+
// ...
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Settings flag
|
|
30
|
+
let settings = CallSettingsBuilder()
|
|
31
|
+
.setSessionType(.video)
|
|
32
|
+
.hideRaiseHandButton(true)
|
|
33
|
+
.build()
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## SwiftUI integration — observable state holder
|
|
39
|
+
|
|
40
|
+
```swift
|
|
41
|
+
import SwiftUI
|
|
42
|
+
import Combine
|
|
43
|
+
import CometChatCallsSDK
|
|
44
|
+
|
|
45
|
+
struct RaisedParticipant: Identifiable {
|
|
46
|
+
let id: String // uid
|
|
47
|
+
let name: String
|
|
48
|
+
let raisedAt: Date
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
final class RaiseHandState: ObservableObject {
|
|
52
|
+
@Published var localRaised: Bool = false
|
|
53
|
+
@Published var raised: [RaisedParticipant] = []
|
|
54
|
+
|
|
55
|
+
func toggle() {
|
|
56
|
+
if localRaised {
|
|
57
|
+
CometChatCalls.lowerHand()
|
|
58
|
+
} else {
|
|
59
|
+
CometChatCalls.raiseHand()
|
|
60
|
+
}
|
|
61
|
+
localRaised.toggle()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
func handle(participantRaised p: Participant) {
|
|
65
|
+
DispatchQueue.main.async {
|
|
66
|
+
let entry = RaisedParticipant(id: p.uid, name: p.name, raisedAt: Date())
|
|
67
|
+
self.raised.removeAll { $0.id == p.uid }
|
|
68
|
+
self.raised.append(entry)
|
|
69
|
+
self.raised.sort { $0.raisedAt < $1.raisedAt }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
func handle(participantLowered p: Participant) {
|
|
74
|
+
DispatchQueue.main.async {
|
|
75
|
+
self.raised.removeAll { $0.id == p.uid }
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`DispatchQueue.main.async` wraps SDK callbacks — same rule as elsewhere in iOS calls integrations (cf. `references/group-calls.md`). Without it, `@Published` updates from a background queue cause UI thread warnings + skipped re-renders.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Toggle button (SwiftUI)
|
|
86
|
+
|
|
87
|
+
```swift
|
|
88
|
+
struct RaiseHandButton: View {
|
|
89
|
+
@ObservedObject var state: RaiseHandState
|
|
90
|
+
|
|
91
|
+
var body: some View {
|
|
92
|
+
Button(action: state.toggle) {
|
|
93
|
+
Label(state.localRaised ? "Lower" : "Raise hand",
|
|
94
|
+
systemImage: state.localRaised ? "hand.raised.fill" : "hand.raised")
|
|
95
|
+
}
|
|
96
|
+
.buttonStyle(.bordered)
|
|
97
|
+
.tint(state.localRaised ? .yellow : .white)
|
|
98
|
+
.accessibilityLabel(state.localRaised ? "Lower hand" : "Raise hand")
|
|
99
|
+
.accessibilityAddTraits(state.localRaised ? .isSelected : [])
|
|
100
|
+
.onChange(of: state.localRaised) { newValue in
|
|
101
|
+
UIAccessibility.post(notification: .announcement, argument:
|
|
102
|
+
newValue ? "Hand raised" : "Hand lowered")
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`UIAccessibility.post(.announcement, ...)` is the iOS equivalent of web's `aria-live` and RN's `AccessibilityInfo.announceForAccessibility`. VoiceOver picks it up.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Raised-hands sheet (SwiftUI)
|
|
113
|
+
|
|
114
|
+
```swift
|
|
115
|
+
struct RaisedHandsSheet: View {
|
|
116
|
+
@ObservedObject var state: RaiseHandState
|
|
117
|
+
|
|
118
|
+
var body: some View {
|
|
119
|
+
VStack(alignment: .leading, spacing: 12) {
|
|
120
|
+
Text("Raised hands (\(state.raised.count))")
|
|
121
|
+
.font(.headline)
|
|
122
|
+
if state.raised.isEmpty {
|
|
123
|
+
Text("No hands raised").foregroundColor(.secondary)
|
|
124
|
+
} else {
|
|
125
|
+
ForEach(state.raised) { p in
|
|
126
|
+
HStack {
|
|
127
|
+
Text("✋").font(.title2)
|
|
128
|
+
Text(p.name).font(.body)
|
|
129
|
+
Spacer()
|
|
130
|
+
Text(secondsAgo(p.raisedAt))
|
|
131
|
+
.font(.caption)
|
|
132
|
+
.foregroundColor(.secondary)
|
|
133
|
+
}
|
|
134
|
+
.padding(.vertical, 4)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
.padding()
|
|
139
|
+
.presentationDetents([.fraction(0.3), .medium])
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
func secondsAgo(_ t: Date) -> String {
|
|
143
|
+
"\(Int(Date().timeIntervalSince(t)))s ago"
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`.presentationDetents([.fraction(0.3), .medium])` is iOS 16+ — gives the user two snap heights, matching the BottomSheet pattern from RN.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## UIKit pattern
|
|
153
|
+
|
|
154
|
+
For UIKit projects (the kit's default surface):
|
|
155
|
+
|
|
156
|
+
```swift
|
|
157
|
+
class CallViewController: UIViewController {
|
|
158
|
+
private let state = RaiseHandState()
|
|
159
|
+
|
|
160
|
+
override func viewDidLoad() {
|
|
161
|
+
super.viewDidLoad()
|
|
162
|
+
// ... attach state to call event listener at session start
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
@objc private func raiseHandTapped() {
|
|
166
|
+
state.toggle()
|
|
167
|
+
// Update the UIBarButtonItem appearance
|
|
168
|
+
raiseHandButton.image = UIImage(systemName: state.localRaised ? "hand.raised.fill" : "hand.raised")
|
|
169
|
+
raiseHandButton.tintColor = state.localRaised ? .systemYellow : .label
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
func presentRaisedHandsSheet() {
|
|
173
|
+
let host = UIHostingController(rootView: RaisedHandsSheet(state: state))
|
|
174
|
+
if #available(iOS 15.0, *) {
|
|
175
|
+
host.sheetPresentationController?.detents = [.medium(), .large()]
|
|
176
|
+
}
|
|
177
|
+
present(host, animated: true)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
`UIHostingController(rootView:)` lets you embed a SwiftUI sheet in a UIKit nav stack. `sheetPresentationController.detents` is the iOS 15+ pull-up sheet API.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Toast announcement for the host
|
|
187
|
+
|
|
188
|
+
iOS has no native toast — use `UNUserNotificationCenter` for an in-call banner-style notification, OR a custom view:
|
|
189
|
+
|
|
190
|
+
```swift
|
|
191
|
+
extension RaiseHandState {
|
|
192
|
+
func showRaisedNotification(for p: Participant) {
|
|
193
|
+
let banner = UIView()
|
|
194
|
+
banner.backgroundColor = .systemYellow.withAlphaComponent(0.95)
|
|
195
|
+
let label = UILabel()
|
|
196
|
+
label.text = "✋ \(p.name) raised their hand"
|
|
197
|
+
label.textColor = .black
|
|
198
|
+
label.font = .systemFont(ofSize: 14, weight: .medium)
|
|
199
|
+
// ...add to view, animate down from top, dismiss after 4s
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Or use a third-party lib like `NotificationBannerSwift`. Custom is fine; just don't use `UNUserNotificationCenter` (which is system-level and feels out of place for in-call signals).
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Anti-patterns
|
|
209
|
+
|
|
210
|
+
Web sister reference rules apply, plus iOS-specific:
|
|
211
|
+
|
|
212
|
+
1. **`@Published` updates from non-main queue.** SwiftUI warns "publishing changes from background threads is not allowed." Always wrap SDK callbacks in `DispatchQueue.main.async`.
|
|
213
|
+
2. **Forgetting `accessibilityAddTraits(.isSelected)` on the toggle button.** VoiceOver doesn't announce "selected" state without it.
|
|
214
|
+
3. **Using `UNUserNotificationCenter`** for in-call raise-hand alerts. System-level; feels wrong. Use a custom banner.
|
|
215
|
+
4. **`removeAll { $0.id == p.uid }` then `append` without sort.** When a participant raises again after lowering, position in queue should reset to "now"; current code already does the right thing, but easy to break by reordering operations.
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Verification checklist
|
|
220
|
+
|
|
221
|
+
- [ ] `RaiseHandState` is an `ObservableObject`
|
|
222
|
+
- [ ] All SDK callbacks wrap in `DispatchQueue.main.async`
|
|
223
|
+
- [ ] Toggle button has `accessibilityLabel` + `accessibilityAddTraits(.isSelected)` when raised
|
|
224
|
+
- [ ] `UIAccessibility.post(.announcement, ...)` on toggle
|
|
225
|
+
- [ ] Raised-hands list sorted by `raisedAt` ascending
|
|
226
|
+
- [ ] `hideRaiseHandButton: true` in CallSettings if custom UI
|
|
227
|
+
- [ ] Real-device smoke: 3 iPhones in same call, host sees both raised
|
|
228
|
+
- [ ] VoiceOver smoke: enable VoiceOver, raise hand, hear "Hand raised" announcement
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Pointers
|
|
233
|
+
|
|
234
|
+
- `cometchat-react-calls/references/raise-hand.md` — sister reference (UX shape)
|
|
235
|
+
- `cometchat-ios-calls` SKILL.md — the seven hard rules
|
|
236
|
+
- `references/group-calls.md` — group call iOS architecture
|
|
237
|
+
- `references/swiftui-uikit-hosting.md` — embedding SwiftUI sheets in UIKit
|
|
238
|
+
- `cometchat-a11y` — UIAccessibility patterns
|
|
239
|
+
- Canonical docs: https://www.cometchat.com/docs/calls/ios/raise-hand
|