@cometchat/skills 4.1.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.
Files changed (257) hide show
  1. package/bin/install.js +298 -41
  2. package/package.json +1 -1
  3. package/skills/cometchat/SKILL.md +146 -27
  4. package/skills/cometchat-a11y/SKILL.md +476 -0
  5. package/skills/cometchat-android-v5/SKILL.md +1 -1
  6. package/skills/cometchat-android-v5-calls/SKILL.md +413 -0
  7. package/skills/cometchat-android-v5-calls/references/README.md +53 -0
  8. package/skills/cometchat-android-v5-calls/references/add-calls-to-existing-chat.md +195 -0
  9. package/skills/cometchat-android-v5-calls/references/audio-controls.md +91 -0
  10. package/skills/cometchat-android-v5-calls/references/background-handling.md +168 -0
  11. package/skills/cometchat-android-v5-calls/references/call-layouts.md +123 -0
  12. package/skills/cometchat-android-v5-calls/references/call-logs.md +113 -0
  13. package/skills/cometchat-android-v5-calls/references/call-session.md +274 -0
  14. package/skills/cometchat-android-v5-calls/references/custom-ui.md +150 -0
  15. package/skills/cometchat-android-v5-calls/references/event-listeners.md +123 -0
  16. package/skills/cometchat-android-v5-calls/references/group-calls.md +205 -0
  17. package/skills/cometchat-android-v5-calls/references/idle-timeout.md +111 -0
  18. package/skills/cometchat-android-v5-calls/references/in-call-chat.md +134 -0
  19. package/skills/cometchat-android-v5-calls/references/join-session.md +120 -0
  20. package/skills/cometchat-android-v5-calls/references/migration-v4-to-v5.md +206 -0
  21. package/skills/cometchat-android-v5-calls/references/participant-management.md +110 -0
  22. package/skills/cometchat-android-v5-calls/references/picture-in-picture.md +106 -0
  23. package/skills/cometchat-android-v5-calls/references/raise-hand.md +242 -0
  24. package/skills/cometchat-android-v5-calls/references/recording.md +101 -0
  25. package/skills/cometchat-android-v5-calls/references/ringing-integration.md +152 -0
  26. package/skills/cometchat-android-v5-calls/references/screen-sharing.md +82 -0
  27. package/skills/cometchat-android-v5-calls/references/server-fcm-voip.md +215 -0
  28. package/skills/cometchat-android-v5-calls/references/session-settings.md +121 -0
  29. package/skills/cometchat-android-v5-calls/references/setup.md +137 -0
  30. package/skills/cometchat-android-v5-calls/references/share-invite.md +151 -0
  31. package/skills/cometchat-android-v5-calls/references/video-controls.md +87 -0
  32. package/skills/cometchat-android-v5-calls/references/voip-calling.md +526 -0
  33. package/skills/cometchat-android-v5-components/SKILL.md +1 -1
  34. package/skills/cometchat-android-v5-core/SKILL.md +1 -1
  35. package/skills/cometchat-android-v5-customization/SKILL.md +1 -1
  36. package/skills/cometchat-android-v5-extensions/SKILL.md +1 -1
  37. package/skills/cometchat-android-v5-features/SKILL.md +1 -1
  38. package/skills/cometchat-android-v5-placement/SKILL.md +1 -1
  39. package/skills/cometchat-android-v5-production/SKILL.md +1 -1
  40. package/skills/cometchat-android-v5-push/SKILL.md +1 -1
  41. package/skills/cometchat-android-v5-testing/SKILL.md +1 -1
  42. package/skills/cometchat-android-v5-theming/SKILL.md +1 -1
  43. package/skills/cometchat-android-v5-troubleshooting/SKILL.md +1 -1
  44. package/skills/cometchat-android-v6/SKILL.md +1 -1
  45. package/skills/cometchat-android-v6-builder-settings/SKILL.md +1 -1
  46. package/skills/cometchat-android-v6-calls/SKILL.md +375 -0
  47. package/skills/cometchat-android-v6-calls/references/add-calls-to-existing-chat.md +97 -0
  48. package/skills/cometchat-android-v6-calls/references/call-layouts.md +91 -0
  49. package/skills/cometchat-android-v6-calls/references/call-session.md +163 -0
  50. package/skills/cometchat-android-v6-calls/references/device-management.md +171 -0
  51. package/skills/cometchat-android-v6-calls/references/group-calls.md +191 -0
  52. package/skills/cometchat-android-v6-calls/references/idle-timeout.md +107 -0
  53. package/skills/cometchat-android-v6-calls/references/in-call-chat.md +136 -0
  54. package/skills/cometchat-android-v6-calls/references/raise-hand.md +186 -0
  55. package/skills/cometchat-android-v6-calls/references/ringing-integration.md +176 -0
  56. package/skills/cometchat-android-v6-calls/references/server-fcm-voip.md +127 -0
  57. package/skills/cometchat-android-v6-calls/references/share-invite.md +105 -0
  58. package/skills/cometchat-android-v6-compose-components/SKILL.md +2 -2
  59. package/skills/cometchat-android-v6-compose-customization/SKILL.md +1 -1
  60. package/skills/cometchat-android-v6-compose-placement/SKILL.md +1 -1
  61. package/skills/cometchat-android-v6-compose-theming/SKILL.md +1 -1
  62. package/skills/cometchat-android-v6-core/SKILL.md +1 -1
  63. package/skills/cometchat-android-v6-events/SKILL.md +1 -1
  64. package/skills/cometchat-android-v6-extensions/SKILL.md +1 -1
  65. package/skills/cometchat-android-v6-features/SKILL.md +1 -1
  66. package/skills/cometchat-android-v6-kotlin-components/SKILL.md +1 -1
  67. package/skills/cometchat-android-v6-kotlin-customization/SKILL.md +1 -1
  68. package/skills/cometchat-android-v6-kotlin-placement/SKILL.md +1 -1
  69. package/skills/cometchat-android-v6-kotlin-theming/SKILL.md +1 -1
  70. package/skills/cometchat-android-v6-migration/SKILL.md +398 -0
  71. package/skills/cometchat-android-v6-production/SKILL.md +1 -1
  72. package/skills/cometchat-android-v6-push/SKILL.md +1 -1
  73. package/skills/cometchat-android-v6-testing/SKILL.md +1 -1
  74. package/skills/cometchat-android-v6-troubleshooting/SKILL.md +1 -1
  75. package/skills/cometchat-angular-calls/SKILL.md +511 -0
  76. package/skills/cometchat-angular-calls/references/add-calls-to-existing-chat.md +95 -0
  77. package/skills/cometchat-angular-calls/references/call-layouts.md +106 -0
  78. package/skills/cometchat-angular-calls/references/call-session.md +167 -0
  79. package/skills/cometchat-angular-calls/references/custom-ui.md +231 -0
  80. package/skills/cometchat-angular-calls/references/device-management.md +135 -0
  81. package/skills/cometchat-angular-calls/references/group-calls.md +412 -0
  82. package/skills/cometchat-angular-calls/references/idle-timeout.md +145 -0
  83. package/skills/cometchat-angular-calls/references/in-call-chat.md +148 -0
  84. package/skills/cometchat-angular-calls/references/lazy-loading-pitfalls.md +205 -0
  85. package/skills/cometchat-angular-calls/references/migration-v4-to-v5.md +53 -0
  86. package/skills/cometchat-angular-calls/references/ngzone-and-async-callbacks.md +183 -0
  87. package/skills/cometchat-angular-calls/references/picture-in-picture.md +285 -0
  88. package/skills/cometchat-angular-calls/references/raise-hand.md +167 -0
  89. package/skills/cometchat-angular-calls/references/recording-screen-share.md +267 -0
  90. package/skills/cometchat-angular-calls/references/ringing-integration.md +131 -0
  91. package/skills/cometchat-angular-calls/references/server-web-push-vapid.md +100 -0
  92. package/skills/cometchat-angular-calls/references/share-invite.md +116 -0
  93. package/skills/cometchat-angular-components/SKILL.md +1 -1
  94. package/skills/cometchat-angular-core/SKILL.md +1 -1
  95. package/skills/cometchat-angular-customization/SKILL.md +1 -1
  96. package/skills/cometchat-angular-features/SKILL.md +1 -1
  97. package/skills/cometchat-angular-patterns/SKILL.md +1 -1
  98. package/skills/cometchat-angular-placement/SKILL.md +1 -1
  99. package/skills/cometchat-angular-production/SKILL.md +1 -1
  100. package/skills/cometchat-angular-push/SKILL.md +403 -0
  101. package/skills/cometchat-angular-testing/SKILL.md +333 -0
  102. package/skills/cometchat-angular-theming/SKILL.md +1 -1
  103. package/skills/cometchat-angular-troubleshooting/SKILL.md +1 -1
  104. package/skills/cometchat-astro-patterns/SKILL.md +1 -1
  105. package/skills/cometchat-calls/SKILL.md +358 -0
  106. package/skills/cometchat-calls/references/use-case-broadcast.md +194 -0
  107. package/skills/cometchat-calls/references/use-case-marketplace.md +158 -0
  108. package/skills/cometchat-calls/references/use-case-support.md +190 -0
  109. package/skills/cometchat-calls/references/use-case-team.md +169 -0
  110. package/skills/cometchat-calls/references/use-case-telehealth.md +154 -0
  111. package/skills/cometchat-components/SKILL.md +1 -1
  112. package/skills/cometchat-core/SKILL.md +1 -1
  113. package/skills/cometchat-customization/SKILL.md +2 -2
  114. package/skills/cometchat-features/SKILL.md +2 -2
  115. package/skills/cometchat-flutter-v5/SKILL.md +1 -1
  116. package/skills/cometchat-flutter-v5-calls/SKILL.md +325 -94
  117. package/skills/cometchat-flutter-v5-calls/references/add-calls-to-existing-chat.md +169 -0
  118. package/skills/cometchat-flutter-v5-calls/references/call-layouts.md +98 -0
  119. package/skills/cometchat-flutter-v5-calls/references/call-session.md +330 -0
  120. package/skills/cometchat-flutter-v5-calls/references/device-management.md +120 -0
  121. package/skills/cometchat-flutter-v5-calls/references/group-calls.md +187 -0
  122. package/skills/cometchat-flutter-v5-calls/references/idle-timeout.md +97 -0
  123. package/skills/cometchat-flutter-v5-calls/references/in-call-chat.md +132 -0
  124. package/skills/cometchat-flutter-v5-calls/references/migration-v4-to-v5.md +160 -0
  125. package/skills/cometchat-flutter-v5-calls/references/raise-hand.md +244 -0
  126. package/skills/cometchat-flutter-v5-calls/references/ringing-integration.md +193 -0
  127. package/skills/cometchat-flutter-v5-calls/references/server-push-bridge.md +101 -0
  128. package/skills/cometchat-flutter-v5-calls/references/share-invite.md +142 -0
  129. package/skills/cometchat-flutter-v5-conversations/SKILL.md +1 -1
  130. package/skills/cometchat-flutter-v5-core/SKILL.md +1 -1
  131. package/skills/cometchat-flutter-v5-customization/SKILL.md +1 -1
  132. package/skills/cometchat-flutter-v5-events/SKILL.md +1 -1
  133. package/skills/cometchat-flutter-v5-messages/SKILL.md +1 -1
  134. package/skills/cometchat-flutter-v5-production/SKILL.md +1 -1
  135. package/skills/cometchat-flutter-v5-push/SKILL.md +1 -1
  136. package/skills/cometchat-flutter-v5-testing/SKILL.md +390 -0
  137. package/skills/cometchat-flutter-v5-theming/SKILL.md +1 -1
  138. package/skills/cometchat-flutter-v5-troubleshooting/SKILL.md +1 -1
  139. package/skills/cometchat-flutter-v5-users-groups/SKILL.md +1 -1
  140. package/skills/cometchat-flutter-v6/SKILL.md +1 -1
  141. package/skills/cometchat-flutter-v6-calls/SKILL.md +308 -243
  142. package/skills/cometchat-flutter-v6-calls/references/add-calls-to-existing-chat.md +134 -0
  143. package/skills/cometchat-flutter-v6-calls/references/call-layouts.md +102 -0
  144. package/skills/cometchat-flutter-v6-calls/references/call-session.md +229 -0
  145. package/skills/cometchat-flutter-v6-calls/references/device-management.md +99 -0
  146. package/skills/cometchat-flutter-v6-calls/references/group-calls.md +190 -0
  147. package/skills/cometchat-flutter-v6-calls/references/idle-timeout.md +80 -0
  148. package/skills/cometchat-flutter-v6-calls/references/in-call-chat.md +122 -0
  149. package/skills/cometchat-flutter-v6-calls/references/raise-hand.md +268 -0
  150. package/skills/cometchat-flutter-v6-calls/references/ringing-integration.md +171 -0
  151. package/skills/cometchat-flutter-v6-calls/references/server-push-bridge.md +93 -0
  152. package/skills/cometchat-flutter-v6-calls/references/share-invite.md +102 -0
  153. package/skills/cometchat-flutter-v6-components/SKILL.md +1 -1
  154. package/skills/cometchat-flutter-v6-conversations/SKILL.md +1 -1
  155. package/skills/cometchat-flutter-v6-core/SKILL.md +1 -1
  156. package/skills/cometchat-flutter-v6-customization/SKILL.md +1 -1
  157. package/skills/cometchat-flutter-v6-events/SKILL.md +1 -1
  158. package/skills/cometchat-flutter-v6-features/SKILL.md +1 -1
  159. package/skills/cometchat-flutter-v6-messages/SKILL.md +1 -1
  160. package/skills/cometchat-flutter-v6-migration/SKILL.md +1 -1
  161. package/skills/cometchat-flutter-v6-placement/SKILL.md +1 -1
  162. package/skills/cometchat-flutter-v6-production/SKILL.md +1 -1
  163. package/skills/cometchat-flutter-v6-push/SKILL.md +452 -0
  164. package/skills/cometchat-flutter-v6-testing/SKILL.md +319 -0
  165. package/skills/cometchat-flutter-v6-theming/SKILL.md +1 -1
  166. package/skills/cometchat-flutter-v6-troubleshooting/SKILL.md +1 -1
  167. package/skills/cometchat-flutter-v6-users-groups/SKILL.md +1 -1
  168. package/skills/cometchat-i18n/SKILL.md +358 -0
  169. package/skills/cometchat-ios/SKILL.md +5 -5
  170. package/skills/cometchat-ios-calls/SKILL.md +409 -0
  171. package/skills/cometchat-ios-calls/references/add-calls-to-existing-chat.md +172 -0
  172. package/skills/cometchat-ios-calls/references/avaudiosession-routing.md +189 -0
  173. package/skills/cometchat-ios-calls/references/call-layouts.md +136 -0
  174. package/skills/cometchat-ios-calls/references/call-session.md +221 -0
  175. package/skills/cometchat-ios-calls/references/callkit-and-pushkit.md +353 -0
  176. package/skills/cometchat-ios-calls/references/custom-ui.md +322 -0
  177. package/skills/cometchat-ios-calls/references/device-management.md +171 -0
  178. package/skills/cometchat-ios-calls/references/group-calls.md +396 -0
  179. package/skills/cometchat-ios-calls/references/idle-timeout.md +185 -0
  180. package/skills/cometchat-ios-calls/references/in-call-chat.md +183 -0
  181. package/skills/cometchat-ios-calls/references/migration-v4-to-v5.md +138 -0
  182. package/skills/cometchat-ios-calls/references/picture-in-picture.md +246 -0
  183. package/skills/cometchat-ios-calls/references/raise-hand.md +239 -0
  184. package/skills/cometchat-ios-calls/references/recording-screen-share.md +339 -0
  185. package/skills/cometchat-ios-calls/references/ringing-integration.md +142 -0
  186. package/skills/cometchat-ios-calls/references/server-apns-pushkit.md +267 -0
  187. package/skills/cometchat-ios-calls/references/share-invite.md +152 -0
  188. package/skills/cometchat-ios-calls/references/swiftui-uikit-hosting.md +246 -0
  189. package/skills/cometchat-ios-calls/references/voip-cert-setup.md +166 -0
  190. package/skills/cometchat-ios-components/SKILL.md +1 -1
  191. package/skills/cometchat-ios-core/SKILL.md +1 -1
  192. package/skills/cometchat-ios-customization/SKILL.md +1 -1
  193. package/skills/cometchat-ios-features/SKILL.md +1 -1
  194. package/skills/cometchat-ios-placement/SKILL.md +1 -1
  195. package/skills/cometchat-ios-production/SKILL.md +1 -1
  196. package/skills/cometchat-ios-push/SKILL.md +1 -1
  197. package/skills/cometchat-ios-testing/SKILL.md +371 -0
  198. package/skills/cometchat-ios-theming/SKILL.md +1 -1
  199. package/skills/cometchat-ios-troubleshooting/SKILL.md +1 -1
  200. package/skills/cometchat-native-bare-patterns/SKILL.md +45 -3
  201. package/skills/cometchat-native-calls/SKILL.md +599 -0
  202. package/skills/cometchat-native-calls/references/add-calls-to-existing-chat.md +203 -0
  203. package/skills/cometchat-native-calls/references/call-layouts.md +124 -0
  204. package/skills/cometchat-native-calls/references/call-session.md +182 -0
  205. package/skills/cometchat-native-calls/references/custom-ui.md +250 -0
  206. package/skills/cometchat-native-calls/references/device-management.md +135 -0
  207. package/skills/cometchat-native-calls/references/expo-vs-bare.md +179 -0
  208. package/skills/cometchat-native-calls/references/group-calls.md +291 -0
  209. package/skills/cometchat-native-calls/references/idle-timeout.md +128 -0
  210. package/skills/cometchat-native-calls/references/in-call-chat.md +143 -0
  211. package/skills/cometchat-native-calls/references/migration-v4-to-v5.md +72 -0
  212. package/skills/cometchat-native-calls/references/picture-in-picture.md +276 -0
  213. package/skills/cometchat-native-calls/references/raise-hand.md +203 -0
  214. package/skills/cometchat-native-calls/references/recording-screen-share.md +290 -0
  215. package/skills/cometchat-native-calls/references/ringing-integration.md +118 -0
  216. package/skills/cometchat-native-calls/references/server-push-bridge.md +135 -0
  217. package/skills/cometchat-native-calls/references/server-push-payloads.md +193 -0
  218. package/skills/cometchat-native-calls/references/share-invite.md +144 -0
  219. package/skills/cometchat-native-calls/references/voip-push-end-to-end.md +317 -0
  220. package/skills/cometchat-native-components/SKILL.md +1 -1
  221. package/skills/cometchat-native-core/SKILL.md +1 -1
  222. package/skills/cometchat-native-customization/SKILL.md +1 -1
  223. package/skills/cometchat-native-expo-patterns/SKILL.md +18 -1
  224. package/skills/cometchat-native-features/SKILL.md +1 -1
  225. package/skills/cometchat-native-placement/SKILL.md +1 -1
  226. package/skills/cometchat-native-production/SKILL.md +1 -1
  227. package/skills/cometchat-native-push/SKILL.md +1 -1
  228. package/skills/cometchat-native-testing/SKILL.md +1 -1
  229. package/skills/cometchat-native-theming/SKILL.md +1 -1
  230. package/skills/cometchat-native-troubleshooting/SKILL.md +1 -1
  231. package/skills/cometchat-nextjs-patterns/SKILL.md +1 -1
  232. package/skills/cometchat-placement/SKILL.md +1 -1
  233. package/skills/cometchat-production/SKILL.md +1 -1
  234. package/skills/cometchat-react-calls/SKILL.md +539 -0
  235. package/skills/cometchat-react-calls/references/add-calls-to-existing-chat.md +145 -0
  236. package/skills/cometchat-react-calls/references/call-layouts.md +161 -0
  237. package/skills/cometchat-react-calls/references/call-session.md +235 -0
  238. package/skills/cometchat-react-calls/references/custom-ui.md +201 -0
  239. package/skills/cometchat-react-calls/references/device-management.md +206 -0
  240. package/skills/cometchat-react-calls/references/group-calls.md +262 -0
  241. package/skills/cometchat-react-calls/references/idle-timeout.md +175 -0
  242. package/skills/cometchat-react-calls/references/in-call-chat.md +212 -0
  243. package/skills/cometchat-react-calls/references/migration-v4-to-v5.md +172 -0
  244. package/skills/cometchat-react-calls/references/picture-in-picture.md +245 -0
  245. package/skills/cometchat-react-calls/references/raise-hand.md +238 -0
  246. package/skills/cometchat-react-calls/references/recording-screen-share.md +143 -0
  247. package/skills/cometchat-react-calls/references/ringing-integration.md +255 -0
  248. package/skills/cometchat-react-calls/references/server-web-push-vapid.md +241 -0
  249. package/skills/cometchat-react-calls/references/share-invite.md +176 -0
  250. package/skills/cometchat-react-calls/references/testing-calls-on-web.md +198 -0
  251. package/skills/cometchat-react-calls/references/voip-and-web-push.md +165 -0
  252. package/skills/cometchat-react-patterns/SKILL.md +1 -1
  253. package/skills/cometchat-react-push/SKILL.md +448 -0
  254. package/skills/cometchat-react-router-patterns/SKILL.md +1 -1
  255. package/skills/cometchat-react-testing/SKILL.md +404 -0
  256. package/skills/cometchat-theming/SKILL.md +3 -3
  257. package/skills/cometchat-troubleshooting/SKILL.md +2 -2
