@kapsula-chat/capacitor-push-calls 1.0.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.
@@ -0,0 +1,517 @@
1
+ import AVFoundation
2
+ import CallKit
3
+ import Capacitor
4
+ import Foundation
5
+ import PushKit
6
+ import UIKit
7
+ import UserNotifications
8
+
9
+ @objc(CapacitorVoipCallsPlugin)
10
+ public class CapacitorVoipCallsPlugin: CAPPlugin, CAPBridgedPlugin {
11
+ public let identifier = "CapacitorVoipCallsPlugin"
12
+ public let jsName = "CapacitorPushCalls"
13
+ public let pluginMethods: [CAPPluginMethod] = [
14
+ CAPPluginMethod(name: "register", returnType: CAPPluginReturnPromise),
15
+ CAPPluginMethod(name: "unregister", returnType: CAPPluginReturnPromise),
16
+ CAPPluginMethod(name: "checkPermissions", returnType: CAPPluginReturnPromise),
17
+ CAPPluginMethod(name: "requestPermissions", returnType: CAPPluginReturnPromise),
18
+ CAPPluginMethod(name: "getDeliveredNotifications", returnType: CAPPluginReturnPromise),
19
+ CAPPluginMethod(name: "removeDeliveredNotifications", returnType: CAPPluginReturnPromise),
20
+ CAPPluginMethod(name: "removeAllDeliveredNotifications", returnType: CAPPluginReturnPromise),
21
+ CAPPluginMethod(name: "getBadgeCount", returnType: CAPPluginReturnPromise),
22
+ CAPPluginMethod(name: "getBadgeNumber", returnType: CAPPluginReturnPromise),
23
+ CAPPluginMethod(name: "setBadgeCount", returnType: CAPPluginReturnPromise),
24
+ CAPPluginMethod(name: "setBadgeNumber", returnType: CAPPluginReturnPromise),
25
+ CAPPluginMethod(name: "clearBadgeCount", returnType: CAPPluginReturnPromise),
26
+ CAPPluginMethod(name: "createChannel", returnType: CAPPluginReturnPromise),
27
+ CAPPluginMethod(name: "deleteChannel", returnType: CAPPluginReturnPromise),
28
+ CAPPluginMethod(name: "listChannels", returnType: CAPPluginReturnPromise),
29
+ CAPPluginMethod(name: "registerVoipNotifications", returnType: CAPPluginReturnPromise),
30
+ CAPPluginMethod(name: "startCall", returnType: CAPPluginReturnPromise),
31
+ CAPPluginMethod(name: "endCall", returnType: CAPPluginReturnPromise),
32
+ CAPPluginMethod(name: "answerCall", returnType: CAPPluginReturnPromise),
33
+ CAPPluginMethod(name: "rejectCall", returnType: CAPPluginReturnPromise),
34
+ CAPPluginMethod(name: "setCallOnHold", returnType: CAPPluginReturnPromise),
35
+ CAPPluginMethod(name: "setAudioRoute", returnType: CAPPluginReturnPromise),
36
+ CAPPluginMethod(name: "setMuted", returnType: CAPPluginReturnPromise),
37
+ CAPPluginMethod(name: "handleIncomingCall", returnType: CAPPluginReturnPromise),
38
+ CAPPluginMethod(name: "updateCallStatus", returnType: CAPPluginReturnPromise),
39
+ CAPPluginMethod(name: "isSupported", returnType: CAPPluginReturnPromise),
40
+ ]
41
+
42
+ private var callManager: CallManager?
43
+ private var voipRegistry: PKPushRegistry?
44
+
45
+ override public func load() {
46
+ callManager = CallManager(plugin: self)
47
+
48
+ NotificationCenter.default.addObserver(
49
+ self,
50
+ selector: #selector(didRegisterForRemoteNotifications(_:)),
51
+ name: .capacitorDidRegisterForRemoteNotifications,
52
+ object: nil
53
+ )
54
+ NotificationCenter.default.addObserver(
55
+ self,
56
+ selector: #selector(didFailToRegisterForRemoteNotifications(_:)),
57
+ name: .capacitorDidFailToRegisterForRemoteNotifications,
58
+ object: nil
59
+ )
60
+ bridge?.notificationRouter.pushNotificationHandler = self
61
+ }
62
+
63
+ deinit {
64
+ NotificationCenter.default.removeObserver(self)
65
+ }
66
+
67
+ @objc func register(_ call: CAPPluginCall) {
68
+ DispatchQueue.main.async {
69
+ UIApplication.shared.registerForRemoteNotifications()
70
+ call.resolve()
71
+ }
72
+ }
73
+
74
+ @objc func unregister(_ call: CAPPluginCall) {
75
+ DispatchQueue.main.async {
76
+ UIApplication.shared.unregisterForRemoteNotifications()
77
+ call.resolve()
78
+ }
79
+ }
80
+
81
+ @objc override public func checkPermissions(_ call: CAPPluginCall) {
82
+ UNUserNotificationCenter.current().getNotificationSettings { settings in
83
+ let permission = self.permissionValue(from: settings.authorizationStatus)
84
+ call.resolve(["receive": permission])
85
+ }
86
+ }
87
+
88
+ @objc override public func requestPermissions(_ call: CAPPluginCall) {
89
+ UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { _, error in
90
+ if let error {
91
+ call.reject("Failed to request push notification permissions", nil, error)
92
+ return
93
+ }
94
+
95
+ self.checkPermissions(call)
96
+ }
97
+ }
98
+
99
+ @objc func getDeliveredNotifications(_ call: CAPPluginCall) {
100
+ UNUserNotificationCenter.current().getDeliveredNotifications { notifications in
101
+ let payload = notifications.map { self.serialize(notification: $0) }
102
+ call.resolve(["notifications": payload])
103
+ }
104
+ }
105
+
106
+ @objc func removeDeliveredNotifications(_ call: CAPPluginCall) {
107
+ guard let raw = call.options["notifications"] as? [Any] else {
108
+ call.reject("Missing notifications parameter")
109
+ return
110
+ }
111
+
112
+ let identifiers: [String] = raw.compactMap {
113
+ if let identifier = $0 as? String {
114
+ return identifier
115
+ }
116
+
117
+ if let dict = $0 as? [String: Any] {
118
+ return dict["id"] as? String
119
+ }
120
+
121
+ return nil
122
+ }
123
+
124
+ UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: identifiers)
125
+ call.resolve()
126
+ }
127
+
128
+ @objc func removeAllDeliveredNotifications(_ call: CAPPluginCall) {
129
+ UNUserNotificationCenter.current().removeAllDeliveredNotifications()
130
+ call.resolve()
131
+ }
132
+
133
+ @objc func getBadgeCount(_ call: CAPPluginCall) {
134
+ DispatchQueue.main.async {
135
+ call.resolve(["count": UIApplication.shared.applicationIconBadgeNumber])
136
+ }
137
+ }
138
+
139
+ @objc func getBadgeNumber(_ call: CAPPluginCall) {
140
+ getBadgeCount(call)
141
+ }
142
+
143
+ @objc func setBadgeCount(_ call: CAPPluginCall) {
144
+ guard let count = call.getInt("count"), count >= 0 else {
145
+ call.reject("Invalid count parameter")
146
+ return
147
+ }
148
+
149
+ DispatchQueue.main.async {
150
+ UIApplication.shared.applicationIconBadgeNumber = count
151
+ call.resolve()
152
+ }
153
+ }
154
+
155
+ @objc func setBadgeNumber(_ call: CAPPluginCall) {
156
+ setBadgeCount(call)
157
+ }
158
+
159
+ @objc func clearBadgeCount(_ call: CAPPluginCall) {
160
+ DispatchQueue.main.async {
161
+ UIApplication.shared.applicationIconBadgeNumber = 0
162
+ call.resolve()
163
+ }
164
+ }
165
+
166
+ @objc func createChannel(_ call: CAPPluginCall) {
167
+ // iOS does not support Android notification channels.
168
+ call.resolve()
169
+ }
170
+
171
+ @objc func deleteChannel(_ call: CAPPluginCall) {
172
+ // iOS does not support Android notification channels.
173
+ call.resolve()
174
+ }
175
+
176
+ @objc func listChannels(_ call: CAPPluginCall) {
177
+ // iOS does not support Android notification channels.
178
+ call.resolve(["channels": []])
179
+ }
180
+
181
+ @objc func registerVoipNotifications(_ call: CAPPluginCall) {
182
+ DispatchQueue.main.async {
183
+ self.voipRegistry = PKPushRegistry(queue: DispatchQueue.main)
184
+ self.voipRegistry?.delegate = self
185
+ self.voipRegistry?.desiredPushTypes = [.voIP]
186
+
187
+ call.resolve()
188
+ }
189
+ }
190
+
191
+ @objc func startCall(_ call: CAPPluginCall) {
192
+ guard let handle = call.getString("handle"),
193
+ let displayName = call.getString("displayName") else {
194
+ call.reject("Missing required parameters: handle and displayName")
195
+ return
196
+ }
197
+
198
+ let handleType = call.getString("handleType") ?? "generic"
199
+ let video = call.getBool("video") ?? false
200
+
201
+ let callId = UUID()
202
+ let handleTypeEnum: CXHandle.HandleType = {
203
+ switch handleType {
204
+ case "phone": return .phoneNumber
205
+ case "email": return .emailAddress
206
+ default: return .generic
207
+ }
208
+ }()
209
+
210
+ callManager?.startCall(
211
+ uuid: callId,
212
+ handle: handle,
213
+ displayName: displayName,
214
+ handleType: handleTypeEnum,
215
+ video: video
216
+ )
217
+
218
+ call.resolve(["callId": callId.uuidString])
219
+ }
220
+
221
+ @objc func endCall(_ call: CAPPluginCall) {
222
+ guard let callIdString = call.getString("callId"),
223
+ let callId = UUID(uuidString: callIdString) else {
224
+ call.reject("Invalid callId")
225
+ return
226
+ }
227
+
228
+ callManager?.endCall(uuid: callId)
229
+ call.resolve()
230
+ }
231
+
232
+ @objc func answerCall(_ call: CAPPluginCall) {
233
+ guard let callIdString = call.getString("callId"),
234
+ let callId = UUID(uuidString: callIdString) else {
235
+ call.reject("Invalid callId")
236
+ return
237
+ }
238
+
239
+ callManager?.answerCall(uuid: callId)
240
+ call.resolve()
241
+ }
242
+
243
+ @objc func rejectCall(_ call: CAPPluginCall) {
244
+ guard let callIdString = call.getString("callId"),
245
+ let callId = UUID(uuidString: callIdString) else {
246
+ call.reject("Invalid callId")
247
+ return
248
+ }
249
+
250
+ callManager?.rejectCall(uuid: callId)
251
+ call.resolve()
252
+ }
253
+
254
+ @objc func setCallOnHold(_ call: CAPPluginCall) {
255
+ guard let callIdString = call.getString("callId"),
256
+ let callId = UUID(uuidString: callIdString),
257
+ let onHold = call.getBool("onHold") else {
258
+ call.reject("Invalid parameters")
259
+ return
260
+ }
261
+
262
+ callManager?.setCallOnHold(uuid: callId, onHold: onHold)
263
+ call.resolve()
264
+ }
265
+
266
+ @objc func setAudioRoute(_ call: CAPPluginCall) {
267
+ guard let route = call.getString("route") else {
268
+ call.reject("Missing route parameter")
269
+ return
270
+ }
271
+
272
+ let audioSession = AVAudioSession.sharedInstance()
273
+
274
+ do {
275
+ switch route {
276
+ case "speaker":
277
+ try audioSession.overrideOutputAudioPort(.speaker)
278
+ case "earpiece":
279
+ try audioSession.overrideOutputAudioPort(.none)
280
+ case "bluetooth":
281
+ try audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: [.allowBluetooth])
282
+ default:
283
+ call.reject("Invalid route: \(route)")
284
+ return
285
+ }
286
+ call.resolve()
287
+ } catch {
288
+ call.reject("Failed to set audio route: \(error.localizedDescription)")
289
+ }
290
+ }
291
+
292
+ @objc func setMuted(_ call: CAPPluginCall) {
293
+ guard call.getBool("muted") != nil else {
294
+ call.reject("Missing muted parameter")
295
+ return
296
+ }
297
+
298
+ // Mute should be handled in your media layer.
299
+ call.resolve()
300
+ }
301
+
302
+ @objc func handleIncomingCall(_ call: CAPPluginCall) {
303
+ guard let handle = call.getString("handle"),
304
+ let displayName = call.getString("displayName") else {
305
+ call.reject("Missing required parameters: handle and displayName")
306
+ return
307
+ }
308
+
309
+ let requestedCallId = call.getString("callId")
310
+ let callId = UUID(uuidString: requestedCallId ?? "") ?? UUID()
311
+ let callIdString = callId.uuidString
312
+
313
+ let handleTypeString = call.getString("handleType") ?? "generic"
314
+ let video = call.getBool("video") ?? false
315
+ let metadata = call.getObject("metadata")
316
+
317
+ let handleType: CXHandle.HandleType = {
318
+ switch handleTypeString {
319
+ case "phone": return .phoneNumber
320
+ case "email": return .emailAddress
321
+ default: return .generic
322
+ }
323
+ }()
324
+
325
+ callManager?.reportIncomingCall(
326
+ uuid: callId,
327
+ handle: handle,
328
+ displayName: displayName,
329
+ handleType: handleType,
330
+ video: video
331
+ ) { error in
332
+ if let error {
333
+ call.reject("Failed to report incoming call: \(error.localizedDescription)")
334
+ return
335
+ }
336
+
337
+ var notificationData: [String: Any] = [
338
+ "callId": callIdString,
339
+ "handle": handle,
340
+ "displayName": displayName,
341
+ "handleType": handleTypeString,
342
+ "video": video,
343
+ ]
344
+ if let metadata {
345
+ notificationData["metadata"] = metadata
346
+ }
347
+ self.emitEvent("incomingCall", data: notificationData)
348
+ call.resolve(["callId": callIdString])
349
+ }
350
+ }
351
+
352
+ @objc func updateCallStatus(_ call: CAPPluginCall) {
353
+ guard let callIdString = call.getString("callId"),
354
+ let callId = UUID(uuidString: callIdString),
355
+ let status = call.getString("status") else {
356
+ call.reject("Invalid parameters")
357
+ return
358
+ }
359
+
360
+ callManager?.updateCallStatus(uuid: callId, status: status)
361
+ call.resolve()
362
+ }
363
+
364
+ @objc func isSupported(_ call: CAPPluginCall) {
365
+ call.resolve(["supported": true])
366
+ }
367
+
368
+ @objc private func didRegisterForRemoteNotifications(_ notification: Notification) {
369
+ guard let token = notification.object as? Data else {
370
+ emitEvent("registrationError", data: ["error": "Missing APNS token data"])
371
+ return
372
+ }
373
+
374
+ let tokenString = token.map { String(format: "%02x", $0) }.joined()
375
+ emitEvent("registration", data: ["value": tokenString])
376
+ }
377
+
378
+ @objc private func didFailToRegisterForRemoteNotifications(_ notification: Notification) {
379
+ let error = (notification.object as? Error)?.localizedDescription ?? "Unknown registration error"
380
+ emitEvent("registrationError", data: ["error": error])
381
+ }
382
+
383
+ private func permissionValue(from status: UNAuthorizationStatus) -> String {
384
+ switch status {
385
+ case .authorized, .provisional, .ephemeral:
386
+ return "granted"
387
+ case .denied:
388
+ return "denied"
389
+ case .notDetermined:
390
+ return "prompt"
391
+ @unknown default:
392
+ return "prompt"
393
+ }
394
+ }
395
+
396
+ private func serialize(notification: UNNotification) -> [String: Any] {
397
+ let content = notification.request.content
398
+ let userInfo = content.userInfo.reduce(into: [String: Any]()) { result, entry in
399
+ result[String(describing: entry.key)] = entry.value
400
+ }
401
+
402
+ var payload: [String: Any] = [
403
+ "id": notification.request.identifier,
404
+ "title": content.title,
405
+ "subtitle": content.subtitle,
406
+ "body": content.body,
407
+ "data": userInfo,
408
+ ]
409
+
410
+ if let badge = content.badge as? Int {
411
+ payload["badge"] = badge
412
+ }
413
+
414
+ return payload
415
+ }
416
+
417
+ internal func emitEvent(_ event: String, data: [String: Any]) {
418
+ super.notifyListeners(event, data: data)
419
+ }
420
+
421
+ // Backward-compatible helper used by CallManager.
422
+ internal func notifyListeners(event: String, data: [String: Any]) {
423
+ emitEvent(event, data: data)
424
+ }
425
+ }
426
+
427
+ // MARK: - PKPushRegistryDelegate
428
+ extension CapacitorVoipCallsPlugin: PKPushRegistryDelegate {
429
+ public func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
430
+ let token = pushCredentials.token.map { String(format: "%02x", $0) }.joined()
431
+ emitEvent("voipPushToken", data: ["token": token])
432
+ }
433
+
434
+ public func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
435
+ guard type == .voIP else {
436
+ completion()
437
+ return
438
+ }
439
+
440
+ let payloadDict = payload.dictionaryPayload
441
+
442
+ guard let callIdString = payloadDict["callId"] as? String,
443
+ let callId = UUID(uuidString: callIdString),
444
+ let handle = payloadDict["handle"] as? String,
445
+ let displayName = payloadDict["displayName"] as? String else {
446
+ completion()
447
+ return
448
+ }
449
+
450
+ let handleTypeString = payloadDict["handleType"] as? String ?? "generic"
451
+ let video = payloadDict["video"] as? Bool ?? false
452
+
453
+ let handleType: CXHandle.HandleType = {
454
+ switch handleTypeString {
455
+ case "phone": return .phoneNumber
456
+ case "email": return .emailAddress
457
+ default: return .generic
458
+ }
459
+ }()
460
+
461
+ callManager?.reportIncomingCall(
462
+ uuid: callId,
463
+ handle: handle,
464
+ displayName: displayName,
465
+ handleType: handleType,
466
+ video: video
467
+ ) { error in
468
+ if let error {
469
+ print("Error reporting incoming call: \(error.localizedDescription)")
470
+ }
471
+ completion()
472
+ }
473
+
474
+ var notificationData: [String: Any] = [
475
+ "callId": callIdString,
476
+ "handle": handle,
477
+ "displayName": displayName,
478
+ "handleType": handleTypeString,
479
+ "video": video,
480
+ ]
481
+
482
+ if let metadata = payloadDict["metadata"] as? [String: Any] {
483
+ notificationData["metadata"] = metadata
484
+ }
485
+
486
+ emitEvent("incomingCall", data: notificationData)
487
+ }
488
+
489
+ public func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) {
490
+ print("Push token invalidated for type: \(type)")
491
+ }
492
+ }
493
+
494
+ extension CapacitorVoipCallsPlugin: NotificationHandlerProtocol {
495
+ public func willPresent(notification: UNNotification) -> UNNotificationPresentationOptions {
496
+ emitEvent("pushNotificationReceived", data: serialize(notification: notification))
497
+
498
+ if #available(iOS 14.0, *) {
499
+ return [.banner, .list, .badge, .sound]
500
+ } else {
501
+ return [.alert, .badge, .sound]
502
+ }
503
+ }
504
+
505
+ public func didReceive(response: UNNotificationResponse) {
506
+ var payload: [String: Any] = [
507
+ "actionId": response.actionIdentifier,
508
+ "notification": serialize(notification: response.notification),
509
+ ]
510
+
511
+ if let textResponse = response as? UNTextInputNotificationResponse {
512
+ payload["inputValue"] = textResponse.userText
513
+ }
514
+
515
+ emitEvent("pushNotificationActionPerformed", data: payload)
516
+ }
517
+ }
@@ -0,0 +1,31 @@
1
+ #import <Foundation/Foundation.h>
2
+ #import <Capacitor/Capacitor.h>
3
+
4
+ CAP_PLUGIN(CapacitorVoipCallsPlugin, "CapacitorPushCalls",
5
+ CAP_PLUGIN_METHOD(register, CAPPluginReturnPromise);
6
+ CAP_PLUGIN_METHOD(unregister, CAPPluginReturnPromise);
7
+ CAP_PLUGIN_METHOD(checkPermissions, CAPPluginReturnPromise);
8
+ CAP_PLUGIN_METHOD(requestPermissions, CAPPluginReturnPromise);
9
+ CAP_PLUGIN_METHOD(getDeliveredNotifications, CAPPluginReturnPromise);
10
+ CAP_PLUGIN_METHOD(removeDeliveredNotifications, CAPPluginReturnPromise);
11
+ CAP_PLUGIN_METHOD(removeAllDeliveredNotifications, CAPPluginReturnPromise);
12
+ CAP_PLUGIN_METHOD(getBadgeCount, CAPPluginReturnPromise);
13
+ CAP_PLUGIN_METHOD(getBadgeNumber, CAPPluginReturnPromise);
14
+ CAP_PLUGIN_METHOD(setBadgeCount, CAPPluginReturnPromise);
15
+ CAP_PLUGIN_METHOD(setBadgeNumber, CAPPluginReturnPromise);
16
+ CAP_PLUGIN_METHOD(clearBadgeCount, CAPPluginReturnPromise);
17
+ CAP_PLUGIN_METHOD(createChannel, CAPPluginReturnPromise);
18
+ CAP_PLUGIN_METHOD(deleteChannel, CAPPluginReturnPromise);
19
+ CAP_PLUGIN_METHOD(listChannels, CAPPluginReturnPromise);
20
+ CAP_PLUGIN_METHOD(registerVoipNotifications, CAPPluginReturnPromise);
21
+ CAP_PLUGIN_METHOD(startCall, CAPPluginReturnPromise);
22
+ CAP_PLUGIN_METHOD(endCall, CAPPluginReturnPromise);
23
+ CAP_PLUGIN_METHOD(answerCall, CAPPluginReturnPromise);
24
+ CAP_PLUGIN_METHOD(rejectCall, CAPPluginReturnPromise);
25
+ CAP_PLUGIN_METHOD(setCallOnHold, CAPPluginReturnPromise);
26
+ CAP_PLUGIN_METHOD(setAudioRoute, CAPPluginReturnPromise);
27
+ CAP_PLUGIN_METHOD(setMuted, CAPPluginReturnPromise);
28
+ CAP_PLUGIN_METHOD(handleIncomingCall, CAPPluginReturnPromise);
29
+ CAP_PLUGIN_METHOD(updateCallStatus, CAPPluginReturnPromise);
30
+ CAP_PLUGIN_METHOD(isSupported, CAPPluginReturnPromise);
31
+ )
package/package.json ADDED
@@ -0,0 +1,95 @@
1
+ {
2
+ "name": "@kapsula-chat/capacitor-push-calls",
3
+ "version": "1.0.0",
4
+ "description": "Capacitor plugin with push routing (message/call), CallKit (iOS), and ConnectionService (Android)",
5
+ "main": "dist/plugin.cjs.js",
6
+ "module": "dist/esm/index.js",
7
+ "types": "dist/esm/index.d.ts",
8
+ "unpkg": "dist/plugin.js",
9
+ "files": [
10
+ "android/src/main/",
11
+ "android/build.gradle",
12
+ "dist/",
13
+ "ios/",
14
+ "Package.swift"
15
+ ],
16
+ "author": "",
17
+ "license": "MIT",
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "repository": {
22
+ "type": "git",
23
+ "url": ""
24
+ },
25
+ "bugs": {
26
+ "url": ""
27
+ },
28
+ "keywords": [
29
+ "capacitor",
30
+ "plugin",
31
+ "native",
32
+ "voip",
33
+ "calls",
34
+ "callkit",
35
+ "pushkit",
36
+ "connectionservice",
37
+ "ios",
38
+ "android",
39
+ "sip",
40
+ "webrtc",
41
+ "phone",
42
+ "telephony",
43
+ "spm"
44
+ ],
45
+ "scripts": {
46
+ "verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
47
+ "verify:ios": "xcodebuild -scheme CapacitorPushCalls -destination generic/platform=iOS",
48
+ "verify:android": "cd android && ./gradlew clean build test && cd ..",
49
+ "verify:web": "npm run build",
50
+ "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
51
+ "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
52
+ "eslint": "eslint . --ext ts",
53
+ "prettier": "prettier \"**/*.{css,html,ts,js,java}\"",
54
+ "swiftlint": "node-swiftlint",
55
+ "docgen": "docgen --api CapacitorPushCallsPlugin --output-readme README.md --output-json dist/docs.json || true",
56
+ "build": "npm run clean && tsc && rollup -c rollup.config.js",
57
+ "build:docs": "npm run build && npm run docgen",
58
+ "clean": "rimraf ./dist",
59
+ "watch": "tsc --watch",
60
+ "prepublishOnly": "npm run build",
61
+ "test:setup": "./scripts/test-plugin.sh",
62
+ "test:update": "./scripts/update-test.sh"
63
+ },
64
+ "devDependencies": {
65
+ "@capacitor/android": "^8.0.0",
66
+ "@capacitor/core": "^8.0.0",
67
+ "@capacitor/docgen": "^0.2.0",
68
+ "@capacitor/ios": "^8.0.0",
69
+ "@ionic/eslint-config": "^0.3.0",
70
+ "@ionic/prettier-config": "^4.0.0",
71
+ "@ionic/swiftlint-config": "^1.1.2",
72
+ "eslint": "^8.0.0",
73
+ "prettier": "^3.0.0",
74
+ "prettier-plugin-java": "^2.0.0",
75
+ "rimraf": "^5.0.0",
76
+ "rollup": "^3.0.0",
77
+ "swiftlint": "^1.0.1",
78
+ "typescript": "~5.0.0"
79
+ },
80
+ "peerDependencies": {
81
+ "@capacitor/core": "^8.0.0"
82
+ },
83
+ "prettier": "@ionic/prettier-config",
84
+ "eslintConfig": {
85
+ "extends": "@ionic/eslint-config/recommended"
86
+ },
87
+ "capacitor": {
88
+ "ios": {
89
+ "src": "ios"
90
+ },
91
+ "android": {
92
+ "src": "android"
93
+ }
94
+ }
95
+ }