@symbiote-native/application 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 (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +223 -0
  3. package/build/angular/index.d.ts +1 -0
  4. package/build/angular/index.js +4 -0
  5. package/build/core/application.d.ts +77 -0
  6. package/build/core/application.js +119 -0
  7. package/build/core/index.d.ts +2 -0
  8. package/build/core/index.js +2 -0
  9. package/build/core/native-module.d.ts +15 -0
  10. package/build/core/native-module.js +3 -0
  11. package/build/core/types.d.ts +17 -0
  12. package/build/core/types.js +16 -0
  13. package/build/react/index.d.ts +1 -0
  14. package/build/react/index.js +5 -0
  15. package/build/vue/index.d.ts +1 -0
  16. package/build/vue/index.js +4 -0
  17. package/build-ngc/angular/index.d.ts +1 -0
  18. package/build-ngc/angular/index.js +5 -0
  19. package/build-ngc/angular/index.js.map +1 -0
  20. package/build-ngc/core/application.d.ts +77 -0
  21. package/build-ngc/core/application.js +120 -0
  22. package/build-ngc/core/application.js.map +1 -0
  23. package/build-ngc/core/index.d.ts +2 -0
  24. package/build-ngc/core/index.js +3 -0
  25. package/build-ngc/core/index.js.map +1 -0
  26. package/build-ngc/core/native-module.d.ts +15 -0
  27. package/build-ngc/core/native-module.js +4 -0
  28. package/build-ngc/core/native-module.js.map +1 -0
  29. package/build-ngc/core/types.d.ts +17 -0
  30. package/build-ngc/core/types.js +17 -0
  31. package/build-ngc/core/types.js.map +1 -0
  32. package/native-link.json +12 -0
  33. package/package.json +107 -0
  34. package/src/angular/index.ts +4 -0
  35. package/src/core/application.test.ts +150 -0
  36. package/src/core/application.ts +134 -0
  37. package/src/core/index.ts +14 -0
  38. package/src/core/native-module.ts +26 -0
  39. package/src/core/types.ts +22 -0
  40. package/src/react/index.ts +5 -0
  41. package/src/vue/index.ts +4 -0
@@ -0,0 +1,120 @@
1
+ import { Platform, UnavailabilityError } from 'expo-modules-core';
2
+ import { expoApplication } from './native-module';
3
+ const NATIVE_MODULE_NAME = 'expo-application';
4
+ /**
5
+ * The human-readable version of the native application that may be displayed in the app store.
6
+ * On Android, this is the version name set by `version` in the app config; on iOS, the
7
+ * `Info.plist` value for `CFBundleShortVersionString`.
8
+ * @example `"2.11.0"`
9
+ */
10
+ export const nativeApplicationVersion = expoApplication.nativeApplicationVersion ?? null;
11
+ /**
12
+ * The internal build version of the native application that the app stores may use to
13
+ * distinguish between different binaries. On Android, this is `android.versionCode`; on iOS,
14
+ * the `Info.plist` value for `CFBundleVersion` (`ios.buildNumber` in the app config).
15
+ * @example `"114"`
16
+ */
17
+ export const nativeBuildVersion = expoApplication.nativeBuildVersion ?? null;
18
+ /**
19
+ * The human-readable name of the application, displayed with the app's icon on the device's
20
+ * home screen.
21
+ * @example `"Expo"`, `"Yelp"`, `"Instagram"`
22
+ */
23
+ export const applicationName = expoApplication.applicationName ?? null;
24
+ /**
25
+ * The ID of the application. On Android, this is the application ID. On iOS, this is the
26
+ * bundle ID.
27
+ * @example `"com.cocoacasts.scribbles"`, `"com.apple.Pages"`
28
+ */
29
+ export const applicationId = expoApplication.applicationId ?? null;
30
+ /**
31
+ * Gets the value of `Settings.Secure.ANDROID_ID` — a hexadecimal string unique to each
32
+ * combination of app-signing key, user, and device. The value may change if a factory reset is
33
+ * performed on the device or if an APK signing key changes.
34
+ * @example `Application.getAndroidId(); // "dd96dec43fb81c97"`
35
+ * @platform android
36
+ */
37
+ export function getAndroidId() {
38
+ if (Platform.OS !== 'android') {
39
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'androidId');
40
+ }
41
+ if (!expoApplication.androidId) {
42
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'androidId');
43
+ }
44
+ return expoApplication.androidId;
45
+ }
46
+ /**
47
+ * Gets the referrer URL of the installed app with the Install Referrer API from the Google Play
48
+ * Store. In practice, the referrer URL may not be a complete, absolute URL.
49
+ * @example `await Application.getInstallReferrerAsync(); // "utm_source=google-play&utm_medium=organic"`
50
+ * @platform android
51
+ */
52
+ export async function getInstallReferrerAsync() {
53
+ if (!expoApplication.getInstallReferrerAsync) {
54
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getInstallReferrerAsync');
55
+ }
56
+ return expoApplication.getInstallReferrerAsync();
57
+ }
58
+ /**
59
+ * Gets the iOS "identifier for vendor" (IDFV) — a string ID that uniquely identifies a device to
60
+ * the app's vendor. May resolve `null`, in which case wait and call again later (this can happen
61
+ * when the device was restarted before the user unlocked it). The OS changes the vendor
62
+ * identifier once every app from the current vendor has been uninstalled.
63
+ * @example `await Application.getIosIdForVendorAsync(); // "68753A44-4D6F-1226-9C60-0050E4C00067"`
64
+ * @platform ios
65
+ */
66
+ export async function getIosIdForVendorAsync() {
67
+ if (!expoApplication.getIosIdForVendorAsync) {
68
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getIosIdForVendorAsync');
69
+ }
70
+ return expoApplication.getIosIdForVendorAsync();
71
+ }
72
+ /**
73
+ * Gets the iOS application release type.
74
+ * @platform ios
75
+ */
76
+ export async function getIosApplicationReleaseTypeAsync() {
77
+ if (!expoApplication.getApplicationReleaseTypeAsync) {
78
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getApplicationReleaseTypeAsync');
79
+ }
80
+ return expoApplication.getApplicationReleaseTypeAsync();
81
+ }
82
+ /**
83
+ * Gets the current Apple Push Notification (APN) service environment.
84
+ * @return Either `'development'` or `'production'`, or `null` on the simulator (which does not
85
+ * support registering with APNs).
86
+ * @platform ios
87
+ */
88
+ export async function getIosPushNotificationServiceEnvironmentAsync() {
89
+ if (!expoApplication.getPushNotificationServiceEnvironmentAsync) {
90
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getPushNotificationServiceEnvironmentAsync');
91
+ }
92
+ return expoApplication.getPushNotificationServiceEnvironmentAsync();
93
+ }
94
+ /**
95
+ * Gets the time the app was installed onto the device, not counting subsequent updates. If the
96
+ * app is uninstalled and reinstalled, this returns the time of the reinstall.
97
+ * - Android: `PackageInfo.firstInstallTime`.
98
+ * - iOS: the `NSFileCreationDate` of the app's document root directory.
99
+ * @example `await Application.getInstallationTimeAsync(); // 2019-07-18T18:08:26.121Z`
100
+ */
101
+ export async function getInstallationTimeAsync() {
102
+ if (!expoApplication.getInstallationTimeAsync) {
103
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getInstallationTimeAsync');
104
+ }
105
+ const installationTime = await expoApplication.getInstallationTimeAsync();
106
+ return new Date(installationTime);
107
+ }
108
+ /**
109
+ * Gets the last time the app was updated from the Google Play Store.
110
+ * @example `await Application.getLastUpdateTimeAsync(); // 2019-07-18T21:20:16.887Z`
111
+ * @platform android
112
+ */
113
+ export async function getLastUpdateTimeAsync() {
114
+ if (!expoApplication.getLastUpdateTimeAsync) {
115
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getLastUpdateTimeAsync');
116
+ }
117
+ const lastUpdateTime = await expoApplication.getLastUpdateTimeAsync();
118
+ return new Date(lastUpdateTime);
119
+ }
120
+ //# sourceMappingURL=application.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"application.js","sourceRoot":"","sources":["../../src/core/application.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { nativeApplicationVersion, nativeBuildVersion, applicationName, applicationId, getAndroidId, getInstallReferrerAsync, getIosIdForVendorAsync, getIosApplicationReleaseTypeAsync, getIosPushNotificationServiceEnvironmentAsync, getInstallationTimeAsync, getLastUpdateTimeAsync, } from './application';
2
+ export { ApplicationReleaseType, type PushNotificationServiceEnvironment } from './types';
@@ -0,0 +1,3 @@
1
+ export { nativeApplicationVersion, nativeBuildVersion, applicationName, applicationId, getAndroidId, getInstallReferrerAsync, getIosIdForVendorAsync, getIosApplicationReleaseTypeAsync, getIosPushNotificationServiceEnvironmentAsync, getInstallationTimeAsync, getLastUpdateTimeAsync, } from './application';
2
+ export { ApplicationReleaseType } from './types';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,YAAY,EACZ,uBAAuB,EACvB,sBAAsB,EACtB,iCAAiC,EACjC,6CAA6C,EAC7C,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,sBAAsB,EAA2C,MAAM,SAAS,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { ApplicationReleaseType, PushNotificationServiceEnvironment } from './types';
2
+ export type INativeApplicationModule = {
3
+ nativeApplicationVersion?: string | null;
4
+ nativeBuildVersion?: string | null;
5
+ applicationName?: string | null;
6
+ applicationId?: string | null;
7
+ androidId?: string;
8
+ getInstallReferrerAsync?(): Promise<string>;
9
+ getIosIdForVendorAsync?(): Promise<string | null>;
10
+ getApplicationReleaseTypeAsync?(): Promise<ApplicationReleaseType>;
11
+ getPushNotificationServiceEnvironmentAsync?(): Promise<PushNotificationServiceEnvironment>;
12
+ getInstallationTimeAsync?(): Promise<number>;
13
+ getLastUpdateTimeAsync?(): Promise<number>;
14
+ };
15
+ export declare const expoApplication: INativeApplicationModule;
@@ -0,0 +1,4 @@
1
+ import { requireNativeModule } from 'expo-modules-core';
2
+ const EXPO_APPLICATION_MODULE_NAME = 'ExpoApplication';
3
+ export const expoApplication = requireNativeModule(EXPO_APPLICATION_MODULE_NAME);
4
+ //# sourceMappingURL=native-module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAGxD,MAAM,4BAA4B,GAAG,iBAAiB,CAAC;AAoBvD,MAAM,CAAC,MAAM,eAAe,GAAG,mBAAmB,CAChD,4BAA4B,CAC7B,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The type of release for the app that is currently running.
3
+ * @platform ios
4
+ */
5
+ export declare enum ApplicationReleaseType {
6
+ UNKNOWN = 0,
7
+ SIMULATOR = 1,
8
+ ENTERPRISE = 2,
9
+ DEVELOPMENT = 3,
10
+ AD_HOC = 4,
11
+ APP_STORE = 5
12
+ }
13
+ /**
14
+ * Maps to the `aps-environment` key in the native target's registered entitlements.
15
+ * @platform ios
16
+ */
17
+ export type PushNotificationServiceEnvironment = 'development' | 'production' | null;
@@ -0,0 +1,17 @@
1
+ // Upstream ships two iOS-only types describing the shape of a running app build — the
2
+ // release channel it was built through and the current APN environment. Ported verbatim
3
+ // from expo-application's Application.types.ts (sdk-57).
4
+ /**
5
+ * The type of release for the app that is currently running.
6
+ * @platform ios
7
+ */
8
+ export var ApplicationReleaseType;
9
+ (function (ApplicationReleaseType) {
10
+ ApplicationReleaseType[ApplicationReleaseType["UNKNOWN"] = 0] = "UNKNOWN";
11
+ ApplicationReleaseType[ApplicationReleaseType["SIMULATOR"] = 1] = "SIMULATOR";
12
+ ApplicationReleaseType[ApplicationReleaseType["ENTERPRISE"] = 2] = "ENTERPRISE";
13
+ ApplicationReleaseType[ApplicationReleaseType["DEVELOPMENT"] = 3] = "DEVELOPMENT";
14
+ ApplicationReleaseType[ApplicationReleaseType["AD_HOC"] = 4] = "AD_HOC";
15
+ ApplicationReleaseType[ApplicationReleaseType["APP_STORE"] = 5] = "APP_STORE";
16
+ })(ApplicationReleaseType || (ApplicationReleaseType = {}));
17
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,wFAAwF;AACxF,yDAAyD;AAEzD;;;GAGG;AACH,MAAM,CAAN,IAAY,sBAOX;AAPD,WAAY,sBAAsB;IAChC,yEAAW,CAAA;IACX,6EAAa,CAAA;IACb,+EAAc,CAAA;IACd,iFAAe,CAAA;IACf,uEAAU,CAAA;IACV,6EAAa,CAAA;AACf,CAAC,EAPW,sBAAsB,KAAtB,sBAAsB,QAOjC"}
@@ -0,0 +1,12 @@
1
+ {
2
+ "android": {
3
+ "gradleProjectName": "expo-application",
4
+ "modules": [
5
+ {
6
+ "importPath": "expo.modules.application.ApplicationModule",
7
+ "className": "ApplicationModule",
8
+ "nativeName": "ExpoApplication"
9
+ }
10
+ ]
11
+ }
12
+ }
package/package.json ADDED
@@ -0,0 +1,107 @@
1
+ {
2
+ "name": "@symbiote-native/application",
3
+ "version": "0.0.1",
4
+ "description": "expo-application wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, and Angular adapters. Native app version/build/name/ID constants, the Android ID, install-referrer and install/update-time lookups, and the iOS vendor ID, release type, and push-notification-service environment.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/OneEyed1366/symbiote-native.git",
9
+ "directory": "packages/application"
10
+ },
11
+ "homepage": "https://github.com/OneEyed1366/symbiote-native/tree/master/packages/application#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/OneEyed1366/symbiote-native/issues"
14
+ },
15
+ "author": "Andrey Prokopenko <psevdoproger@gmail.com>",
16
+ "type": "module",
17
+ "main": "./build/core/index.js",
18
+ "module": "./build/core/index.js",
19
+ "types": "./build/core/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./build/core/index.d.ts",
23
+ "default": "./build/core/index.js"
24
+ },
25
+ "./vue": {
26
+ "types": "./build/vue/index.d.ts",
27
+ "default": "./build/vue/index.js"
28
+ },
29
+ "./react": {
30
+ "types": "./build/react/index.d.ts",
31
+ "default": "./build/react/index.js"
32
+ },
33
+ "./angular": {
34
+ "types": "./build-ngc/angular/index.d.ts",
35
+ "react-native": "./build-ngc/angular/index.js",
36
+ "default": "./src/angular/index.ts"
37
+ }
38
+ },
39
+ "files": [
40
+ "src",
41
+ "build",
42
+ "build-ngc",
43
+ "native-link.json"
44
+ ],
45
+ "publishConfig": {
46
+ "access": "public"
47
+ },
48
+ "dependencies": {
49
+ "expo-application": "57.0.2",
50
+ "expo-modules-core": "57.0.5"
51
+ },
52
+ "peerDependencies": {
53
+ "@symbiote-native/engine": ">=0.1.7",
54
+ "@angular/core": ">=20",
55
+ "@vue/runtime-core": "^3.5.13",
56
+ "react": ">=19.0.0",
57
+ "react-native": ">=0.86",
58
+ "vue": ">=3.5.0",
59
+ "@symbiote-native/angular": "0.6.1",
60
+ "@symbiote-native/react": "0.2.8",
61
+ "@symbiote-native/vue": "0.3.8"
62
+ },
63
+ "peerDependenciesMeta": {
64
+ "@symbiote-native/angular": {
65
+ "optional": true
66
+ },
67
+ "@symbiote-native/react": {
68
+ "optional": true
69
+ },
70
+ "@symbiote-native/vue": {
71
+ "optional": true
72
+ },
73
+ "@angular/core": {
74
+ "optional": true
75
+ },
76
+ "@vue/runtime-core": {
77
+ "optional": true
78
+ },
79
+ "react": {
80
+ "optional": true
81
+ },
82
+ "vue": {
83
+ "optional": true
84
+ }
85
+ },
86
+ "devDependencies": {
87
+ "@angular/compiler": "^22",
88
+ "@angular/compiler-cli": "^22",
89
+ "@angular/core": "^22",
90
+ "@types/node": "^26.0.0",
91
+ "@types/react": "^19.2.0",
92
+ "@vue/runtime-core": "^3.5.13",
93
+ "react": "19.2.3",
94
+ "typescript": "~6.0.0",
95
+ "@symbiote-native/angular": "0.6.1",
96
+ "@symbiote-native/engine": "0.1.7",
97
+ "@symbiote-native/react": "0.2.8",
98
+ "@symbiote-native/test-utils": "0.1.6",
99
+ "@symbiote-native/vue": "0.3.8"
100
+ },
101
+ "scripts": {
102
+ "typecheck": "tsc --build",
103
+ "clean": "rm -rf build-ngc",
104
+ "ng:build": "pnpm run clean && ngc -p tsconfig.angular.json",
105
+ "format": "prettier --write \"src/**/*.{ts,tsx}\""
106
+ }
107
+ }
@@ -0,0 +1,4 @@
1
+ // @symbiote-native/application/angular: the Angular entry over the framework-agnostic core.
2
+ // Same reasoning as the React/Vue entries — no per-instance state or event stream to wrap in a
3
+ // service, so this is a plain re-export.
4
+ export * from '../core';
@@ -0,0 +1,150 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest';
2
+
3
+ const FAKE_NATIVE_APPLICATION = {
4
+ nativeApplicationVersion: '2.11.0',
5
+ nativeBuildVersion: '114',
6
+ applicationName: 'Canary',
7
+ applicationId: 'com.symbiote.canary',
8
+ androidId: 'dd96dec43fb81c97',
9
+ getInstallReferrerAsync: vi.fn(async () => 'utm_source=google-play&utm_medium=organic'),
10
+ getIosIdForVendorAsync: vi.fn(async () => '68753A44-4D6F-1226-9C60-0050E4C00067'),
11
+ getApplicationReleaseTypeAsync: vi.fn(async () => 5),
12
+ getPushNotificationServiceEnvironmentAsync: vi.fn(async () => 'production' as const),
13
+ getInstallationTimeAsync: vi.fn(async () => 1_563_473_306_121),
14
+ getLastUpdateTimeAsync: vi.fn(async () => 1_563_484_816_887),
15
+ };
16
+
17
+ const fakePlatform = { OS: 'android' as 'ios' | 'android' };
18
+
19
+ // The real ExpoApplication native module only exists on device — resolving it via
20
+ // requireNativeModule() at import time would throw in this headless test run, so the
21
+ // module-lookup file is faked in place of expo-modules-core's runtime resolution, the same
22
+ // pattern packages/local-auth/src/core/local-authentication.test.ts uses.
23
+ vi.mock('./native-module', () => ({
24
+ expoApplication: FAKE_NATIVE_APPLICATION,
25
+ }));
26
+
27
+ // expo-modules-core's real entry transitively imports 'react-native' for Platform/
28
+ // TurboModuleRegistry, whose Flow-typed source Vitest's Oxc transform can't parse — same fake
29
+ // packages/local-auth/src/core/local-authentication.test.ts uses.
30
+ vi.mock('expo-modules-core', () => ({
31
+ Platform: fakePlatform,
32
+ UnavailabilityError: class UnavailabilityError extends Error {
33
+ constructor(moduleName: string, propertyName: string) {
34
+ super(`${propertyName} is not available on ${moduleName}`);
35
+ }
36
+ },
37
+ }));
38
+
39
+ const {
40
+ nativeApplicationVersion,
41
+ nativeBuildVersion,
42
+ applicationName,
43
+ applicationId,
44
+ getAndroidId,
45
+ getInstallReferrerAsync,
46
+ getIosIdForVendorAsync,
47
+ getIosApplicationReleaseTypeAsync,
48
+ getIosPushNotificationServiceEnvironmentAsync,
49
+ getInstallationTimeAsync,
50
+ getLastUpdateTimeAsync,
51
+ } = await import('./application');
52
+
53
+ afterEach(() => {
54
+ fakePlatform.OS = 'android';
55
+ vi.clearAllMocks();
56
+ });
57
+
58
+ describe('constants', () => {
59
+ it('resolves eagerly from the native module', () => {
60
+ expect(nativeApplicationVersion).toBe('2.11.0');
61
+ expect(nativeBuildVersion).toBe('114');
62
+ expect(applicationName).toBe('Canary');
63
+ expect(applicationId).toBe('com.symbiote.canary');
64
+ });
65
+ });
66
+
67
+ describe('getAndroidId', () => {
68
+ it('returns the native androidId on android', () => {
69
+ expect(getAndroidId()).toBe('dd96dec43fb81c97');
70
+ });
71
+
72
+ it('throws an UnavailabilityError-shaped error on a non-android platform', () => {
73
+ fakePlatform.OS = 'ios';
74
+ expect(() => getAndroidId()).toThrow('androidId is not available on expo-application');
75
+ });
76
+ });
77
+
78
+ describe('getInstallReferrerAsync', () => {
79
+ it('delegates to the native module', async () => {
80
+ await expect(getInstallReferrerAsync()).resolves.toBe(
81
+ 'utm_source=google-play&utm_medium=organic',
82
+ );
83
+ });
84
+
85
+ it('throws an UnavailabilityError-shaped error when the native method is absent', async () => {
86
+ const { getInstallReferrerAsync: native } = FAKE_NATIVE_APPLICATION;
87
+ // @ts-expect-error -- simulating a platform where the native module has no such method
88
+ FAKE_NATIVE_APPLICATION.getInstallReferrerAsync = undefined;
89
+
90
+ await expect(getInstallReferrerAsync()).rejects.toThrow(
91
+ 'getInstallReferrerAsync is not available on expo-application',
92
+ );
93
+
94
+ FAKE_NATIVE_APPLICATION.getInstallReferrerAsync = native;
95
+ });
96
+ });
97
+
98
+ describe('getIosIdForVendorAsync', () => {
99
+ it('delegates to the native module', async () => {
100
+ await expect(getIosIdForVendorAsync()).resolves.toBe('68753A44-4D6F-1226-9C60-0050E4C00067');
101
+ });
102
+ });
103
+
104
+ describe('getIosApplicationReleaseTypeAsync', () => {
105
+ it('delegates to the native module', async () => {
106
+ await expect(getIosApplicationReleaseTypeAsync()).resolves.toBe(5);
107
+ });
108
+
109
+ it('throws an UnavailabilityError-shaped error when the native method is absent', async () => {
110
+ const { getApplicationReleaseTypeAsync: native } = FAKE_NATIVE_APPLICATION;
111
+ // @ts-expect-error -- simulating a platform where the native module has no such method
112
+ FAKE_NATIVE_APPLICATION.getApplicationReleaseTypeAsync = undefined;
113
+
114
+ await expect(getIosApplicationReleaseTypeAsync()).rejects.toThrow(
115
+ 'getApplicationReleaseTypeAsync is not available on expo-application',
116
+ );
117
+
118
+ FAKE_NATIVE_APPLICATION.getApplicationReleaseTypeAsync = native;
119
+ });
120
+ });
121
+
122
+ describe('getIosPushNotificationServiceEnvironmentAsync', () => {
123
+ it('delegates to the native module', async () => {
124
+ await expect(getIosPushNotificationServiceEnvironmentAsync()).resolves.toBe('production');
125
+ });
126
+ });
127
+
128
+ describe('getInstallationTimeAsync', () => {
129
+ it('wraps the native epoch-ms number into a Date', async () => {
130
+ await expect(getInstallationTimeAsync()).resolves.toEqual(new Date(1_563_473_306_121));
131
+ });
132
+ });
133
+
134
+ describe('getLastUpdateTimeAsync', () => {
135
+ it('wraps the native epoch-ms number into a Date', async () => {
136
+ await expect(getLastUpdateTimeAsync()).resolves.toEqual(new Date(1_563_484_816_887));
137
+ });
138
+
139
+ it('throws an UnavailabilityError-shaped error when the native method is absent', async () => {
140
+ const { getLastUpdateTimeAsync: native } = FAKE_NATIVE_APPLICATION;
141
+ // @ts-expect-error -- simulating iOS, where getLastUpdateTimeAsync has no native implementation
142
+ FAKE_NATIVE_APPLICATION.getLastUpdateTimeAsync = undefined;
143
+
144
+ await expect(getLastUpdateTimeAsync()).rejects.toThrow(
145
+ 'getLastUpdateTimeAsync is not available on expo-application',
146
+ );
147
+
148
+ FAKE_NATIVE_APPLICATION.getLastUpdateTimeAsync = native;
149
+ });
150
+ });
@@ -0,0 +1,134 @@
1
+ import { Platform, UnavailabilityError } from 'expo-modules-core';
2
+
3
+ import { expoApplication } from './native-module';
4
+ import type { ApplicationReleaseType, PushNotificationServiceEnvironment } from './types';
5
+
6
+ const NATIVE_MODULE_NAME = 'expo-application';
7
+
8
+ /**
9
+ * The human-readable version of the native application that may be displayed in the app store.
10
+ * On Android, this is the version name set by `version` in the app config; on iOS, the
11
+ * `Info.plist` value for `CFBundleShortVersionString`.
12
+ * @example `"2.11.0"`
13
+ */
14
+ export const nativeApplicationVersion: string | null =
15
+ expoApplication.nativeApplicationVersion ?? null;
16
+
17
+ /**
18
+ * The internal build version of the native application that the app stores may use to
19
+ * distinguish between different binaries. On Android, this is `android.versionCode`; on iOS,
20
+ * the `Info.plist` value for `CFBundleVersion` (`ios.buildNumber` in the app config).
21
+ * @example `"114"`
22
+ */
23
+ export const nativeBuildVersion: string | null = expoApplication.nativeBuildVersion ?? null;
24
+
25
+ /**
26
+ * The human-readable name of the application, displayed with the app's icon on the device's
27
+ * home screen.
28
+ * @example `"Expo"`, `"Yelp"`, `"Instagram"`
29
+ */
30
+ export const applicationName: string | null = expoApplication.applicationName ?? null;
31
+
32
+ /**
33
+ * The ID of the application. On Android, this is the application ID. On iOS, this is the
34
+ * bundle ID.
35
+ * @example `"com.cocoacasts.scribbles"`, `"com.apple.Pages"`
36
+ */
37
+ export const applicationId: string | null = expoApplication.applicationId ?? null;
38
+
39
+ /**
40
+ * Gets the value of `Settings.Secure.ANDROID_ID` — a hexadecimal string unique to each
41
+ * combination of app-signing key, user, and device. The value may change if a factory reset is
42
+ * performed on the device or if an APK signing key changes.
43
+ * @example `Application.getAndroidId(); // "dd96dec43fb81c97"`
44
+ * @platform android
45
+ */
46
+ export function getAndroidId(): string {
47
+ if (Platform.OS !== 'android') {
48
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'androidId');
49
+ }
50
+ if (!expoApplication.androidId) {
51
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'androidId');
52
+ }
53
+ return expoApplication.androidId;
54
+ }
55
+
56
+ /**
57
+ * Gets the referrer URL of the installed app with the Install Referrer API from the Google Play
58
+ * Store. In practice, the referrer URL may not be a complete, absolute URL.
59
+ * @example `await Application.getInstallReferrerAsync(); // "utm_source=google-play&utm_medium=organic"`
60
+ * @platform android
61
+ */
62
+ export async function getInstallReferrerAsync(): Promise<string> {
63
+ if (!expoApplication.getInstallReferrerAsync) {
64
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getInstallReferrerAsync');
65
+ }
66
+ return expoApplication.getInstallReferrerAsync();
67
+ }
68
+
69
+ /**
70
+ * Gets the iOS "identifier for vendor" (IDFV) — a string ID that uniquely identifies a device to
71
+ * the app's vendor. May resolve `null`, in which case wait and call again later (this can happen
72
+ * when the device was restarted before the user unlocked it). The OS changes the vendor
73
+ * identifier once every app from the current vendor has been uninstalled.
74
+ * @example `await Application.getIosIdForVendorAsync(); // "68753A44-4D6F-1226-9C60-0050E4C00067"`
75
+ * @platform ios
76
+ */
77
+ export async function getIosIdForVendorAsync(): Promise<string | null> {
78
+ if (!expoApplication.getIosIdForVendorAsync) {
79
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getIosIdForVendorAsync');
80
+ }
81
+ return expoApplication.getIosIdForVendorAsync();
82
+ }
83
+
84
+ /**
85
+ * Gets the iOS application release type.
86
+ * @platform ios
87
+ */
88
+ export async function getIosApplicationReleaseTypeAsync(): Promise<ApplicationReleaseType> {
89
+ if (!expoApplication.getApplicationReleaseTypeAsync) {
90
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getApplicationReleaseTypeAsync');
91
+ }
92
+ return expoApplication.getApplicationReleaseTypeAsync();
93
+ }
94
+
95
+ /**
96
+ * Gets the current Apple Push Notification (APN) service environment.
97
+ * @return Either `'development'` or `'production'`, or `null` on the simulator (which does not
98
+ * support registering with APNs).
99
+ * @platform ios
100
+ */
101
+ export async function getIosPushNotificationServiceEnvironmentAsync(): Promise<PushNotificationServiceEnvironment> {
102
+ if (!expoApplication.getPushNotificationServiceEnvironmentAsync) {
103
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getPushNotificationServiceEnvironmentAsync');
104
+ }
105
+ return expoApplication.getPushNotificationServiceEnvironmentAsync();
106
+ }
107
+
108
+ /**
109
+ * Gets the time the app was installed onto the device, not counting subsequent updates. If the
110
+ * app is uninstalled and reinstalled, this returns the time of the reinstall.
111
+ * - Android: `PackageInfo.firstInstallTime`.
112
+ * - iOS: the `NSFileCreationDate` of the app's document root directory.
113
+ * @example `await Application.getInstallationTimeAsync(); // 2019-07-18T18:08:26.121Z`
114
+ */
115
+ export async function getInstallationTimeAsync(): Promise<Date> {
116
+ if (!expoApplication.getInstallationTimeAsync) {
117
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getInstallationTimeAsync');
118
+ }
119
+ const installationTime = await expoApplication.getInstallationTimeAsync();
120
+ return new Date(installationTime);
121
+ }
122
+
123
+ /**
124
+ * Gets the last time the app was updated from the Google Play Store.
125
+ * @example `await Application.getLastUpdateTimeAsync(); // 2019-07-18T21:20:16.887Z`
126
+ * @platform android
127
+ */
128
+ export async function getLastUpdateTimeAsync(): Promise<Date> {
129
+ if (!expoApplication.getLastUpdateTimeAsync) {
130
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getLastUpdateTimeAsync');
131
+ }
132
+ const lastUpdateTime = await expoApplication.getLastUpdateTimeAsync();
133
+ return new Date(lastUpdateTime);
134
+ }
@@ -0,0 +1,14 @@
1
+ export {
2
+ nativeApplicationVersion,
3
+ nativeBuildVersion,
4
+ applicationName,
5
+ applicationId,
6
+ getAndroidId,
7
+ getInstallReferrerAsync,
8
+ getIosIdForVendorAsync,
9
+ getIosApplicationReleaseTypeAsync,
10
+ getIosPushNotificationServiceEnvironmentAsync,
11
+ getInstallationTimeAsync,
12
+ getLastUpdateTimeAsync,
13
+ } from './application';
14
+ export { ApplicationReleaseType, type PushNotificationServiceEnvironment } from './types';
@@ -0,0 +1,26 @@
1
+ import { requireNativeModule } from 'expo-modules-core';
2
+ import type { ApplicationReleaseType, PushNotificationServiceEnvironment } from './types';
3
+
4
+ const EXPO_APPLICATION_MODULE_NAME = 'ExpoApplication';
5
+
6
+ // Every method is optional — each call site checks for its presence before calling through and
7
+ // throws an UnavailabilityError itself, matching upstream's own per-platform capability checks
8
+ // rather than assuming the native module implements the whole surface. Same convention as
9
+ // packages/local-auth/src/core/native-module.ts.
10
+ export type INativeApplicationModule = {
11
+ nativeApplicationVersion?: string | null;
12
+ nativeBuildVersion?: string | null;
13
+ applicationName?: string | null;
14
+ applicationId?: string | null;
15
+ androidId?: string;
16
+ getInstallReferrerAsync?(): Promise<string>;
17
+ getIosIdForVendorAsync?(): Promise<string | null>;
18
+ getApplicationReleaseTypeAsync?(): Promise<ApplicationReleaseType>;
19
+ getPushNotificationServiceEnvironmentAsync?(): Promise<PushNotificationServiceEnvironment>;
20
+ getInstallationTimeAsync?(): Promise<number>;
21
+ getLastUpdateTimeAsync?(): Promise<number>;
22
+ };
23
+
24
+ export const expoApplication = requireNativeModule<INativeApplicationModule>(
25
+ EXPO_APPLICATION_MODULE_NAME,
26
+ );