expo-app-lifecycle-plus 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +5 -0
- package/LICENSE +21 -0
- package/README.md +269 -0
- package/android/build.gradle +43 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/expo/modules/applifecycleplus/ExpoAppLifecyclePlusModule.kt +91 -0
- package/build/ExpoAppLifecyclePlus.types.d.ts +17 -0
- package/build/ExpoAppLifecyclePlus.types.d.ts.map +1 -0
- package/build/ExpoAppLifecyclePlus.types.js +2 -0
- package/build/ExpoAppLifecyclePlus.types.js.map +1 -0
- package/build/ExpoAppLifecyclePlusModule.d.ts +8 -0
- package/build/ExpoAppLifecyclePlusModule.d.ts.map +1 -0
- package/build/ExpoAppLifecyclePlusModule.js +3 -0
- package/build/ExpoAppLifecyclePlusModule.js.map +1 -0
- package/build/index.d.ts +6 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +9 -0
- package/build/index.js.map +1 -0
- package/expo-module.config.json +9 -0
- package/ios/ExpoAppLifecyclePlus.podspec +29 -0
- package/ios/ExpoAppLifecyclePlusModule.swift +167 -0
- package/package.json +43 -0
- package/src/ExpoAppLifecyclePlus.types.ts +32 -0
- package/src/ExpoAppLifecyclePlusModule.ts +9 -0
- package/src/index.ts +14 -0
- package/tsconfig.json +9 -0
package/.eslintrc.js
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright 2025 Adem Hatay
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
<a id="readme-top"></a>
|
|
2
|
+
|
|
3
|
+
[![Contributors][contributors-shield]][contributors-url]
|
|
4
|
+
[![Forks][forks-shield]][forks-url]
|
|
5
|
+
[![Stargazers][stars-shield]][stars-url]
|
|
6
|
+
[![Issues][issues-shield]][issues-url]
|
|
7
|
+
[![MIT License][license-shield]][license-url]
|
|
8
|
+
|
|
9
|
+
<br />
|
|
10
|
+
<div align="center">
|
|
11
|
+
<h3 align="center">expo-app-lifecycle-plus</h3>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
Native app lifecycle event module for Expo / React Native on Android and iOS.
|
|
15
|
+
<br />
|
|
16
|
+
Android: Process lifecycle + optional activity focus/blur signals
|
|
17
|
+
<br />
|
|
18
|
+
iOS: UIApplication + UIScene notifications with inferred termination support
|
|
19
|
+
<br />
|
|
20
|
+
<a href="https://github.com/ademhatay/expo-app-lifecycle-plus"><strong>Explore the docs »</strong></a>
|
|
21
|
+
<br />
|
|
22
|
+
<br />
|
|
23
|
+
<a href="https://github.com/ademhatay/expo-app-lifecycle-plus/issues/new?labels=bug">Report Bug</a>
|
|
24
|
+
·
|
|
25
|
+
<a href="https://github.com/ademhatay/expo-app-lifecycle-plus/issues/new?labels=enhancement">Request Feature</a>
|
|
26
|
+
</p>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<details>
|
|
30
|
+
<summary>Table of Contents</summary>
|
|
31
|
+
<ol>
|
|
32
|
+
<li>
|
|
33
|
+
<a href="#about-the-project">About The Project</a>
|
|
34
|
+
<ul>
|
|
35
|
+
<li><a href="#built-with">Built With</a></li>
|
|
36
|
+
</ul>
|
|
37
|
+
</li>
|
|
38
|
+
<li>
|
|
39
|
+
<a href="#getting-started">Getting Started</a>
|
|
40
|
+
<ul>
|
|
41
|
+
<li><a href="#prerequisites">Prerequisites</a></li>
|
|
42
|
+
<li><a href="#installation">Installation</a></li>
|
|
43
|
+
</ul>
|
|
44
|
+
</li>
|
|
45
|
+
<li><a href="#usage">Usage</a></li>
|
|
46
|
+
<li><a href="#event-reference">Event Reference</a></li>
|
|
47
|
+
<li><a href="#api-reference">API Reference</a></li>
|
|
48
|
+
<li><a href="#platform-notes">Platform Notes</a></li>
|
|
49
|
+
<li><a href="#troubleshooting">Troubleshooting</a></li>
|
|
50
|
+
<li><a href="#contributing">Contributing</a></li>
|
|
51
|
+
<li><a href="#license">License</a></li>
|
|
52
|
+
</ol>
|
|
53
|
+
</details>
|
|
54
|
+
|
|
55
|
+
## About The Project
|
|
56
|
+
|
|
57
|
+
`expo-app-lifecycle-plus` exposes a single event channel and typed payloads so you can observe app state transitions consistently from JavaScript.
|
|
58
|
+
|
|
59
|
+
It is designed for real-world lifecycle analytics and debugging:
|
|
60
|
+
|
|
61
|
+
- `addListener((event) => ...)` to stream lifecycle events
|
|
62
|
+
- `getCurrentState()` to read current native state snapshot
|
|
63
|
+
- iOS launch/scene signals and inferred termination support
|
|
64
|
+
- Android process foreground/background and optional activity focus/blur
|
|
65
|
+
|
|
66
|
+
### Built With
|
|
67
|
+
|
|
68
|
+
- [Expo Modules](https://docs.expo.dev/modules/overview/)
|
|
69
|
+
- [React Native](https://reactnative.dev/)
|
|
70
|
+
- [AndroidX Lifecycle](https://developer.android.com/topic/libraries/architecture/lifecycle)
|
|
71
|
+
- [UIKit UIApplication Notifications](https://developer.apple.com/documentation/uikit/uiapplication)
|
|
72
|
+
- [UIKit UIScene Notifications](https://developer.apple.com/documentation/uikit/uiscene)
|
|
73
|
+
|
|
74
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
75
|
+
|
|
76
|
+
## Getting Started
|
|
77
|
+
|
|
78
|
+
### Prerequisites
|
|
79
|
+
|
|
80
|
+
- Node.js LTS (`20` or `22` recommended)
|
|
81
|
+
- Expo / React Native app
|
|
82
|
+
|
|
83
|
+
### Installation
|
|
84
|
+
|
|
85
|
+
Expo managed or prebuild:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npx expo install expo-app-lifecycle-plus
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Bare React Native / Expo bare:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
npm install expo-app-lifecycle-plus
|
|
95
|
+
# or
|
|
96
|
+
bun add expo-app-lifecycle-plus
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Rebuild native apps after install:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
npx expo run:android
|
|
103
|
+
npx expo run:ios
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
107
|
+
|
|
108
|
+
## Usage
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
import * as Lifecycle from 'expo-app-lifecycle-plus';
|
|
112
|
+
|
|
113
|
+
const current = Lifecycle.getCurrentState();
|
|
114
|
+
console.log('current state', current);
|
|
115
|
+
|
|
116
|
+
const sub = Lifecycle.addListener((event) => {
|
|
117
|
+
console.log('lifecycle event', event);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// later
|
|
121
|
+
sub.remove();
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Example Event Payload
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"type": "foreground",
|
|
129
|
+
"state": "foreground",
|
|
130
|
+
"timestamp": 1700000000000,
|
|
131
|
+
"platform": "ios"
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
136
|
+
|
|
137
|
+
## Event Reference
|
|
138
|
+
|
|
139
|
+
### Common Events
|
|
140
|
+
|
|
141
|
+
- `jsReload`
|
|
142
|
+
- `coldStart`
|
|
143
|
+
- `foreground`
|
|
144
|
+
- `background`
|
|
145
|
+
|
|
146
|
+
### iOS Events
|
|
147
|
+
|
|
148
|
+
- `appLaunch`
|
|
149
|
+
- `active`
|
|
150
|
+
- `inactive`
|
|
151
|
+
- `sceneActive`
|
|
152
|
+
- `sceneInactive`
|
|
153
|
+
- `willTerminate` (best-effort; not guaranteed)
|
|
154
|
+
- `inferredTermination` (emitted on next launch if app was previously backgrounded and appears to have been killed)
|
|
155
|
+
|
|
156
|
+
### Android Events
|
|
157
|
+
|
|
158
|
+
- `focusActivity`
|
|
159
|
+
- `blurActivity`
|
|
160
|
+
|
|
161
|
+
### Event Type
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
type LifecycleEvent = {
|
|
165
|
+
type:
|
|
166
|
+
| 'jsReload'
|
|
167
|
+
| 'coldStart'
|
|
168
|
+
| 'appLaunch'
|
|
169
|
+
| 'inferredTermination'
|
|
170
|
+
| 'foreground'
|
|
171
|
+
| 'background'
|
|
172
|
+
| 'active'
|
|
173
|
+
| 'inactive'
|
|
174
|
+
| 'willTerminate'
|
|
175
|
+
| 'sceneActive'
|
|
176
|
+
| 'sceneInactive'
|
|
177
|
+
| 'focusActivity'
|
|
178
|
+
| 'blurActivity';
|
|
179
|
+
state: 'unknown' | 'foreground' | 'background' | 'active' | 'inactive';
|
|
180
|
+
timestamp: number;
|
|
181
|
+
platform: 'ios' | 'android';
|
|
182
|
+
activity?: string;
|
|
183
|
+
source?: 'didFinishLaunching' | 'observerStart';
|
|
184
|
+
inferredFrom?: 'previousBackground';
|
|
185
|
+
previousBackgroundTimestamp?: number;
|
|
186
|
+
elapsedSinceBackgroundMs?: number;
|
|
187
|
+
};
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
191
|
+
|
|
192
|
+
## API Reference
|
|
193
|
+
|
|
194
|
+
### `addListener(listener): EventSubscription`
|
|
195
|
+
|
|
196
|
+
Subscribes to native lifecycle updates on `onLifecycleEvent`.
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
addListener(listener: (event: LifecycleEvent) => void): EventSubscription
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### `getCurrentState(): LifecycleState`
|
|
203
|
+
|
|
204
|
+
Returns current native lifecycle state snapshot.
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
getCurrentState(): 'unknown' | 'foreground' | 'background' | 'active' | 'inactive'
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
211
|
+
|
|
212
|
+
## Platform Notes
|
|
213
|
+
|
|
214
|
+
- `willTerminate` is not guaranteed on mobile OSes.
|
|
215
|
+
- On iOS, app kills in background may not emit a terminate callback. Use `inferredTermination` for practical analytics.
|
|
216
|
+
- On Android, process lifecycle (`foreground`/`background`) is generally the most reliable signal.
|
|
217
|
+
- `jsReload` can happen during development due to fast refresh/reload and does not always mean a fresh process launch.
|
|
218
|
+
|
|
219
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
220
|
+
|
|
221
|
+
## Troubleshooting
|
|
222
|
+
|
|
223
|
+
### I only see `inactive -> background -> foreground -> active`
|
|
224
|
+
|
|
225
|
+
This is normal lifecycle flow on iOS when app moves between foreground/background.
|
|
226
|
+
|
|
227
|
+
### `willTerminate` is not emitted
|
|
228
|
+
|
|
229
|
+
Expected on many real-device scenarios. Mobile OS may kill apps without firing terminate callbacks.
|
|
230
|
+
|
|
231
|
+
### `coldStart` appears more than once in development
|
|
232
|
+
|
|
233
|
+
Development reload / fast refresh can recreate JS/runtime state. Use `jsReload` and `appLaunch` together to interpret startup behavior.
|
|
234
|
+
|
|
235
|
+
### TypeScript import errors
|
|
236
|
+
|
|
237
|
+
Rebuild package and restart Metro:
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
npm run build
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
|
244
|
+
|
|
245
|
+
## Contributing
|
|
246
|
+
|
|
247
|
+
PRs and issues are welcome.
|
|
248
|
+
|
|
249
|
+
1. Fork the project
|
|
250
|
+
2. Create branch
|
|
251
|
+
3. Commit changes
|
|
252
|
+
4. Push branch
|
|
253
|
+
5. Open PR
|
|
254
|
+
|
|
255
|
+
## License
|
|
256
|
+
|
|
257
|
+
MIT © Adem Hatay
|
|
258
|
+
|
|
259
|
+
<!-- MARKDOWN LINKS & IMAGES -->
|
|
260
|
+
[contributors-shield]: https://img.shields.io/github/contributors/ademhatay/expo-app-lifecycle-plus.svg?style=for-the-badge
|
|
261
|
+
[contributors-url]: https://github.com/ademhatay/expo-app-lifecycle-plus/graphs/contributors
|
|
262
|
+
[forks-shield]: https://img.shields.io/github/forks/ademhatay/expo-app-lifecycle-plus.svg?style=for-the-badge
|
|
263
|
+
[forks-url]: https://github.com/ademhatay/expo-app-lifecycle-plus/network/members
|
|
264
|
+
[stars-shield]: https://img.shields.io/github/stars/ademhatay/expo-app-lifecycle-plus.svg?style=for-the-badge
|
|
265
|
+
[stars-url]: https://github.com/ademhatay/expo-app-lifecycle-plus/stargazers
|
|
266
|
+
[issues-shield]: https://img.shields.io/github/issues/ademhatay/expo-app-lifecycle-plus.svg?style=for-the-badge
|
|
267
|
+
[issues-url]: https://github.com/ademhatay/expo-app-lifecycle-plus/issues
|
|
268
|
+
[license-shield]: https://img.shields.io/github/license/ademhatay/expo-app-lifecycle-plus.svg?style=for-the-badge
|
|
269
|
+
[license-url]: https://github.com/ademhatay/expo-app-lifecycle-plus/blob/main/LICENSE
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
apply plugin: 'com.android.library'
|
|
2
|
+
|
|
3
|
+
group = 'expo.modules.applifecycleplus'
|
|
4
|
+
version = '0.1.0'
|
|
5
|
+
|
|
6
|
+
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
7
|
+
apply from: expoModulesCorePlugin
|
|
8
|
+
applyKotlinExpoModulesCorePlugin()
|
|
9
|
+
useCoreDependencies()
|
|
10
|
+
useExpoPublishing()
|
|
11
|
+
|
|
12
|
+
// If you want to use the managed Android SDK versions from expo-modules-core, set this to true.
|
|
13
|
+
// The Android SDK versions will be bumped from time to time in SDK releases and may introduce breaking changes in your module code.
|
|
14
|
+
// Most of the time, you may like to manage the Android SDK versions yourself.
|
|
15
|
+
def useManagedAndroidSdkVersions = false
|
|
16
|
+
if (useManagedAndroidSdkVersions) {
|
|
17
|
+
useDefaultAndroidSdkVersions()
|
|
18
|
+
} else {
|
|
19
|
+
buildscript {
|
|
20
|
+
// Simple helper that allows the root project to override versions declared by this library.
|
|
21
|
+
ext.safeExtGet = { prop, fallback ->
|
|
22
|
+
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
project.android {
|
|
26
|
+
compileSdkVersion safeExtGet("compileSdkVersion", 36)
|
|
27
|
+
defaultConfig {
|
|
28
|
+
minSdkVersion safeExtGet("minSdkVersion", 24)
|
|
29
|
+
targetSdkVersion safeExtGet("targetSdkVersion", 36)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
android {
|
|
35
|
+
namespace "expo.modules.applifecycleplus"
|
|
36
|
+
defaultConfig {
|
|
37
|
+
versionCode 1
|
|
38
|
+
versionName "0.1.0"
|
|
39
|
+
}
|
|
40
|
+
lintOptions {
|
|
41
|
+
abortOnError false
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
package expo.modules.applifecycleplus
|
|
2
|
+
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.app.Application
|
|
5
|
+
import android.os.Bundle
|
|
6
|
+
import androidx.lifecycle.DefaultLifecycleObserver
|
|
7
|
+
import androidx.lifecycle.LifecycleOwner
|
|
8
|
+
import androidx.lifecycle.ProcessLifecycleOwner
|
|
9
|
+
import expo.modules.kotlin.modules.Module
|
|
10
|
+
import expo.modules.kotlin.modules.ModuleDefinition
|
|
11
|
+
|
|
12
|
+
class ExpoAppLifecyclePlusModule : Module(), DefaultLifecycleObserver, Application.ActivityLifecycleCallbacks {
|
|
13
|
+
private var didSendStartupEvents = false
|
|
14
|
+
private var currentState: String = "unknown"
|
|
15
|
+
private var application: Application? = null
|
|
16
|
+
|
|
17
|
+
override fun definition() = ModuleDefinition {
|
|
18
|
+
Name("ExpoAppLifecyclePlus")
|
|
19
|
+
|
|
20
|
+
Events("onLifecycleEvent")
|
|
21
|
+
|
|
22
|
+
OnCreate {
|
|
23
|
+
ProcessLifecycleOwner.get().lifecycle.addObserver(this@ExpoAppLifecyclePlusModule)
|
|
24
|
+
|
|
25
|
+
val app = appContext.reactContext?.applicationContext as? Application
|
|
26
|
+
application = app
|
|
27
|
+
app?.registerActivityLifecycleCallbacks(this@ExpoAppLifecyclePlusModule)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
OnStartObserving {
|
|
31
|
+
if (!didSendStartupEvents) {
|
|
32
|
+
didSendStartupEvents = true
|
|
33
|
+
send("jsReload")
|
|
34
|
+
send("coldStart")
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
Function("getCurrentState") {
|
|
39
|
+
currentState
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
OnDestroy {
|
|
43
|
+
ProcessLifecycleOwner.get().lifecycle.removeObserver(this@ExpoAppLifecyclePlusModule)
|
|
44
|
+
application?.unregisterActivityLifecycleCallbacks(this@ExpoAppLifecyclePlusModule)
|
|
45
|
+
application = null
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private fun send(type: String, extra: Map<String, Any?> = emptyMap()) {
|
|
50
|
+
currentState = when (type) {
|
|
51
|
+
"foreground", "active" -> "foreground"
|
|
52
|
+
"background", "inactive" -> "background"
|
|
53
|
+
else -> currentState
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
val payload = mutableMapOf<String, Any?>(
|
|
57
|
+
"type" to type,
|
|
58
|
+
"state" to currentState,
|
|
59
|
+
"timestamp" to System.currentTimeMillis(),
|
|
60
|
+
"platform" to "android"
|
|
61
|
+
)
|
|
62
|
+
payload.putAll(extra)
|
|
63
|
+
sendEvent("onLifecycleEvent", payload)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
override fun onStart(owner: LifecycleOwner) {
|
|
67
|
+
send("foreground")
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
override fun onStop(owner: LifecycleOwner) {
|
|
71
|
+
send("background")
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
override fun onActivityResumed(activity: Activity) {
|
|
75
|
+
send("focusActivity", mapOf("activity" to activity.javaClass.simpleName))
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
override fun onActivityPaused(activity: Activity) {
|
|
79
|
+
send("blurActivity", mapOf("activity" to activity.javaClass.simpleName))
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
|
|
83
|
+
|
|
84
|
+
override fun onActivityStarted(activity: Activity) {}
|
|
85
|
+
|
|
86
|
+
override fun onActivityStopped(activity: Activity) {}
|
|
87
|
+
|
|
88
|
+
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
|
|
89
|
+
|
|
90
|
+
override fun onActivityDestroyed(activity: Activity) {}
|
|
91
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type LifecycleEventType = 'jsReload' | 'coldStart' | 'appLaunch' | 'inferredTermination' | 'foreground' | 'background' | 'active' | 'inactive' | 'willTerminate' | 'sceneActive' | 'sceneInactive' | 'focusActivity' | 'blurActivity';
|
|
2
|
+
export type LifecycleState = 'unknown' | 'foreground' | 'background' | 'active' | 'inactive';
|
|
3
|
+
export type LifecycleEvent = {
|
|
4
|
+
type: LifecycleEventType;
|
|
5
|
+
state: LifecycleState;
|
|
6
|
+
timestamp: number;
|
|
7
|
+
platform: 'ios' | 'android';
|
|
8
|
+
activity?: string;
|
|
9
|
+
source?: 'didFinishLaunching' | 'observerStart';
|
|
10
|
+
inferredFrom?: 'previousBackground';
|
|
11
|
+
previousBackgroundTimestamp?: number;
|
|
12
|
+
elapsedSinceBackgroundMs?: number;
|
|
13
|
+
};
|
|
14
|
+
export type ExpoAppLifecyclePlusModuleEvents = {
|
|
15
|
+
onLifecycleEvent: (event: LifecycleEvent) => void;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=ExpoAppLifecyclePlus.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpoAppLifecyclePlus.types.d.ts","sourceRoot":"","sources":["../src/ExpoAppLifecyclePlus.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAC1B,UAAU,GACV,WAAW,GACX,WAAW,GACX,qBAAqB,GACrB,YAAY,GACZ,YAAY,GACZ,QAAQ,GACR,UAAU,GACV,eAAe,GACf,aAAa,GACb,eAAe,GACf,eAAe,GACf,cAAc,CAAC;AAEnB,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,YAAY,GAAG,YAAY,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE7F,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,kBAAkB,CAAC;IACzB,KAAK,EAAE,cAAc,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,KAAK,GAAG,SAAS,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,oBAAoB,GAAG,eAAe,CAAC;IAChD,YAAY,CAAC,EAAE,oBAAoB,CAAC;IACpC,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,gCAAgC,GAAG;IAC7C,gBAAgB,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;CACnD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpoAppLifecyclePlus.types.js","sourceRoot":"","sources":["../src/ExpoAppLifecyclePlus.types.ts"],"names":[],"mappings":"","sourcesContent":["export type LifecycleEventType =\n | 'jsReload'\n | 'coldStart'\n | 'appLaunch'\n | 'inferredTermination'\n | 'foreground'\n | 'background'\n | 'active'\n | 'inactive'\n | 'willTerminate'\n | 'sceneActive'\n | 'sceneInactive'\n | 'focusActivity'\n | 'blurActivity';\n\nexport type LifecycleState = 'unknown' | 'foreground' | 'background' | 'active' | 'inactive';\n\nexport type LifecycleEvent = {\n type: LifecycleEventType;\n state: LifecycleState;\n timestamp: number;\n platform: 'ios' | 'android';\n activity?: string;\n source?: 'didFinishLaunching' | 'observerStart';\n inferredFrom?: 'previousBackground';\n previousBackgroundTimestamp?: number;\n elapsedSinceBackgroundMs?: number;\n};\n\nexport type ExpoAppLifecyclePlusModuleEvents = {\n onLifecycleEvent: (event: LifecycleEvent) => void;\n};\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { NativeModule } from 'expo';
|
|
2
|
+
import { ExpoAppLifecyclePlusModuleEvents, LifecycleState } from './ExpoAppLifecyclePlus.types';
|
|
3
|
+
declare class ExpoAppLifecyclePlusModule extends NativeModule<ExpoAppLifecyclePlusModuleEvents> {
|
|
4
|
+
getCurrentState(): LifecycleState;
|
|
5
|
+
}
|
|
6
|
+
declare const _default: ExpoAppLifecyclePlusModule;
|
|
7
|
+
export default _default;
|
|
8
|
+
//# sourceMappingURL=ExpoAppLifecyclePlusModule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpoAppLifecyclePlusModule.d.ts","sourceRoot":"","sources":["../src/ExpoAppLifecyclePlusModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,MAAM,CAAC;AAEzD,OAAO,EAAE,gCAAgC,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAEhG,OAAO,OAAO,0BAA2B,SAAQ,YAAY,CAAC,gCAAgC,CAAC;IAC7F,eAAe,IAAI,cAAc;CAClC;;AAED,wBAAuF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpoAppLifecyclePlusModule.js","sourceRoot":"","sources":["../src/ExpoAppLifecyclePlusModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAQzD,eAAe,mBAAmB,CAA6B,sBAAsB,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo';\n\nimport { ExpoAppLifecyclePlusModuleEvents, LifecycleState } from './ExpoAppLifecyclePlus.types';\n\ndeclare class ExpoAppLifecyclePlusModule extends NativeModule<ExpoAppLifecyclePlusModuleEvents> {\n getCurrentState(): LifecycleState;\n}\n\nexport default requireNativeModule<ExpoAppLifecyclePlusModule>('ExpoAppLifecyclePlus');\n"]}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { EventSubscription } from 'expo-modules-core';
|
|
2
|
+
import { LifecycleEvent, LifecycleState } from './ExpoAppLifecyclePlus.types';
|
|
3
|
+
export * from './ExpoAppLifecyclePlus.types';
|
|
4
|
+
export declare function addListener(listener: (event: LifecycleEvent) => void): EventSubscription;
|
|
5
|
+
export declare function getCurrentState(): LifecycleState;
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGtD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9E,cAAc,8BAA8B,CAAC;AAE7C,wBAAgB,WAAW,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GAAG,iBAAiB,CAExF;AAED,wBAAgB,eAAe,IAAI,cAAc,CAEhD"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import Module from './ExpoAppLifecyclePlusModule';
|
|
2
|
+
export * from './ExpoAppLifecyclePlus.types';
|
|
3
|
+
export function addListener(listener) {
|
|
4
|
+
return Module.addListener('onLifecycleEvent', listener);
|
|
5
|
+
}
|
|
6
|
+
export function getCurrentState() {
|
|
7
|
+
return Module.getCurrentState();
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,MAAM,MAAM,8BAA8B,CAAC;AAGlD,cAAc,8BAA8B,CAAC;AAE7C,MAAM,UAAU,WAAW,CAAC,QAAyC;IACnE,OAAO,MAAM,CAAC,WAAW,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,MAAM,CAAC,eAAe,EAAE,CAAC;AAClC,CAAC","sourcesContent":["import { EventSubscription } from 'expo-modules-core';\n\nimport Module from './ExpoAppLifecyclePlusModule';\nimport { LifecycleEvent, LifecycleState } from './ExpoAppLifecyclePlus.types';\n\nexport * from './ExpoAppLifecyclePlus.types';\n\nexport function addListener(listener: (event: LifecycleEvent) => void): EventSubscription {\n return Module.addListener('onLifecycleEvent', listener);\n}\n\nexport function getCurrentState(): LifecycleState {\n return Module.getCurrentState();\n}\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
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 = 'ExpoAppLifecyclePlus'
|
|
7
|
+
s.version = package['version']
|
|
8
|
+
s.summary = package['description']
|
|
9
|
+
s.description = package['description']
|
|
10
|
+
s.license = package['license']
|
|
11
|
+
s.author = package['author']
|
|
12
|
+
s.homepage = package['homepage']
|
|
13
|
+
s.platforms = {
|
|
14
|
+
:ios => '15.1',
|
|
15
|
+
:tvos => '15.1'
|
|
16
|
+
}
|
|
17
|
+
s.swift_version = '5.9'
|
|
18
|
+
s.source = { git: 'https://github.com/ademhatay/expo-app-lifecycle-plus' }
|
|
19
|
+
s.static_framework = true
|
|
20
|
+
|
|
21
|
+
s.dependency 'ExpoModulesCore'
|
|
22
|
+
|
|
23
|
+
# Swift/Objective-C compatibility
|
|
24
|
+
s.pod_target_xcconfig = {
|
|
25
|
+
'DEFINES_MODULE' => 'YES',
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
s.source_files = "**/*.{h,m,mm,swift,hpp,cpp}"
|
|
29
|
+
end
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
public class ExpoAppLifecyclePlusModule: Module {
|
|
5
|
+
private var didSendStartupEvents = false
|
|
6
|
+
private var didSendAppLaunch = false
|
|
7
|
+
private var observers: [NSObjectProtocol] = []
|
|
8
|
+
private var currentState = "unknown"
|
|
9
|
+
private let defaults = UserDefaults.standard
|
|
10
|
+
private let pendingBackgroundKey = "expo.appLifecyclePlus.pendingBackground"
|
|
11
|
+
private let backgroundTimestampKey = "expo.appLifecyclePlus.backgroundTimestamp"
|
|
12
|
+
|
|
13
|
+
public func definition() -> ModuleDefinition {
|
|
14
|
+
Name("ExpoAppLifecyclePlus")
|
|
15
|
+
|
|
16
|
+
Events("onLifecycleEvent")
|
|
17
|
+
|
|
18
|
+
OnCreate {
|
|
19
|
+
self.currentState = self.readApplicationState()
|
|
20
|
+
let center = NotificationCenter.default
|
|
21
|
+
|
|
22
|
+
self.observers.append(center.addObserver(
|
|
23
|
+
forName: UIApplication.didBecomeActiveNotification,
|
|
24
|
+
object: nil,
|
|
25
|
+
queue: .main
|
|
26
|
+
) { _ in
|
|
27
|
+
self.clearBackgroundMarker()
|
|
28
|
+
self.send(type: "active", nextState: "active")
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
self.observers.append(center.addObserver(
|
|
32
|
+
forName: UIApplication.willResignActiveNotification,
|
|
33
|
+
object: nil,
|
|
34
|
+
queue: .main
|
|
35
|
+
) { _ in self.send(type: "inactive", nextState: "inactive") })
|
|
36
|
+
|
|
37
|
+
self.observers.append(center.addObserver(
|
|
38
|
+
forName: UIApplication.willEnterForegroundNotification,
|
|
39
|
+
object: nil,
|
|
40
|
+
queue: .main
|
|
41
|
+
) { _ in self.send(type: "foreground", nextState: "foreground") })
|
|
42
|
+
|
|
43
|
+
self.observers.append(center.addObserver(
|
|
44
|
+
forName: UIApplication.didEnterBackgroundNotification,
|
|
45
|
+
object: nil,
|
|
46
|
+
queue: .main
|
|
47
|
+
) { _ in
|
|
48
|
+
self.markEnteredBackground()
|
|
49
|
+
self.send(type: "background", nextState: "background")
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
self.observers.append(center.addObserver(
|
|
53
|
+
forName: UIApplication.willTerminateNotification,
|
|
54
|
+
object: nil,
|
|
55
|
+
queue: .main
|
|
56
|
+
) { _ in
|
|
57
|
+
self.clearBackgroundMarker()
|
|
58
|
+
self.send(type: "willTerminate")
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
self.observers.append(center.addObserver(
|
|
62
|
+
forName: UIApplication.didFinishLaunchingNotification,
|
|
63
|
+
object: nil,
|
|
64
|
+
queue: .main
|
|
65
|
+
) { _ in
|
|
66
|
+
self.emitAppLaunch(source: "didFinishLaunching")
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
self.observers.append(center.addObserver(
|
|
70
|
+
forName: UIScene.didActivateNotification,
|
|
71
|
+
object: nil,
|
|
72
|
+
queue: .main
|
|
73
|
+
) { _ in self.send(type: "sceneActive") })
|
|
74
|
+
|
|
75
|
+
self.observers.append(center.addObserver(
|
|
76
|
+
forName: UIScene.willDeactivateNotification,
|
|
77
|
+
object: nil,
|
|
78
|
+
queue: .main
|
|
79
|
+
) { _ in self.send(type: "sceneInactive") })
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
OnStartObserving {
|
|
83
|
+
if !self.didSendStartupEvents {
|
|
84
|
+
self.didSendStartupEvents = true
|
|
85
|
+
self.send(type: "jsReload")
|
|
86
|
+
self.send(type: "coldStart")
|
|
87
|
+
self.emitAppLaunch(source: "observerStart")
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
Function("getCurrentState") {
|
|
92
|
+
self.currentState
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
OnDestroy {
|
|
96
|
+
let center = NotificationCenter.default
|
|
97
|
+
self.observers.forEach { center.removeObserver($0) }
|
|
98
|
+
self.observers.removeAll()
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private func emitAppLaunch(source: String) {
|
|
103
|
+
if didSendAppLaunch {
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
didSendAppLaunch = true
|
|
107
|
+
send(type: "appLaunch", extra: ["source": source])
|
|
108
|
+
emitInferredTerminationIfNeeded()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private func markEnteredBackground() {
|
|
112
|
+
defaults.set(true, forKey: pendingBackgroundKey)
|
|
113
|
+
defaults.set(Int(Date().timeIntervalSince1970 * 1000), forKey: backgroundTimestampKey)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private func clearBackgroundMarker() {
|
|
117
|
+
defaults.set(false, forKey: pendingBackgroundKey)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private func emitInferredTerminationIfNeeded() {
|
|
121
|
+
guard defaults.bool(forKey: pendingBackgroundKey) else {
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
let nowMs = Int(Date().timeIntervalSince1970 * 1000)
|
|
126
|
+
let lastBackgroundAt = defaults.integer(forKey: backgroundTimestampKey)
|
|
127
|
+
let elapsedMs = lastBackgroundAt > 0 ? nowMs - lastBackgroundAt : nil
|
|
128
|
+
|
|
129
|
+
send(
|
|
130
|
+
type: "inferredTermination",
|
|
131
|
+
extra: [
|
|
132
|
+
"inferredFrom": "previousBackground",
|
|
133
|
+
"previousBackgroundTimestamp": lastBackgroundAt > 0 ? lastBackgroundAt : nil,
|
|
134
|
+
"elapsedSinceBackgroundMs": elapsedMs
|
|
135
|
+
]
|
|
136
|
+
)
|
|
137
|
+
clearBackgroundMarker()
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private func readApplicationState() -> String {
|
|
141
|
+
switch UIApplication.shared.applicationState {
|
|
142
|
+
case .active:
|
|
143
|
+
return "active"
|
|
144
|
+
case .inactive:
|
|
145
|
+
return "inactive"
|
|
146
|
+
case .background:
|
|
147
|
+
return "background"
|
|
148
|
+
@unknown default:
|
|
149
|
+
return "unknown"
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private func send(type: String, nextState: String? = nil, extra: [String: Any?] = [:]) {
|
|
154
|
+
if let nextState {
|
|
155
|
+
self.currentState = nextState
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
var payload: [String: Any?] = [
|
|
159
|
+
"type": type,
|
|
160
|
+
"state": self.currentState,
|
|
161
|
+
"timestamp": Int(Date().timeIntervalSince1970 * 1000),
|
|
162
|
+
"platform": "ios"
|
|
163
|
+
]
|
|
164
|
+
extra.forEach { payload[$0.key] = $0.value }
|
|
165
|
+
sendEvent("onLifecycleEvent", payload)
|
|
166
|
+
}
|
|
167
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "expo-app-lifecycle-plus",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "this module handle manage app states",
|
|
5
|
+
"main": "build/index.js",
|
|
6
|
+
"types": "build/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "expo-module build",
|
|
9
|
+
"clean": "expo-module clean",
|
|
10
|
+
"lint": "expo-module lint",
|
|
11
|
+
"test": "expo-module test",
|
|
12
|
+
"prepare": "expo-module prepare",
|
|
13
|
+
"prepublishOnly": "expo-module prepublishOnly",
|
|
14
|
+
"expo-module": "expo-module",
|
|
15
|
+
"open:ios": "xed example/ios",
|
|
16
|
+
"open:android": "open -a \"Android Studio\" example/android"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"react-native",
|
|
20
|
+
"expo",
|
|
21
|
+
"expo-app-lifecycle-plus",
|
|
22
|
+
"ExpoAppLifecyclePlus"
|
|
23
|
+
],
|
|
24
|
+
"repository": "https://github.com/ademhatay/expo-app-lifecycle-plus",
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/ademhatay/expo-app-lifecycle-plus/issues"
|
|
27
|
+
},
|
|
28
|
+
"author": "Adem Hatay <hatayadem5@gmail.com> (https://github.com/ademhatay)",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"homepage": "https://github.com/ademhatay/expo-app-lifecycle-plus#readme",
|
|
31
|
+
"dependencies": {},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/react": "~19.1.0",
|
|
34
|
+
"expo-module-scripts": "^5.0.8",
|
|
35
|
+
"expo": "^54.0.27",
|
|
36
|
+
"react-native": "0.81.5"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"expo": "*",
|
|
40
|
+
"react": "*",
|
|
41
|
+
"react-native": "*"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type LifecycleEventType =
|
|
2
|
+
| 'jsReload'
|
|
3
|
+
| 'coldStart'
|
|
4
|
+
| 'appLaunch'
|
|
5
|
+
| 'inferredTermination'
|
|
6
|
+
| 'foreground'
|
|
7
|
+
| 'background'
|
|
8
|
+
| 'active'
|
|
9
|
+
| 'inactive'
|
|
10
|
+
| 'willTerminate'
|
|
11
|
+
| 'sceneActive'
|
|
12
|
+
| 'sceneInactive'
|
|
13
|
+
| 'focusActivity'
|
|
14
|
+
| 'blurActivity';
|
|
15
|
+
|
|
16
|
+
export type LifecycleState = 'unknown' | 'foreground' | 'background' | 'active' | 'inactive';
|
|
17
|
+
|
|
18
|
+
export type LifecycleEvent = {
|
|
19
|
+
type: LifecycleEventType;
|
|
20
|
+
state: LifecycleState;
|
|
21
|
+
timestamp: number;
|
|
22
|
+
platform: 'ios' | 'android';
|
|
23
|
+
activity?: string;
|
|
24
|
+
source?: 'didFinishLaunching' | 'observerStart';
|
|
25
|
+
inferredFrom?: 'previousBackground';
|
|
26
|
+
previousBackgroundTimestamp?: number;
|
|
27
|
+
elapsedSinceBackgroundMs?: number;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type ExpoAppLifecyclePlusModuleEvents = {
|
|
31
|
+
onLifecycleEvent: (event: LifecycleEvent) => void;
|
|
32
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { NativeModule, requireNativeModule } from 'expo';
|
|
2
|
+
|
|
3
|
+
import { ExpoAppLifecyclePlusModuleEvents, LifecycleState } from './ExpoAppLifecyclePlus.types';
|
|
4
|
+
|
|
5
|
+
declare class ExpoAppLifecyclePlusModule extends NativeModule<ExpoAppLifecyclePlusModuleEvents> {
|
|
6
|
+
getCurrentState(): LifecycleState;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default requireNativeModule<ExpoAppLifecyclePlusModule>('ExpoAppLifecyclePlus');
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { EventSubscription } from 'expo-modules-core';
|
|
2
|
+
|
|
3
|
+
import Module from './ExpoAppLifecyclePlusModule';
|
|
4
|
+
import { LifecycleEvent, LifecycleState } from './ExpoAppLifecyclePlus.types';
|
|
5
|
+
|
|
6
|
+
export * from './ExpoAppLifecyclePlus.types';
|
|
7
|
+
|
|
8
|
+
export function addListener(listener: (event: LifecycleEvent) => void): EventSubscription {
|
|
9
|
+
return Module.addListener('onLifecycleEvent', listener);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getCurrentState(): LifecycleState {
|
|
13
|
+
return Module.getCurrentState();
|
|
14
|
+
}
|
package/tsconfig.json
ADDED