capacitor-push-signal 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.
Files changed (38) hide show
  1. package/CapacitorPushSignal.podspec +19 -0
  2. package/Package.swift +36 -0
  3. package/README.md +271 -0
  4. package/android/build.gradle +66 -0
  5. package/android/src/main/AndroidManifest.xml +19 -0
  6. package/android/src/main/java/com/antonseagull/cap/push/signal/PushModels.kt +15 -0
  7. package/android/src/main/java/com/antonseagull/cap/push/signal/PushSignalCenter.kt +397 -0
  8. package/android/src/main/java/com/antonseagull/cap/push/signal/PushSignalInitProvider.kt +34 -0
  9. package/android/src/main/java/com/antonseagull/cap/push/signal/PushSignalMessagingService.kt +20 -0
  10. package/android/src/main/java/com/antonseagull/cap/push/signal/PushSignalPlugin.kt +85 -0
  11. package/android/src/main/res/.gitkeep +0 -0
  12. package/dist/docs.json +275 -0
  13. package/dist/esm/definitions.d.ts +19 -0
  14. package/dist/esm/definitions.js +2 -0
  15. package/dist/esm/definitions.js.map +1 -0
  16. package/dist/esm/index.d.ts +2 -0
  17. package/dist/esm/index.js +2 -0
  18. package/dist/esm/index.js.map +1 -0
  19. package/dist/esm/pushSignal.d.ts +8 -0
  20. package/dist/esm/pushSignal.js +76 -0
  21. package/dist/esm/pushSignal.js.map +1 -0
  22. package/dist/esm/types.d.ts +20 -0
  23. package/dist/esm/types.js +2 -0
  24. package/dist/esm/types.js.map +1 -0
  25. package/dist/esm/web.d.ts +8 -0
  26. package/dist/esm/web.js +13 -0
  27. package/dist/esm/web.js.map +1 -0
  28. package/dist/plugin.cjs.js +100 -0
  29. package/dist/plugin.cjs.js.map +1 -0
  30. package/dist/plugin.js +103 -0
  31. package/dist/plugin.js.map +1 -0
  32. package/ios/Sources/PushSignalCore/PushSignalCenter.m +583 -0
  33. package/ios/Sources/PushSignalCore/PushSignalLaunchStore.m +38 -0
  34. package/ios/Sources/PushSignalCore/include/PushSignalCenter.h +26 -0
  35. package/ios/Sources/PushSignalCore/include/PushSignalLaunchStore.h +21 -0
  36. package/ios/Sources/PushSignalPlugin/PushSignalPlugin.swift +66 -0
  37. package/ios/Tests/PushSignalPluginTests/PushSignalTests.swift +9 -0
  38. package/package.json +80 -0