@@ -0,0 +1,267 @@
1
+ # Server-side APNs PushKit for iOS VoIP
2
+
3
+ When a CometChat call event fires (`onIncomingCallReceived` → CometChat backend webhook), your server sends a **VoIP push** to the recipient's iPhone via Apple's PushKit. Your app's PushKit delegate then triggers CallKit's incoming-call UI. Without VoIP push, the recipient's phone won't ring when the app is backgrounded.
4
+
5
+ **Canonical docs:** https://developer.apple.com/documentation/pushkit, https://developer.apple.com/documentation/usernotifications/sending-notification-requests-to-apns
6
+
7
+ ---
8
+
9
+ ## Hard rules
10
+
11
+ 1. **VoIP push is .p8 only** (token-auth). Old .pem cert auth was deprecated in 2020 and is no longer supported for VoIP.
12
+ 2. **Use a separate APNs topic.** VoIP topic = `<bundle-id>.voip`. NOT the same as your regular APNs topic.
13
+ 3. **`apns-push-type: voip`** header is mandatory. Apple drops the push silently if missing.
14
+ 4. **`apns-priority: 10`** (immediate). VoIP must be high-priority.
15
+ 5. **Must report to CallKit within ~5 seconds.** PushKit gives you a tiny window — if you don't call `CXProvider.reportNewIncomingCall(with:update:)` fast, iOS terminates your app and counts a strike. Three strikes in 24h and Apple revokes your VoIP entitlement.
16
+ 6. **VoIP push payload size ≤ 4KB.** Include the minimum (`sessionId`, `callerName`, `callType`); fetch the rest after CallKit is reported.
17
+
18
+ ---
19
+
20
+ ## .p8 setup
21
+
22
+ 1. Apple Developer Portal → Certificates, Identifiers & Profiles → **Keys** → "+"
23
+ 2. Enable **Apple Push Notifications service (APNs)** → Continue → Register
24
+ 3. Download the `.p8` file. **Save it — Apple shows it once.**
25
+ 4. Note the **Key ID** (10-char alphanumeric).
26
+ 5. Note your **Team ID** (top-right of Apple Developer dashboard).
27
+
28
+ Store securely on your server (Secret Manager / Vault — never in source).
29
+
30
+ ---
31
+
32
+ ## Node.js template
33
+
34
+ ```js
35
+ // npm install @parse/node-apn
36
+ import apn from "@parse/node-apn";
37
+ import fs from "fs";
38
+
39
+ const provider = new apn.Provider({
40
+ token: {
41
+ key: fs.readFileSync("/secrets/AuthKey_ABC123.p8"),
42
+ keyId: process.env.APNS_KEY_ID, // e.g. "ABC123XYZ"
43
+ teamId: process.env.APNS_TEAM_ID, // e.g. "TEAM12345"
44
+ },
45
+ production: process.env.NODE_ENV === "production",
46
+ });
47
+
48
+ /**
49
+ * Called from your CometChat call-event webhook handler.
50
+ * Looks up the recipient's PushKit token (you stored it when their app
51
+ * registered) and fires the VoIP push.
52
+ */
53
+ export async function sendVoipPush(opts) {
54
+ const { recipientUid, sessionId, callerName, callerUid, callType } = opts;
55
+
56
+ const pushKitToken = await db.getVoipToken(recipientUid);
57
+ if (!pushKitToken) return; // user not on iOS or token expired
58
+
59
+ const note = new apn.Notification();
60
+ note.topic = `${process.env.APNS_BUNDLE_ID}.voip`; // <bundle>.voip — required
61
+ note.pushType = "voip"; // apns-push-type: voip
62
+ note.priority = 10; // apns-priority: 10 — immediate
63
+ note.expiry = Math.floor(Date.now() / 1000) + 30; // 30s — VoIP push must fire fast
64
+
65
+ // Custom payload — keep ≤4KB, include only what CallKit needs
66
+ note.rawPayload = {
67
+ aps: { "content-available": 1 },
68
+ sessionId,
69
+ callerName,
70
+ callerUid,
71
+ callType, // "audio" | "video"
72
+ timestamp: Date.now(),
73
+ };
74
+
75
+ const result = await provider.send(note, pushKitToken);
76
+ if (result.failed.length > 0) {
77
+ const fail = result.failed[0];
78
+ if (fail.status === "410" || fail.response?.reason === "Unregistered") {
79
+ // Token is dead — remove from DB so we don't keep retrying
80
+ await db.removeVoipToken(recipientUid, pushKitToken);
81
+ }
82
+ console.error("APNs VoIP push failed", fail);
83
+ return;
84
+ }
85
+ }
86
+ ```
87
+
88
+ ---
89
+
90
+ ## Python template
91
+
92
+ ```python
93
+ # pip install httpx pyjwt cryptography
94
+ import time, json, jwt, httpx
95
+ from cryptography.hazmat.primitives.serialization import load_pem_private_key
96
+
97
+ PRIVATE_KEY = load_pem_private_key(
98
+ open("/secrets/AuthKey_ABC123.p8", "rb").read(), password=None
99
+ )
100
+ KEY_ID = os.environ["APNS_KEY_ID"]
101
+ TEAM_ID = os.environ["APNS_TEAM_ID"]
102
+ BUNDLE_ID = os.environ["APNS_BUNDLE_ID"]
103
+ APNS_HOST = "https://api.push.apple.com" if os.environ.get("ENV") == "production" else "https://api.sandbox.push.apple.com"
104
+
105
+ # Provider tokens are valid for 1h; cache + refresh
106
+ _token_cache = {"token": None, "issued_at": 0}
107
+
108
+ def provider_token() -> str:
109
+ if time.time() - _token_cache["issued_at"] < 3000: # refresh every 50min
110
+ return _token_cache["token"]
111
+ token = jwt.encode(
112
+ {"iss": TEAM_ID, "iat": int(time.time())},
113
+ PRIVATE_KEY,
114
+ algorithm="ES256",
115
+ headers={"kid": KEY_ID},
116
+ )
117
+ _token_cache.update(token=token, issued_at=time.time())
118
+ return token
119
+
120
+ async def send_voip_push(pushkit_token: str, payload: dict):
121
+ headers = {
122
+ "authorization": f"bearer {provider_token()}",
123
+ "apns-topic": f"{BUNDLE_ID}.voip",
124
+ "apns-push-type": "voip",
125
+ "apns-priority": "10",
126
+ "apns-expiration": str(int(time.time()) + 30),
127
+ }
128
+ async with httpx.AsyncClient(http2=True) as client:
129
+ r = await client.post(
130
+ f"{APNS_HOST}/3/device/{pushkit_token}",
131
+ headers=headers,
132
+ content=json.dumps(payload),
133
+ )
134
+ if r.status_code == 410:
135
+ # Token unregistered — remove from DB
136
+ return "unregistered"
137
+ if r.status_code != 200:
138
+ raise RuntimeError(f"APNs error {r.status_code}: {r.text}")
139
+ ```
140
+
141
+ ---
142
+
143
+ ## Webhook payload from CometChat
144
+
145
+ In the CometChat dashboard, configure a **Webhook** → "Call events" → POST to `https://yourserver.com/webhooks/call`. The body shape:
146
+
147
+ ```json
148
+ {
149
+ "trigger": "call_initiated",
150
+ "data": {
151
+ "id": "session-uuid",
152
+ "sessionId": "session-uuid",
153
+ "type": "video",
154
+ "initiator": { "uid": "alice", "name": "Alice" },
155
+ "receiver": { "uid": "bob", "name": "Bob" },
156
+ "receiverType": "user"
157
+ }
158
+ }
159
+ ```
160
+
161
+ Your handler:
162
+
163
+ ```js
164
+ app.post("/webhooks/call", async (req, res) => {
165
+ // 1. Verify webhook signature (X-CometChat-Signature header)
166
+ const sig = req.headers["x-cometchat-signature"];
167
+ const expected = crypto.createHmac("sha256", process.env.COMETCHAT_WEBHOOK_SECRET)
168
+ .update(req.rawBody).digest("hex");
169
+ if (sig !== expected) return res.status(401).end();
170
+
171
+ // 2. Only fire VoIP push on call_initiated (not on call-ended)
172
+ if (req.body.trigger !== "call_initiated") return res.status(200).end();
173
+
174
+ // 3. Forward to platform-specific push
175
+ const { sessionId, type, initiator, receiver } = req.body.data;
176
+ await sendVoipPush({
177
+ recipientUid: receiver.uid,
178
+ sessionId,
179
+ callerName: initiator.name,
180
+ callerUid: initiator.uid,
181
+ callType: type,
182
+ });
183
+
184
+ res.status(200).end();
185
+ });
186
+ ```
187
+
188
+ ---
189
+
190
+ ## Token registration (the round-trip)
191
+
192
+ When your iOS app launches, register PushKit and POST the token to your server:
193
+
194
+ ```swift
195
+ // AppDelegate or dedicated VoIP manager
196
+ import PushKit
197
+
198
+ class VoipPushHandler: NSObject, PKPushRegistryDelegate {
199
+ let registry = PKPushRegistry(queue: .main)
200
+
201
+ override init() {
202
+ super.init()
203
+ registry.delegate = self
204
+ registry.desiredPushTypes = [.voIP]
205
+ }
206
+
207
+ func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) {
208
+ guard type == .voIP else { return }
209
+ let token = credentials.token.map { String(format: "%02x", $0) }.joined()
210
+ Task { try? await api.registerVoipToken(uid: currentUserUid, token: token) }
211
+ }
212
+
213
+ func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) {
214
+ Task { try? await api.removeVoipToken(uid: currentUserUid) }
215
+ }
216
+
217
+ func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
218
+ // Report to CallKit IMMEDIATELY — Apple gives you ~5s
219
+ let dict = payload.dictionaryPayload
220
+ let update = CXCallUpdate()
221
+ update.remoteHandle = CXHandle(type: .generic, value: dict["callerUid"] as? String ?? "Unknown")
222
+ update.localizedCallerName = dict["callerName"] as? String ?? "Unknown"
223
+ update.hasVideo = (dict["callType"] as? String) == "video"
224
+
225
+ let uuid = UUID()
226
+ callProvider.reportNewIncomingCall(with: uuid, update: update) { error in
227
+ if let error = error { print("CallKit reportNewIncomingCall failed:", error) }
228
+ completion()
229
+ }
230
+ }
231
+ }
232
+ ```
233
+
234
+ ---
235
+
236
+ ## Anti-patterns
237
+
238
+ 1. **Using regular APNs token, not VoIP.** Push arrives but CallKit doesn't trigger — the app got woken up, not put into "incoming call" state.
239
+ 2. **Forgetting `apns-push-type: voip`.** Apple drops the push, no error to your server.
240
+ 3. **Not calling `reportNewIncomingCall` from `didReceiveIncomingPushWith`.** Three strikes → entitlement revoked.
241
+ 4. **Sending too much in the VoIP payload.** Apple throttles or rejects >4KB. Send minimum, fetch the rest after CallKit fires.
242
+ 5. **Webhook handler doing slow work synchronously.** Push to a queue (Redis, SQS, BullMQ); return 200 immediately. Slow webhook = CometChat retries = duplicate calls.
243
+ 6. **Same token for chat push and VoIP push.** Different tokens — you must register both via `UNUserNotificationCenter` (regular) AND `PKPushRegistry` (VoIP).
244
+
245
+ ---
246
+
247
+ ## Verification checklist
248
+
249
+ - [ ] `.p8` key stored in secret manager
250
+ - [ ] `apns-topic = <bundle-id>.voip`
251
+ - [ ] `apns-push-type: voip` + `apns-priority: 10`
252
+ - [ ] `reportNewIncomingCall` fires from PushKit delegate inside ~5s
253
+ - [ ] 410 responses purge dead tokens from DB
254
+ - [ ] Webhook signature verified before processing
255
+ - [ ] Webhook returns 200 within 1s (push enqueued, not awaited)
256
+ - [ ] Real-device smoke: backgrounded recipient → caller dials → recipient phone rings on lock screen
257
+
258
+ ---
259
+
260
+ ## Pointers
261
+
262
+ - `cometchat-ios-calls/SKILL.md` — the seven hard rules (init order, IncomingCall mounting, hangup teardown)
263
+ - `cometchat-ios-push/SKILL.md` — chat push (sister, different APNs topic)
264
+ - `cometchat-android-v5-calls/references/server-fcm-voip.md` — Android sibling (FCM HTTP v1)
265
+ - `cometchat-react-calls/references/server-web-push-vapid.md` — Web sibling
266
+ - Apple PushKit: https://developer.apple.com/documentation/pushkit
267
+ - APNs HTTP/2 reference: https://developer.apple.com/documentation/usernotifications/sending-notification-requests-to-apns
@@ -0,0 +1,152 @@
1
+ # Share invite on iOS
2
+
3
+ Same SDK API. iOS-specific: `UIActivityViewController` is the native share sheet (handles iMessage, Mail, AirDrop, third-party apps). Universal Links must be configured for the call link to deep into the app.
4
+
5
+ **Canonical docs:** https://www.cometchat.com/docs/calls/ios/share-invite
6
+ **Read first:** `cometchat-react-calls/references/share-invite.md` — deep-link rule + share dialog UX.
7
+
8
+ ---
9
+
10
+ ## Hard rule: Universal Links + apple-app-site-association
11
+
12
+ For `https://yourapp.com/call/SESSION_ID` to open your app:
13
+
14
+ 1. Add `Associated Domains` capability with `applinks:yourapp.com`
15
+ 2. Host `apple-app-site-association` JSON at `https://yourapp.com/.well-known/apple-app-site-association`:
16
+
17
+ ```json
18
+ {
19
+ "applinks": {
20
+ "apps": [],
21
+ "details": [{
22
+ "appID": "TEAMID.com.yourapp.bundle",
23
+ "paths": ["/call/*"]
24
+ }]
25
+ }
26
+ }
27
+ ```
28
+
29
+ 3. In `SceneDelegate` / `AppDelegate`, handle `NSUserActivity` with `activityType == NSUserActivityTypeBrowsingWeb`:
30
+
31
+ ```swift
32
+ func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
33
+ guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
34
+ let url = userActivity.webpageURL,
35
+ url.pathComponents.count >= 3,
36
+ url.pathComponents[1] == "call" else { return }
37
+ let sessionId = url.pathComponents[2]
38
+ router.routeToCall(sessionId: sessionId)
39
+ }
40
+ ```
41
+
42
+ Without these, share-invite URLs open in Safari instead of your app.
43
+
44
+ ---
45
+
46
+ ## SDK API
47
+
48
+ ```swift
49
+ let settings = CallSettingsBuilder()
50
+ .setHideShareInviteButton(false)
51
+ .build()
52
+
53
+ class CallEvents: CometChatCallsEventsListener {
54
+ func onShareInviteButtonClicked() {
55
+ DispatchQueue.main.async {
56
+ self.shareCallInvite()
57
+ }
58
+ }
59
+ }
60
+ ```
61
+
62
+ ---
63
+
64
+ ## UIActivityViewController
65
+
66
+ ```swift
67
+ extension CallViewController {
68
+ func shareCallInvite() {
69
+ let url = URL(string: "https://yourapp.com/call/\(sessionId)")!
70
+ let title = "Join my call"
71
+ let text = "I'm on a call — tap to join."
72
+
73
+ let items: [Any] = [text, url]
74
+ let activityVC = UIActivityViewController(
75
+ activityItems: items,
76
+ applicationActivities: nil
77
+ )
78
+
79
+ // Optional: exclude activities you don't want
80
+ activityVC.excludedActivityTypes = [.assignToContact, .saveToCameraRoll]
81
+
82
+ // iPad: must set source for popover
83
+ if let popover = activityVC.popoverPresentationController {
84
+ popover.sourceView = self.view
85
+ popover.sourceRect = CGRect(
86
+ x: self.view.bounds.midX,
87
+ y: self.view.bounds.midY,
88
+ width: 0,
89
+ height: 0
90
+ )
91
+ popover.permittedArrowDirections = []
92
+ }
93
+
94
+ present(activityVC, animated: true)
95
+ }
96
+ }
97
+ ```
98
+
99
+ iOS auto-renders link preview if your URL has Open Graph tags + universal-link routing — the share sheet shows app icon + name + "Join my call" instead of a raw URL.
100
+
101
+ ---
102
+
103
+ ## SwiftUI ShareLink (iOS 16+)
104
+
105
+ If you build the share button yourself in SwiftUI:
106
+
107
+ ```swift
108
+ import SwiftUI
109
+
110
+ struct CallShareButton: View {
111
+ let sessionId: String
112
+
113
+ var body: some View {
114
+ let url = URL(string: "https://yourapp.com/call/\(sessionId)")!
115
+ ShareLink(item: url, subject: Text("Join my call"), message: Text("I'm on a call — tap to join.")) {
116
+ Label("Share invite", systemImage: "square.and.arrow.up")
117
+ }
118
+ }
119
+ }
120
+ ```
121
+
122
+ ---
123
+
124
+ ## Anti-patterns
125
+
126
+ Web sister rules apply, plus iOS-specific:
127
+
128
+ 1. **iPad without `popoverPresentationController.sourceView`.** Crashes on iPad — popover requires an anchor.
129
+ 2. **`UIPasteboard.general.string = url.absoluteString` instead of UIActivityVC.** Skips iOS's curated share sheet — bad UX.
130
+ 3. **Universal Links not configured.** Recipients tap link → opens Safari, not your app.
131
+ 4. **`activityItems: [url]` without title text.** Recipients see just a URL, no context.
132
+ 5. **Presenting from background queue.** Crash. Always main.
133
+
134
+ ---
135
+
136
+ ## Verification checklist
137
+
138
+ - [ ] Associated Domains entitlement added
139
+ - [ ] `apple-app-site-association` JSON hosted + reachable
140
+ - [ ] `NSUserActivity` continuation in SceneDelegate routes to call
141
+ - [ ] `UIActivityViewController` (UIKit) or `ShareLink` (SwiftUI 16+) used
142
+ - [ ] iPad popover anchored
143
+ - [ ] Real-device smoke: share via Messages → tap from another iPhone → opens your app at call
144
+
145
+ ---
146
+
147
+ ## Pointers
148
+
149
+ - `cometchat-react-calls/references/share-invite.md` — sister
150
+ - `cometchat-ios-calls` SKILL.md — seven hard rules
151
+ - `references/universal-links.md`
152
+ - Canonical docs: https://www.cometchat.com/docs/calls/ios/share-invite
@@ -0,0 +1,246 @@
1
+ # SwiftUI + UIKit hosting for iOS calls
2
+
3
+ CometChat's iOS Calls UI Kit ships UIKit `UIViewController`s. SwiftUI apps host them via `UIViewControllerRepresentable`. Mixed projects (SwiftUI app shell with UIKit feature screens) work natively.
4
+
5
+ ---
6
+
7
+ ## Pure UIKit project — direct usage
8
+
9
+ ```swift
10
+ import UIKit
11
+ import CometChatUIKitSwift
12
+
13
+ class ProfileViewController: UIViewController {
14
+ override func viewDidLoad() {
15
+ super.viewDidLoad()
16
+
17
+ let callButton = CometChatCallButton(user: user)
18
+ callButton.translatesAutoresizingMaskIntoConstraints = false
19
+ view.addSubview(callButton)
20
+ NSLayoutConstraint.activate([
21
+ callButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
22
+ callButton.centerYAnchor.constraint(equalTo: nameLabel.centerYAnchor),
23
+ ])
24
+ }
25
+ }
26
+ ```
27
+
28
+ For ongoing call:
29
+
30
+ ```swift
31
+ let ongoingCallVC = CometChatOngoingCall()
32
+ ongoingCallVC.set(sessionID: sessionId)
33
+ present(ongoingCallVC, animated: true)
34
+ ```
35
+
36
+ ---
37
+
38
+ ## Pure SwiftUI project — UIViewControllerRepresentable
39
+
40
+ Wrap each UIKit VC in a SwiftUI view:
41
+
42
+ ```swift
43
+ import SwiftUI
44
+ import CometChatUIKitSwift
45
+
46
+ struct CallButtonView: UIViewRepresentable {
47
+ let user: User
48
+
49
+ func makeUIView(context: Context) -> CometChatCallButton {
50
+ CometChatCallButton(user: user)
51
+ }
52
+
53
+ func updateUIView(_ uiView: CometChatCallButton, context: Context) {
54
+ // Re-bind on user change if your view supports that
55
+ }
56
+ }
57
+
58
+ struct OngoingCallView: UIViewControllerRepresentable {
59
+ let sessionID: String
60
+ @Binding var isPresented: Bool
61
+
62
+ func makeUIViewController(context: Context) -> CometChatOngoingCall {
63
+ let vc = CometChatOngoingCall()
64
+ vc.set(sessionID: sessionID)
65
+ return vc
66
+ }
67
+
68
+ func updateUIViewController(_ vc: CometChatOngoingCall, context: Context) {}
69
+ }
70
+
71
+ struct CallLogsView: UIViewControllerRepresentable {
72
+ func makeUIViewController(context: Context) -> CometChatCallLogs {
73
+ CometChatCallLogs()
74
+ }
75
+
76
+ func updateUIViewController(_ vc: CometChatCallLogs, context: Context) {}
77
+ }
78
+ ```
79
+
80
+ Use them like any SwiftUI view:
81
+
82
+ ```swift
83
+ struct ProfileView: View {
84
+ let user: User
85
+ @State private var ongoingSessionID: String?
86
+
87
+ var body: some View {
88
+ VStack {
89
+ Text(user.name)
90
+ CallButtonView(user: user)
91
+ .frame(width: 100, height: 44)
92
+ }
93
+ .fullScreenCover(isPresented: Binding(
94
+ get: { ongoingSessionID != nil },
95
+ set: { if !$0 { ongoingSessionID = nil } }
96
+ )) {
97
+ if let id = ongoingSessionID {
98
+ OngoingCallView(sessionID: id, isPresented: Binding(
99
+ get: { true }, set: { _ in ongoingSessionID = nil }
100
+ ))
101
+ }
102
+ }
103
+ }
104
+ }
105
+ ```
106
+
107
+ ---
108
+
109
+ ## Mixed-stack — SwiftUI app, UIKit feature screen
110
+
111
+ Common in apps that started UIKit and adopted SwiftUI for new features. Calls can stay UIKit; chat can be SwiftUI; both interop.
112
+
113
+ ```swift
114
+ // SwiftUI screen pushing a UIKit call screen via UIHostingController bridge
115
+ import SwiftUI
116
+
117
+ struct ChatView: View {
118
+ let user: User
119
+
120
+ var body: some View {
121
+ NavigationLink(destination: UIKitCallScreenWrapper(user: user)) {
122
+ Text("Start call")
123
+ }
124
+ }
125
+ }
126
+
127
+ struct UIKitCallScreenWrapper: UIViewControllerRepresentable {
128
+ let user: User
129
+
130
+ func makeUIViewController(context: Context) -> UINavigationController {
131
+ let vc = OngoingCallViewController(user: user) // your custom UIKit VC
132
+ return UINavigationController(rootViewController: vc)
133
+ }
134
+
135
+ func updateUIViewController(_ vc: UINavigationController, context: Context) {}
136
+ }
137
+ ```
138
+
139
+ ---
140
+
141
+ ## Where the IncomingCall UI goes (additive mode)
142
+
143
+ In standalone mode, **CallKit owns the incoming-call UI** — no in-app rendering needed.
144
+
145
+ In additive mode (chat already integrated and user is foregrounded when a call comes in), the kit's `CometChatIncomingCall` view can render. Mount it at the root:
146
+
147
+ ### SwiftUI app — root mount
148
+
149
+ ```swift
150
+ @main
151
+ struct YourApp: App {
152
+ @StateObject private var callState = CallState.shared
153
+
154
+ var body: some Scene {
155
+ WindowGroup {
156
+ ZStack {
157
+ ContentView()
158
+ if let call = callState.foregroundIncomingCall {
159
+ IncomingCallView(call: call, callState: callState)
160
+ .transition(.move(edge: .top))
161
+ }
162
+ }
163
+ }
164
+ }
165
+ }
166
+
167
+ struct IncomingCallView: UIViewControllerRepresentable {
168
+ let call: Call
169
+ let callState: CallState
170
+
171
+ func makeUIViewController(context: Context) -> CometChatIncomingCall {
172
+ let vc = CometChatIncomingCall()
173
+ vc.set(call: call)
174
+ vc.setOnAccept { [weak callState] _ in callState?.accept(call) }
175
+ vc.setOnDecline { [weak callState] _ in callState?.decline(call) }
176
+ return vc
177
+ }
178
+
179
+ func updateUIViewController(_ vc: CometChatIncomingCall, context: Context) {}
180
+ }
181
+ ```
182
+
183
+ ### UIKit app — present from root window
184
+
185
+ ```swift
186
+ class CallObserver: NSObject {
187
+ func showIncomingCallUI(call: Call) {
188
+ let vc = CometChatIncomingCall()
189
+ vc.set(call: call)
190
+ UIApplication.shared.windows.first?.rootViewController?.present(vc, animated: true)
191
+ }
192
+ }
193
+ ```
194
+
195
+ In standalone, neither — CallKit handles it.
196
+
197
+ ---
198
+
199
+ ## When to use SwiftUI navigation vs UIKit modal
200
+
201
+ For ongoing calls specifically, **modal full-screen** is the right pattern on both stacks:
202
+
203
+ - SwiftUI: `.fullScreenCover(isPresented:)` (NOT `.sheet`, which is dismissable by swipe)
204
+ - UIKit: `present(_:animated:)` with `vc.modalPresentationStyle = .fullScreen`
205
+
206
+ Reason: in-call UI shouldn't be navigable-back-to (the user shouldn't accidentally swipe-down and lose the call view). Full-screen modal forces explicit "End" button to dismiss.
207
+
208
+ ---
209
+
210
+ ## CometChatUIKit init for SwiftUI
211
+
212
+ Init in `App.init()`:
213
+
214
+ ```swift
215
+ @main
216
+ struct YourApp: App {
217
+ init() {
218
+ let settings = UIKitSettings()
219
+ .set(appID: Secrets.cometchatAppID)
220
+ .set(region: Secrets.cometchatRegion)
221
+ .set(authKey: Secrets.cometchatAuthKey)
222
+ .subscribePresenceForAllUsers()
223
+ .build()
224
+
225
+ CometChatUIKit.init(uiKitSettings: settings) { result in
226
+ switch result {
227
+ case .success: print("Init OK")
228
+ case .onError(let error): print("Init failed: \(error)")
229
+ }
230
+ }
231
+ }
232
+
233
+ var body: some Scene { /* ... */ }
234
+ }
235
+ ```
236
+
237
+ Reference `cometchat-ios-core` for the full init pattern with login.
238
+
239
+ ---
240
+
241
+ ## Things that don't work cleanly in SwiftUI
242
+
243
+ - **Custom theming via Storyboard inspection** — UIKit-only. SwiftUI projects override theme via `CometChatTheme` API in code (see `cometchat-ios-theming`).
244
+ - **NavigationLink to a `CometChat*` VC without representable wrapper** — must wrap.
245
+ - **Sharing UIKit and SwiftUI navigation stacks** — possible but messy. Pick one for any single feature.
246
+ - **Presenting `CometChatIncomingCall` from a SwiftUI sheet** — sheets dismiss on swipe; calls shouldn't. Use `fullScreenCover` or a manually-rendered ZStack overlay.