capacitor-jpush-core 0.0.1

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,524 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ /**
5
+ * 极光推送Capacitor插件
6
+ */
7
+
8
+ /// JPush事件名称枚举
9
+ enum JPushEventName {
10
+ static let notificationReceived = "notificationReceived"
11
+ static let notificationOpened = "notificationOpened"
12
+ static let customMessageReceived = "customMessageReceived"
13
+ static let registrationCompleted = "registrationCompleted"
14
+ static let registrationFailed = "registrationFailed"
15
+ }
16
+
17
+ // 1. 扩展 NSNotification.Name,定义专属通知名
18
+ extension NSNotification.Name {
19
+ // 自定义通知名(本质是封装字符串,类型为 NSNotification.Name)
20
+ static let backgroundPush = NSNotification.Name("CapacitorJPushCoreBackgroundPush")
21
+ }
22
+
23
+ @objc(JPushPlugin)
24
+ public class JPushPlugin: CAPPlugin, CAPBridgedPlugin, JPUSHRegisterDelegate {
25
+ public let identifier = "JPushPlugin"
26
+ public let jsName = "JPush"
27
+ public let pluginMethods: [CAPPluginMethod] = [
28
+ CAPPluginMethod(name: "setupJPush", returnType: CAPPluginReturnPromise),
29
+ CAPPluginMethod(name: "getRegistrationID", returnType: CAPPluginReturnPromise),
30
+ CAPPluginMethod(name: "setAlias", returnType: CAPPluginReturnPromise),
31
+ CAPPluginMethod(name: "deleteAlias", returnType: CAPPluginReturnPromise),
32
+ CAPPluginMethod(name: "setTags", returnType: CAPPluginReturnPromise),
33
+ CAPPluginMethod(name: "addTags", returnType: CAPPluginReturnPromise),
34
+ CAPPluginMethod(name: "deleteTags", returnType: CAPPluginReturnPromise),
35
+ CAPPluginMethod(name: "cleanTags", returnType: CAPPluginReturnPromise),
36
+ CAPPluginMethod(name: "checkPermissions", returnType: CAPPluginReturnPromise),
37
+ CAPPluginMethod(name: "requestPermissions", returnType: CAPPluginReturnPromise),
38
+ CAPPluginMethod(name: "setBadge", returnType: CAPPluginReturnPromise),
39
+ CAPPluginMethod(name: "removeAllListeners", returnType: CAPPluginReturnPromise)
40
+ ]
41
+
42
+ private var registrationIdCall: CAPPluginCall?
43
+ private var aliasCall: CAPPluginCall?
44
+ private var activeClearBadge = false
45
+
46
+ override public func load() {
47
+ super.load()
48
+ addObservers()
49
+
50
+ // 根据配置决定是否在load时初始化JPush
51
+ if isIosAutoRegisterEnabled() {
52
+ registerJPushDelegate()
53
+ initializeJPush()
54
+ }
55
+ }
56
+
57
+ deinit {
58
+ // 移除所有观察者,避免内存泄漏
59
+ NotificationCenter.default.removeObserver(self, name: .capacitorDidRegisterForRemoteNotifications, object: nil)
60
+ NotificationCenter.default.removeObserver(self, name: .capacitorDidFailToRegisterForRemoteNotifications, object: nil)
61
+ NotificationCenter.default.removeObserver(self, name: UIApplication.didBecomeActiveNotification, object: nil)
62
+ NotificationCenter.default.removeObserver(self, name: .backgroundPush, object: nil)
63
+ }
64
+
65
+ private func registerJPushDelegate() {
66
+ // 注册JPUSHRegisterDelegate
67
+ let registerConfig = JPUSHRegisterEntity()
68
+ registerConfig.types = Int(JPAuthorizationOptions.alert.rawValue | JPAuthorizationOptions.badge.rawValue | JPAuthorizationOptions.sound.rawValue)
69
+ // 忽略主线程的警告,否则冷启动通知收不到回调。
70
+ JPUSHService.register(forRemoteNotificationConfig: registerConfig, delegate: self)
71
+ }
72
+
73
+ // MARK: - 监听
74
+
75
+ private func addObservers() {
76
+
77
+ // 监听 DeviceToken 注册成功
78
+ NotificationCenter.default.addObserver(self, selector: #selector(deviceTokenRegistered(_:)), name: .capacitorDidRegisterForRemoteNotifications, object: nil)
79
+
80
+ // 监听 DeviceToken 注册失败
81
+ NotificationCenter.default.addObserver(self, selector: #selector(deviceTokenFailToRegister(_:)), name:.capacitorDidFailToRegisterForRemoteNotifications, object: nil)
82
+
83
+ // 监听 App 进入前台时的状态
84
+ NotificationCenter.default.addObserver(self, selector: #selector(appDidBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)
85
+
86
+ // 监听后台收到远程推送
87
+ NotificationCenter.default.addObserver(self, selector: #selector(didReceiveRemoteNotification(_:)), name: .backgroundPush, object: nil)
88
+ }
89
+
90
+
91
+ @objc private func appDidBecomeActive() {
92
+ // 当应用回到前台时,如果配置了activeClearBadge为true,则清除角标
93
+ if activeClearBadge {
94
+ JPushWrapper.setBadge(0)
95
+ }
96
+ }
97
+
98
+ @objc private func backgroundPush(userInfo: [AnyHashable : Any]) {
99
+ JPushWrapper.handleRemoteNotification(userInfo);
100
+ }
101
+
102
+ @objc private func deviceTokenRegistered(_ notification: NSNotification) {
103
+ // 从通知中获取 DeviceToken
104
+ if let deviceToken = notification.object as? Data {
105
+
106
+ // 上报给极光
107
+ JPUSHService.registerDeviceToken(deviceToken)
108
+
109
+ // 获取registrationID并透传给JS层
110
+ let registrationID = JPushWrapper.registrationID()
111
+ if !registrationID.isEmpty {
112
+ notifyListeners(JPushEventName.registrationCompleted, data: ["registrationID": registrationID], retainUntilConsumed: true)
113
+ }
114
+ }
115
+ }
116
+
117
+ @objc private func deviceTokenFailToRegister(_ notification: NSNotification) {
118
+ // 从通知中获取错误信息
119
+ if let error = notification.object as? Error {
120
+ print("极光远程推送注册失败: \(error.localizedDescription)")
121
+ // 发送注册失败事件给JS层
122
+ notifyListeners(JPushEventName.registrationFailed, data: ["errorCode": -1, "errorMessage": error.localizedDescription], retainUntilConsumed: true)
123
+ }
124
+ }
125
+
126
+ @objc private func didReceiveRemoteNotification(_ notification: NSNotification) {
127
+ // 从通知中获取推送数据
128
+ if let userInfo = notification.object as? [AnyHashable: Any] {
129
+ print("后台收到推送: \(userInfo)")
130
+
131
+ // 处理推送通知
132
+ JPushWrapper.handleRemoteNotification(userInfo)
133
+
134
+ // 检查是否是JPush的通知
135
+ if let aps = userInfo["aps"] as? [String: Any] {
136
+ var notificationData: [String: Any] = aps
137
+
138
+ // 添加额外的数据
139
+ if let alert = aps["alert"] as? [String: String] {
140
+ notificationData["title"] = alert["title"]
141
+ notificationData["body"] = alert["body"]
142
+ } else if let alert = aps["alert"] as? String {
143
+ notificationData["title"] = ""
144
+ notificationData["body"] = alert
145
+ }
146
+
147
+ // 添加自定义字段
148
+ notificationData["extras"] = userInfo.filter { $0.key as! String != "aps" }
149
+
150
+ // 发送收到通知事件给JS层
151
+ notifyListeners(JPushEventName.notificationReceived, data: notificationData)
152
+ }
153
+
154
+ // 处理自定义消息
155
+ if userInfo["msg_content"] != nil {
156
+ let messageData: [String: Any] = userInfo as! [String : Any]
157
+ notifyListeners(JPushEventName.customMessageReceived, data: messageData)
158
+ }
159
+ }
160
+ }
161
+
162
+ // MARK: - 插件方法实现
163
+
164
+ /**
165
+ * 初始化极光推送(私有方法)
166
+ */
167
+ private func initializeJPush() {
168
+ // 从Capacitor配置中读取参数
169
+ let config = getConfig()
170
+ let appKey = config.getString("appKey") ?? ""
171
+ let channel = config.getString("channel", "App Store")
172
+ let production = config.getBoolean("production", false)
173
+ activeClearBadge = config.getBoolean("activeClearBadge", false)
174
+
175
+ // 初始化JPush
176
+ JPushWrapper.initJPush(withAppKey: appKey, channel: channel, production: production)
177
+ }
178
+
179
+ private func isIosAutoRegisterEnabled() -> Bool {
180
+ let config = getConfig()
181
+ return config.getBoolean("iosAutoRegister", false)
182
+ }
183
+
184
+ /**
185
+ * 初始化极光推送
186
+ */
187
+ @objc func setupJPush(_ call: CAPPluginCall) {
188
+ guard let appKey = getConfig().getString("appKey"), appKey != "" else {
189
+ call.reject("appKey 不能为空")
190
+ return
191
+ }
192
+
193
+ initializeJPush()
194
+ call.resolve()
195
+ }
196
+
197
+ /**
198
+ * 获取注册ID
199
+ */
200
+ @objc func getRegistrationID(_ call: CAPPluginCall) {
201
+ self.registrationIdCall = call
202
+
203
+ // 获取缓存的registrationID
204
+ let registrationID = JPushWrapper.registrationID()
205
+ if !registrationID.isEmpty {
206
+ call.resolve(["registrationID": registrationID])
207
+ self.registrationIdCall = nil
208
+ } else {
209
+ // 保存call,等待注册成功后回调
210
+ call.keepAlive = true
211
+ }
212
+ }
213
+
214
+ /**
215
+ * 设置别名
216
+ */
217
+ @objc func setAlias(_ call: CAPPluginCall) {
218
+ guard let alias = call.getString("alias") else {
219
+ call.reject("别名不能为空")
220
+ return
221
+ }
222
+
223
+ // 获取可选的seq参数
224
+ let seq = call.getInt("seq")
225
+
226
+ self.aliasCall = call
227
+
228
+ // 设置别名
229
+ JPushWrapper.setAlias(alias, seq: seq) { [weak self] (success, error) in
230
+ guard let call = self?.aliasCall else { return }
231
+
232
+ if success {
233
+ call.resolve()
234
+ self?.aliasCall = nil
235
+ } else {
236
+ call.reject(error?.localizedDescription ?? "别名设置失败")
237
+ self?.aliasCall = nil
238
+ }
239
+ }
240
+
241
+ call.keepAlive = true
242
+ }
243
+
244
+ /**
245
+ * 删除别名
246
+ */
247
+ @objc func deleteAlias(_ call: CAPPluginCall) {
248
+ self.aliasCall = call
249
+
250
+ // 获取可选的seq参数
251
+ let seq = call.getInt("seq")
252
+
253
+ // 删除别名
254
+ JPushWrapper.deleteAlias(seq: seq) { [weak self] (success, error) in
255
+ guard let call = self?.aliasCall else { return }
256
+
257
+ if success {
258
+ call.resolve()
259
+ self?.aliasCall = nil
260
+ } else {
261
+ call.reject(error?.localizedDescription ?? "别名删除失败")
262
+ self?.aliasCall = nil
263
+ }
264
+ }
265
+
266
+ call.keepAlive = true
267
+ }
268
+
269
+ /**
270
+ * 设置标签(替换所有现有标签)
271
+ */
272
+ @objc func setTags(_ call: CAPPluginCall) {
273
+ guard let tagsArray = call.getArray("tags", String.self) else {
274
+ call.reject("标签不能为空")
275
+ return
276
+ }
277
+
278
+ let tags = Set(tagsArray)
279
+ let seq = call.getInt("seq")
280
+
281
+ // 设置标签
282
+ JPushWrapper.setTags(tags, seq: seq) { (success, error) in
283
+ if success {
284
+ call.resolve()
285
+ } else {
286
+ call.reject(error?.localizedDescription ?? "标签设置失败")
287
+ }
288
+ }
289
+ }
290
+
291
+ /**
292
+ * 添加标签
293
+ */
294
+ @objc func addTags(_ call: CAPPluginCall) {
295
+ guard let tagsArray = call.getArray("tags", String.self) else {
296
+ call.reject("标签不能为空")
297
+ return
298
+ }
299
+
300
+ let tags = Set(tagsArray)
301
+ let seq = call.getInt("seq")
302
+
303
+ // 添加标签
304
+ JPushWrapper.addTags(tags, seq: seq) { (success, error) in
305
+ if success {
306
+ call.resolve()
307
+ } else {
308
+ call.reject(error?.localizedDescription ?? "标签添加失败")
309
+ }
310
+ }
311
+ }
312
+
313
+ /**
314
+ * 删除标签
315
+ */
316
+ @objc func deleteTags(_ call: CAPPluginCall) {
317
+ guard let tagsArray = call.getArray("tags", String.self) else {
318
+ call.reject("标签不能为空")
319
+ return
320
+ }
321
+
322
+ let tags = Set(tagsArray)
323
+ let seq = call.getInt("seq")
324
+
325
+ // 删除标签
326
+ JPushWrapper.deleteTags(tags, seq: seq) { (success, error) in
327
+ if success {
328
+ call.resolve()
329
+ } else {
330
+ call.reject(error?.localizedDescription ?? "标签删除失败")
331
+ }
332
+ }
333
+ }
334
+
335
+ /**
336
+ * 清除所有标签
337
+ */
338
+ @objc func cleanTags(_ call: CAPPluginCall) {
339
+ // 清除所有标签
340
+ JPushWrapper.cleanTags() { (success, error) in
341
+ if success {
342
+ call.resolve()
343
+ } else {
344
+ call.reject(error?.localizedDescription ?? "标签清除失败")
345
+ }
346
+ }
347
+ }
348
+
349
+ /**
350
+ * 检查权限
351
+ */
352
+ @objc public override func checkPermissions(_ call: CAPPluginCall) {
353
+ UNUserNotificationCenter.current().getNotificationSettings { settings in
354
+ DispatchQueue.main.async {
355
+ var permissionStatus: String = "denied"
356
+
357
+ switch settings.authorizationStatus {
358
+ case .notDetermined:
359
+ permissionStatus = "prompt"
360
+ case .authorized:
361
+ permissionStatus = "granted"
362
+ case .denied:
363
+ permissionStatus = "denied"
364
+ case .provisional:
365
+ permissionStatus = "granted"
366
+ case .ephemeral:
367
+ permissionStatus = "granted"
368
+ @unknown default:
369
+ permissionStatus = "denied"
370
+ }
371
+
372
+ call.resolve(["permission": permissionStatus])
373
+ }
374
+ }
375
+ }
376
+
377
+ /**
378
+ * 请求权限
379
+ */
380
+ @objc public override func requestPermissions(_ call: CAPPluginCall) {
381
+ let center = UNUserNotificationCenter.current()
382
+
383
+ center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
384
+ DispatchQueue.main.async {
385
+ if let error = error {
386
+ call.reject("请求通知权限失败: \(error.localizedDescription)")
387
+ } else {
388
+ let permissionStatus: String = granted ? "granted" : "denied"
389
+ call.resolve(["permission": permissionStatus])
390
+
391
+ // 如果用户授权,需要注册APNs
392
+ if granted {
393
+ DispatchQueue.main.async {
394
+ UIApplication.shared.registerForRemoteNotifications()
395
+ }
396
+ }
397
+ }
398
+ }
399
+ }
400
+ }
401
+
402
+ /**
403
+ * 设置未读角标数量
404
+ */
405
+ @objc func setBadge(_ call: CAPPluginCall) {
406
+ let badge = call.getInt("badge", 0)
407
+
408
+ // 设置角标数量
409
+ JPushWrapper.setBadge(Int(badge))
410
+
411
+ call.resolve()
412
+ }
413
+
414
+ // MARK: - JPUSHRegisterDelegate协议方法
415
+
416
+ /**
417
+ * 前台收到通知时调用
418
+ */
419
+ public func jpushNotificationCenter(_ center: UNUserNotificationCenter!, willPresent notification: UNNotification!, withCompletionHandler completionHandler: ((NSInteger) -> Void)!) {
420
+
421
+ self.handleNotification(request: notification.request, isClick: false)
422
+
423
+ // 显示通知(使用默认行为)
424
+ completionHandler(NSInteger(UNNotificationPresentationOptions.banner.rawValue | UNNotificationPresentationOptions.sound.rawValue | UNNotificationPresentationOptions.badge.rawValue))
425
+ }
426
+
427
+ /**
428
+ * 用户点击通知时调用
429
+ */
430
+ public func jpushNotificationCenter(_ center: UNUserNotificationCenter!, didReceive response: UNNotificationResponse!, withCompletionHandler completionHandler: (() -> Void)!) {
431
+
432
+ // 测试结论:
433
+ // 通过点击通知启动App, applicationState = background;App在后台时,点击通知打开App,applicationState = inactive;
434
+ // bridge?.showAlertWith(title: "applicationState", message: String(describing: UIApplication.shared.applicationState), buttonTitle: "confirm")
435
+
436
+ self.handleNotification(request: response.notification.request, isClick: true)
437
+
438
+ // 完成处理
439
+ completionHandler()
440
+ }
441
+
442
+ /**
443
+ * 打开通知设置时调用(iOS 12.0+)
444
+ */
445
+ public func jpushNotificationCenter(_ center: UNUserNotificationCenter!, openSettingsFor notification: UNNotification!) {
446
+ // 可以在这里处理打开通知设置的逻辑
447
+ }
448
+
449
+ /**
450
+ * 监测通知授权状态返回的结果
451
+ */
452
+ public func jpushNotificationAuthorization(_ status: JPAuthorizationStatus, withInfo info: [AnyHashable : Any]!) {
453
+ // 可以在这里处理通知授权状态变化的逻辑
454
+ }
455
+
456
+ // MARK: - 私有方法
457
+
458
+ private func handleNotification(request: UNNotificationRequest, isClick: Bool) {
459
+ let userInfo = request.content.userInfo
460
+ if let trigger = request.trigger, trigger.isKind(of: UNPushNotificationTrigger.self) {
461
+ JPushWrapper.handleRemoteNotification(userInfo)
462
+ }
463
+
464
+ UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [request.identifier])
465
+
466
+ /*
467
+ {
468
+ "mutable-content": 1,
469
+ "alert": {
470
+ "body": "地震预警6.6级",
471
+ "title": "重要情报"
472
+ },
473
+ "sound": "default",
474
+ "interruption-level": "active",
475
+ "content-available": false,
476
+ "badge": 1,
477
+ "body": "地震预警6.6级",
478
+ "thread-id": "",
479
+ "title": "重要情报",
480
+ "extras": {
481
+ "_j_data_": {
482
+ "data_msgtype": 1,
483
+ "push_type": 8,
484
+ "is_vip": 0
485
+ },
486
+ "_j_business": 1,
487
+ "_j_msgid": 18102787182097624,
488
+ "_j_uid": 67885102067
489
+ }
490
+ }
491
+ */
492
+
493
+ // 检查是否是JPush的通知
494
+ if let aps = userInfo["aps"] as? [String: Any] {
495
+ var notificationData: [String: Any] = aps
496
+
497
+ // 添加额外的数据
498
+ if let alert = aps["alert"] as? [String: String] {
499
+ notificationData["title"] = alert["title"]
500
+ notificationData["body"] = alert["body"]
501
+ } else if let alert = aps["alert"] as? String {
502
+ notificationData["title"] = ""
503
+ notificationData["body"] = alert
504
+ }
505
+
506
+ // 添加自定义字段
507
+ notificationData["extras"] = userInfo.filter { $0.key as! String != "aps" }
508
+
509
+ if isClick {
510
+ // 用户点击通知
511
+ notifyListeners(JPushEventName.notificationOpened, data: notificationData, retainUntilConsumed: true)
512
+ } else {
513
+ // 收到通知
514
+ notifyListeners(JPushEventName.notificationReceived, data: notificationData)
515
+ }
516
+ }
517
+
518
+ // 处理自定义消息
519
+ if userInfo["msg_content"] != nil {
520
+ let messageData: [String: Any] = userInfo as! [String : Any]
521
+ notifyListeners(JPushEventName.customMessageReceived, data: messageData)
522
+ }
523
+ }
524
+ }