@@ -0,0 +1,19 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'CapacitorPushSignal'
7
+ s.version = package['version']
8
+ s.summary = package['description']
9
+ s.license = package['license']
10
+ s.homepage = package['repository']['url']
11
+ s.author = package['author']
12
+ s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
13
+ s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
14
+ s.public_header_files = 'ios/Sources/PushSignalCore/include/*.h'
15
+ s.ios.deployment_target = '15.0'
16
+ s.dependency 'Capacitor'
17
+ s.frameworks = 'UIKit', 'UserNotifications'
18
+ s.swift_version = '5.1'
19
+ end
package/Package.swift ADDED
@@ -0,0 +1,36 @@
1
+ // swift-tools-version: 5.9
2
+ import PackageDescription
3
+
4
+ let package = Package(
5
+ name: "CapacitorPushSignal",
6
+ platforms: [.iOS(.v15)],
7
+ products: [
8
+ .library(
9
+ name: "CapacitorPushSignal",
10
+ targets: ["PushSignalPlugin"])
11
+ ],
12
+ dependencies: [
13
+ .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0")
14
+ ],
15
+ targets: [
16
+ .target(
17
+ name: "PushSignalCore",
18
+ path: "ios/Sources/PushSignalCore",
19
+ publicHeadersPath: "include",
20
+ cSettings: [
21
+ .headerSearchPath("include")
22
+ ]),
23
+ .target(
24
+ name: "PushSignalPlugin",
25
+ dependencies: [
26
+ "PushSignalCore",
27
+ .product(name: "Capacitor", package: "capacitor-swift-pm"),
28
+ .product(name: "Cordova", package: "capacitor-swift-pm")
29
+ ],
30
+ path: "ios/Sources/PushSignalPlugin"),
31
+ .testTarget(
32
+ name: "PushSignalPluginTests",
33
+ dependencies: ["PushSignalPlugin", "PushSignalCore"],
34
+ path: "ios/Tests/PushSignalPluginTests")
35
+ ]
36
+ )
package/README.md ADDED
@@ -0,0 +1,271 @@
1
+ # capacitor-push-signal
2
+
3
+ Get device credentials for your server and listen for incoming notifications or taps. The plugin does not send pushes — your backend talks to APNs and FCM. Request notification permission yourself (for example with [`@capacitor/push-notifications`](https://capacitorjs.com/docs/apis/push-notifications) permission APIs or platform APIs).
4
+
5
+ The public JS API matches [`react-native-push-signal`](https://github.com/AntonSeagull/react-native-push-signal): `initialize`, `getCredentials`, `onMessage`, `onNotificationPress`.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install capacitor-push-signal
11
+ npx cap sync
12
+ ```
13
+
14
+ ## Usage
15
+
16
+ ```ts
17
+ import {
18
+ getCredentials,
19
+ initialize,
20
+ onMessage,
21
+ onNotificationPress,
22
+ } from 'capacitor-push-signal';
23
+
24
+ await initialize({
25
+ project_id: 'my-project',
26
+ mobilesdk_app_id: '1:123456789:android:abcd',
27
+ current_key: 'AIza...',
28
+ project_number: '123456789',
29
+ });
30
+
31
+ // Request notification permission with your preferred API before getCredentials().
32
+
33
+ const credentials = await getCredentials();
34
+ // POST credentials to your server: { platform, token, environment? }
35
+
36
+ const stopMessages = onMessage((message) => {
37
+ console.log('incoming', message);
38
+ });
39
+
40
+ const stopPress = onNotificationPress((message) => {
41
+ console.log('opened from notification', message);
42
+ });
43
+ ```
44
+
45
+ `onNotificationPress` also delivers the tap that launched the app if you subscribe after startup.
46
+
47
+ ### What to send to your server
48
+
49
+ | Platform | `credentials.token` | Server sends through |
50
+ | --- | --- | --- |
51
+ | iOS | APNs device token (hex) | Apple APNs HTTP/2 |
52
+ | Android | FCM registration token | Firebase Cloud Messaging HTTP v1 |
53
+
54
+ Keep Apple `.p8` keys and the Firebase service account on the server. The app never sees them.
55
+
56
+ `environment` is iOS-only: `sandbox` for development builds, `production` for TestFlight / App Store.
57
+
58
+ `platform` is `'ios'` or `'android'`.
59
+
60
+ ## iOS setup
61
+
62
+ 1. Enable **Push Notifications** on the app target.
63
+ 2. Enable **Background Modes → Remote notifications**.
64
+ 3. Use a physical device. The simulator cannot register with APNs.
65
+
66
+ The plugin hooks `UNUserNotificationCenter` at launch (before JS starts) and APNs token callbacks, so the host `AppDelegate` does not need extra code. Foreground pushes only reach JS if this delegate is installed before launch finishes.
67
+
68
+ ## Android setup
69
+
70
+ Remote Android pushes go through FCM. You can either pass the client Firebase fields at runtime or use `google-services.json`.
71
+
72
+ ### Runtime config (no file in the APK)
73
+
74
+ Call `initialize` with values from `google-services.json` (not a service account):
75
+
76
+ ```ts
77
+ await initialize({
78
+ project_id: '...',
79
+ mobilesdk_app_id: '...',
80
+ current_key: '...',
81
+ project_number: '...',
82
+ });
83
+ ```
84
+
85
+ All four fields are required for Firebase to start. If any is missing, `initialize` resolves and does nothing. iOS ignores the config and still resolves the promise.
86
+
87
+ Keep the Firebase service account on your server. Do not put it in the app.
88
+
89
+ ### google-services.json
90
+
91
+ Alternatively, add Firebase to the host app:
92
+
93
+ 1. Place `google-services.json` in `android/app`.
94
+ 2. Apply the Google Services plugin in `android/app/build.gradle`:
95
+
96
+ ```gradle
97
+ apply plugin: "com.google.gms.google-services"
98
+ ```
99
+
100
+ 3. Add the plugin classpath in `android/build.gradle` / `settings.gradle` as in the [Firebase Android setup](https://firebase.google.com/docs/android/setup).
101
+
102
+ Without `initialize(...)` or that file, `getCredentials()` throws.
103
+
104
+ If the host app already declares its own `FirebaseMessagingService`, only one service can handle `com.google.firebase.MESSAGING_EVENT`. Prefer this plugin’s service or forward events into it.
105
+
106
+ ## Incoming messages vs taps
107
+
108
+ | App state | iOS | Android (notification payload) | Android (data-only) |
109
+ | --- | --- | --- | --- |
110
+ | Foreground | `onMessage` + system banner | `onMessage` + tray (if title/body) | `onMessage` + tray (if title/body) |
111
+ | Background / killed | System banner. Tap → `onNotificationPress` | System banner. Tap → `onNotificationPress` | No banner. `onMessage` if the process is alive |
112
+
113
+ - `onMessage` — the push arrived while the app is in the foreground (and Android data messages while the process is alive). Listeners only receive the payload; they do not control the banner.
114
+ - `onNotificationPress` — the user opened the notification, including a cold start.
115
+ - On iOS, a visible push received in the background or when the app is killed is delivered on tap, not through `onMessage`. That is an OS limit.
116
+
117
+ ## Web
118
+
119
+ `initialize()` resolves. `getCredentials()` throws. Listeners (`onMessage`, `onNotificationPress`) are no-ops.
120
+
121
+ ## API
122
+
123
+ <docgen-index>
124
+
125
+ * [`initialize(...)`](#initialize)
126
+ * [`getCredentials()`](#getcredentials)
127
+ * [`startListening()`](#startlistening)
128
+ * [`addListener('onMessage', ...)`](#addlisteneronmessage-)
129
+ * [`addListener('onNotificationPress', ...)`](#addlisteneronnotificationpress-)
130
+ * [Interfaces](#interfaces)
131
+ * [Type Aliases](#type-aliases)
132
+
133
+ </docgen-index>
134
+
135
+ <docgen-api>
136
+ <!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
137
+
138
+ ### initialize(...)
139
+
140
+ ```typescript
141
+ initialize(config?: AndroidFirebaseConfig | undefined) => Promise<void>
142
+ ```
143
+
144
+ Bootstrap Android Firebase from client fields (no-op on iOS / incomplete config).
145
+
146
+ | Param | Type |
147
+ | ------------ | ----------------------------------------------------------------------- |
148
+ | **`config`** | <code><a href="#androidfirebaseconfig">AndroidFirebaseConfig</a></code> |
149
+
150
+ --------------------
151
+
152
+
153
+ ### getCredentials()
154
+
155
+ ```typescript
156
+ getCredentials() => Promise<PushCredentials>
157
+ ```
158
+
159
+ Returns APNs (iOS) or FCM (Android) credentials for your server.
160
+
161
+ **Returns:** <code>Promise&lt;<a href="#pushcredentials">PushCredentials</a>&gt;</code>
162
+
163
+ --------------------
164
+
165
+
166
+ ### startListening()
167
+
168
+ ```typescript
169
+ startListening() => Promise<void>
170
+ ```
171
+
172
+ Start delivering native message / press events to JS listeners.
173
+
174
+ --------------------
175
+
176
+
177
+ ### addListener('onMessage', ...)
178
+
179
+ ```typescript
180
+ addListener(eventName: 'onMessage', listenerFunc: (message: PushMessage) => void) => Promise<PluginListenerHandle>
181
+ ```
182
+
183
+ | Param | Type |
184
+ | ------------------ | ------------------------------------------------------------------------- |
185
+ | **`eventName`** | <code>'onMessage'</code> |
186
+ | **`listenerFunc`** | <code>(message: <a href="#pushmessage">PushMessage</a>) =&gt; void</code> |
187
+
188
+ **Returns:** <code>Promise&lt;<a href="#pluginlistenerhandle">PluginListenerHandle</a>&gt;</code>
189
+
190
+ --------------------
191
+
192
+
193
+ ### addListener('onNotificationPress', ...)
194
+
195
+ ```typescript
196
+ addListener(eventName: 'onNotificationPress', listenerFunc: (message: PushMessage) => void) => Promise<PluginListenerHandle>
197
+ ```
198
+
199
+ | Param | Type |
200
+ | ------------------ | ------------------------------------------------------------------------- |
201
+ | **`eventName`** | <code>'onNotificationPress'</code> |
202
+ | **`listenerFunc`** | <code>(message: <a href="#pushmessage">PushMessage</a>) =&gt; void</code> |
203
+
204
+ **Returns:** <code>Promise&lt;<a href="#pluginlistenerhandle">PluginListenerHandle</a>&gt;</code>
205
+
206
+ --------------------
207
+
208
+
209
+ ### Interfaces
210
+
211
+
212
+ #### AndroidFirebaseConfig
213
+
214
+ | Prop | Type |
215
+ | ---------------------- | ------------------- |
216
+ | **`project_id`** | <code>string</code> |
217
+ | **`mobilesdk_app_id`** | <code>string</code> |
218
+ | **`current_key`** | <code>string</code> |
219
+ | **`project_number`** | <code>string</code> |
220
+
221
+
222
+ #### PushCredentials
223
+
224
+ | Prop | Type |
225
+ | ----------------- | ----------------------------------------------------------- |
226
+ | **`platform`** | <code><a href="#pushplatform">PushPlatform</a></code> |
227
+ | **`token`** | <code>string</code> |
228
+ | **`environment`** | <code><a href="#pushenvironment">PushEnvironment</a></code> |
229
+
230
+
231
+ #### PluginListenerHandle
232
+
233
+ | Prop | Type |
234
+ | ------------ | ----------------------------------------- |
235
+ | **`remove`** | <code>() =&gt; Promise&lt;void&gt;</code> |
236
+
237
+
238
+ #### PushMessage
239
+
240
+ | Prop | Type |
241
+ | ----------- | --------------------------------------------------------------- |
242
+ | **`id`** | <code>string</code> |
243
+ | **`title`** | <code>string</code> |
244
+ | **`body`** | <code>string</code> |
245
+ | **`data`** | <code><a href="#record">Record</a>&lt;string, string&gt;</code> |
246
+
247
+
248
+ ### Type Aliases
249
+
250
+
251
+ #### PushPlatform
252
+
253
+ <code>'ios' | 'android'</code>
254
+
255
+
256
+ #### PushEnvironment
257
+
258
+ <code>'sandbox' | 'production'</code>
259
+
260
+
261
+ #### Record
262
+
263
+ Construct a type with a set of properties K of type T
264
+
265
+ <code>{
266
  [P in K]: T;
1
267
  }</code>
268
+
269
+ </docgen-api>
270
+
271
+ ## License
272
+
273
+ MIT
@@ -0,0 +1,66 @@
1
+ ext {
2
+ junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
3
+ androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
4
+ androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
5
+ androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
6
+ }
7
+
8
+ buildscript {
9
+ ext.kotlin_version = project.hasProperty('kotlin_version') ? rootProject.ext.kotlin_version : '2.0.21'
10
+ repositories {
11
+ google()
12
+ mavenCentral()
13
+ }
14
+ dependencies {
15
+ classpath 'com.android.tools.build:gradle:8.13.0'
16
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
17
+ }
18
+ }
19
+
20
+ apply plugin: 'com.android.library'
21
+ apply plugin: 'kotlin-android'
22
+
23
+ android {
24
+ namespace = "com.antonseagull.cap.push.signal"
25
+ compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
26
+ defaultConfig {
27
+ minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
28
+ targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36
29
+ versionCode 1
30
+ versionName "1.0"
31
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
32
+ }
33
+ buildTypes {
34
+ release {
35
+ minifyEnabled false
36
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
37
+ }
38
+ }
39
+ lintOptions {
40
+ abortOnError = false
41
+ }
42
+ compileOptions {
43
+ sourceCompatibility JavaVersion.VERSION_21
44
+ targetCompatibility JavaVersion.VERSION_21
45
+ }
46
+ kotlinOptions {
47
+ jvmTarget = '21'
48
+ }
49
+ }
50
+
51
+ repositories {
52
+ google()
53
+ mavenCentral()
54
+ }
55
+
56
+ dependencies {
57
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
58
+ implementation project(':capacitor-android')
59
+ implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
60
+ implementation "androidx.activity:activity-ktx:1.10.1"
61
+ implementation "androidx.core:core-ktx:1.16.0"
62
+ implementation "com.google.firebase:firebase-messaging:24.1.2"
63
+ testImplementation "junit:junit:$junitVersion"
64
+ androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
65
+ androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
66
+ }
@@ -0,0 +1,19 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
3
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
4
+
5
+ <application>
6
+ <provider
7
+ android:name="com.antonseagull.cap.push.signal.PushSignalInitProvider"
8
+ android:authorities="${applicationId}.pushsignal.init"
9
+ android:exported="false" />
10
+
11
+ <service
12
+ android:name="com.antonseagull.cap.push.signal.PushSignalMessagingService"
13
+ android:exported="false">
14
+ <intent-filter>
15
+ <action android:name="com.google.firebase.MESSAGING_EVENT" />
16
+ </intent-filter>
17
+ </service>
18
+ </application>
19
+ </manifest>
@@ -0,0 +1,15 @@
1
+ package com.antonseagull.cap.push.signal
2
+
3
+ data class PushMessage(
4
+ val id: String?,
5
+ val title: String?,
6
+ val body: String?,
7
+ val data: Map<String, String>,
8
+ )
9
+
10
+ data class AndroidFirebaseConfig(
11
+ val project_id: String?,
12
+ val mobilesdk_app_id: String?,
13
+ val current_key: String?,
14
+ val project_number: String?,
15
+ )