@umituz/react-native-firebase 1.12.2 → 1.13.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.
- package/package.json +1 -1
- package/src/analytics/infrastructure/adapters/native-analytics.adapter.ts +47 -38
- package/src/crashlytics/infrastructure/adapters/native-crashlytics.adapter.ts +67 -59
- package/src/index.ts +3 -0
- package/src/infrastructure/config/FirebaseClient.ts +48 -283
- package/src/infrastructure/config/orchestrators/FirebaseInitializationOrchestrator.ts +84 -0
- package/src/infrastructure/config/services/FirebaseServiceInitializer.ts +69 -0
- package/src/infrastructure/config/state/FirebaseClientState.ts +38 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-firebase",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.1",
|
|
4
4
|
"description": "Unified Firebase package for React Native apps - Centralized initialization and core services (Analytics, Crashlytics).",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -2,16 +2,21 @@
|
|
|
2
2
|
* Native Analytics Adapter
|
|
3
3
|
* Single Responsibility: Handle Firebase Analytics native implementation
|
|
4
4
|
* Uses React Native Firebase v23+ modular API
|
|
5
|
+
*
|
|
6
|
+
* NOTE: This adapter uses optional import to support Expo Go.
|
|
7
|
+
* Native modules are only available in development builds or standalone apps.
|
|
5
8
|
*/
|
|
6
9
|
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
// Optional import - will be null in Expo Go
|
|
11
|
+
let nativeAnalytics: any = null;
|
|
12
|
+
try {
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
14
|
+
nativeAnalytics = require('@react-native-firebase/analytics');
|
|
15
|
+
} catch {
|
|
16
|
+
// Native module not available (e.g., Expo Go)
|
|
17
|
+
// eslint-disable-next-line no-console
|
|
18
|
+
if (__DEV__) console.warn('⚠️ Firebase Analytics: Native module not available');
|
|
19
|
+
}
|
|
15
20
|
|
|
16
21
|
export interface NativeAnalyticsAdapter {
|
|
17
22
|
getAnalytics(): any;
|
|
@@ -22,33 +27,37 @@ export interface NativeAnalyticsAdapter {
|
|
|
22
27
|
setAnalyticsCollectionEnabled(analytics: any, enabled: boolean): Promise<void>;
|
|
23
28
|
}
|
|
24
29
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
30
|
+
declare const __DEV__: boolean;
|
|
31
|
+
|
|
32
|
+
export const nativeAnalyticsAdapter: NativeAnalyticsAdapter | null = nativeAnalytics
|
|
33
|
+
? {
|
|
34
|
+
getAnalytics(): any {
|
|
35
|
+
return nativeAnalytics.getAnalytics();
|
|
36
|
+
},
|
|
37
|
+
async logEvent(
|
|
38
|
+
analytics: any,
|
|
39
|
+
eventName: string,
|
|
40
|
+
params?: Record<string, any>,
|
|
41
|
+
): Promise<void> {
|
|
42
|
+
await nativeAnalytics.logEvent(analytics, eventName, params);
|
|
43
|
+
},
|
|
44
|
+
async setUserId(analytics: any, userId: string): Promise<void> {
|
|
45
|
+
await nativeAnalytics.setUserId(analytics, userId);
|
|
46
|
+
},
|
|
47
|
+
async setUserProperties(
|
|
48
|
+
analytics: any,
|
|
49
|
+
properties: Record<string, string>,
|
|
50
|
+
): Promise<void> {
|
|
51
|
+
await nativeAnalytics.setUserProperties(analytics, properties);
|
|
52
|
+
},
|
|
53
|
+
async resetAnalyticsData(analytics: any): Promise<void> {
|
|
54
|
+
await nativeAnalytics.resetAnalyticsData(analytics);
|
|
55
|
+
},
|
|
56
|
+
async setAnalyticsCollectionEnabled(
|
|
57
|
+
analytics: any,
|
|
58
|
+
enabled: boolean,
|
|
59
|
+
): Promise<void> {
|
|
60
|
+
await nativeAnalytics.setAnalyticsCollectionEnabled(analytics, enabled);
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
: null;
|
|
@@ -2,18 +2,22 @@
|
|
|
2
2
|
* Native Crashlytics Adapter
|
|
3
3
|
* Single Responsibility: Handle Firebase Crashlytics native implementation
|
|
4
4
|
* Uses React Native Firebase v23+ modular API
|
|
5
|
+
*
|
|
6
|
+
* NOTE: This adapter uses optional import to support Expo Go.
|
|
7
|
+
* Native modules are only available in development builds or standalone apps.
|
|
5
8
|
*/
|
|
6
9
|
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
// Optional import - will be null in Expo Go
|
|
11
|
+
let nativeCrashlytics: any = null;
|
|
12
|
+
try {
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
14
|
+
nativeCrashlytics = require('@react-native-firebase/crashlytics');
|
|
15
|
+
} catch {
|
|
16
|
+
// Native module not available (e.g., Expo Go)
|
|
17
|
+
// Silent fail - crashlytics is optional
|
|
18
|
+
}
|
|
15
19
|
|
|
16
|
-
export type CrashlyticsInstance =
|
|
20
|
+
export type CrashlyticsInstance = any;
|
|
17
21
|
|
|
18
22
|
export interface NativeCrashlyticsAdapter {
|
|
19
23
|
getCrashlytics(): CrashlyticsInstance;
|
|
@@ -23,54 +27,58 @@ export interface NativeCrashlyticsAdapter {
|
|
|
23
27
|
log(crashlytics: CrashlyticsInstance, message: string): Promise<void>;
|
|
24
28
|
}
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
30
|
+
declare const __DEV__: boolean;
|
|
31
|
+
|
|
32
|
+
export const nativeCrashlyticsAdapter: NativeCrashlyticsAdapter | null = nativeCrashlytics
|
|
33
|
+
? {
|
|
34
|
+
getCrashlytics(): CrashlyticsInstance {
|
|
35
|
+
return nativeCrashlytics.getCrashlytics();
|
|
36
|
+
},
|
|
37
|
+
async setUserId(crashlytics: CrashlyticsInstance, userId: string): Promise<void> {
|
|
38
|
+
try {
|
|
39
|
+
await nativeCrashlytics.setUserId(crashlytics, userId);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
/* eslint-disable-next-line no-console */
|
|
42
|
+
if (__DEV__) {
|
|
43
|
+
console.warn('[Crashlytics] Failed to set user ID:', error);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
async setAttributes(
|
|
48
|
+
crashlytics: CrashlyticsInstance,
|
|
49
|
+
attributes: Record<string, string>,
|
|
50
|
+
): Promise<void> {
|
|
51
|
+
try {
|
|
52
|
+
// Set each attribute individually using the modular API
|
|
53
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
54
|
+
await nativeCrashlytics.setAttribute(crashlytics, key, value);
|
|
55
|
+
}
|
|
56
|
+
} catch (error) {
|
|
57
|
+
/* eslint-disable-next-line no-console */
|
|
58
|
+
if (__DEV__) {
|
|
59
|
+
console.warn('[Crashlytics] Failed to set attributes:', error);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
async recordError(crashlytics: CrashlyticsInstance, error: Error): Promise<void> {
|
|
64
|
+
try {
|
|
65
|
+
await nativeCrashlytics.recordError(crashlytics, error);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
/* eslint-disable-next-line no-console */
|
|
68
|
+
if (__DEV__) {
|
|
69
|
+
console.warn('[Crashlytics] Failed to record error:', err);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
async log(crashlytics: CrashlyticsInstance, message: string): Promise<void> {
|
|
74
|
+
try {
|
|
75
|
+
await nativeCrashlytics.log(crashlytics, message);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
/* eslint-disable-next-line no-console */
|
|
78
|
+
if (__DEV__) {
|
|
79
|
+
console.warn('[Crashlytics] Failed to log message:', error);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
74
83
|
}
|
|
75
|
-
|
|
76
|
-
};
|
|
84
|
+
: null;
|
package/src/index.ts
CHANGED
|
@@ -38,6 +38,9 @@ export {
|
|
|
38
38
|
|
|
39
39
|
export type {
|
|
40
40
|
FirebaseApp,
|
|
41
|
+
AuthInitializer,
|
|
42
|
+
ServiceInitializationOptions,
|
|
43
|
+
ServiceInitializationResult,
|
|
41
44
|
} from './infrastructure/config/FirebaseClient';
|
|
42
45
|
|
|
43
46
|
// =============================================================================
|
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
* IMPORTANT: This package does NOT read from .env files.
|
|
8
8
|
* Configuration must be provided by the application.
|
|
9
9
|
*
|
|
10
|
+
* NOTE: Auth initialization is handled by the main app via callback.
|
|
11
|
+
* This removes the need for dynamic require() which causes issues in production.
|
|
12
|
+
*
|
|
10
13
|
* SOLID Principles:
|
|
11
14
|
* - Single Responsibility: Only orchestrates initialization, delegates to specialized classes
|
|
12
15
|
* - Open/Closed: Extensible through configuration, closed for modification
|
|
@@ -15,154 +18,28 @@
|
|
|
15
18
|
|
|
16
19
|
import type { FirebaseConfig } from '../../domain/value-objects/FirebaseConfig';
|
|
17
20
|
import type { IFirebaseClient } from '../../application/ports/IFirebaseClient';
|
|
18
|
-
import {
|
|
21
|
+
import type { FirebaseApp } from './initializers/FirebaseAppInitializer';
|
|
22
|
+
import { FirebaseClientState } from './state/FirebaseClientState';
|
|
23
|
+
import { FirebaseInitializationOrchestrator } from './orchestrators/FirebaseInitializationOrchestrator';
|
|
19
24
|
import {
|
|
20
|
-
|
|
21
|
-
type
|
|
22
|
-
|
|
25
|
+
FirebaseServiceInitializer,
|
|
26
|
+
type AuthInitializer,
|
|
27
|
+
type ServiceInitializationOptions,
|
|
28
|
+
} from './services/FirebaseServiceInitializer';
|
|
23
29
|
import { loadFirebaseConfig } from './FirebaseConfigLoader';
|
|
24
|
-
import { firebaseAnalyticsService } from '../../analytics';
|
|
25
|
-
import { firebaseCrashlyticsService } from '../../crashlytics';
|
|
26
30
|
|
|
27
|
-
export type { FirebaseApp };
|
|
31
|
+
export type { FirebaseApp, AuthInitializer, ServiceInitializationOptions };
|
|
28
32
|
|
|
29
|
-
// Development environment check
|
|
30
33
|
declare const __DEV__: boolean;
|
|
31
34
|
|
|
32
35
|
/**
|
|
33
|
-
*
|
|
34
|
-
* Manages the state of Firebase initialization
|
|
35
|
-
*/
|
|
36
|
-
class FirebaseClientState {
|
|
37
|
-
private app: FirebaseApp | null = null;
|
|
38
|
-
private initializationError: string | null = null;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Get the current Firebase app instance
|
|
42
|
-
*/
|
|
43
|
-
getApp(): FirebaseApp | null {
|
|
44
|
-
return this.app;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Set the Firebase app instance
|
|
49
|
-
*/
|
|
50
|
-
setApp(app: FirebaseApp | null): void {
|
|
51
|
-
this.app = app;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Check if client is initialized
|
|
56
|
-
*/
|
|
57
|
-
isInitialized(): boolean {
|
|
58
|
-
return this.app !== null;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Get initialization error if any
|
|
63
|
-
*/
|
|
64
|
-
getInitializationError(): string | null {
|
|
65
|
-
return this.initializationError;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Set initialization error
|
|
70
|
-
*/
|
|
71
|
-
setInitializationError(error: string | null): void {
|
|
72
|
-
this.initializationError = error;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Reset the client state
|
|
77
|
-
*/
|
|
78
|
-
reset(): void {
|
|
79
|
-
this.app = null;
|
|
80
|
-
this.initializationError = null;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Firebase Initialization Orchestrator
|
|
86
|
-
* Handles the initialization logic
|
|
36
|
+
* Service initialization result interface
|
|
87
37
|
*/
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
config: FirebaseConfig,
|
|
94
|
-
state: FirebaseClientState
|
|
95
|
-
): FirebaseApp | null {
|
|
96
|
-
// Return existing instance if already initialized
|
|
97
|
-
if (state.isInitialized()) {
|
|
98
|
-
if (__DEV__) {
|
|
99
|
-
console.log('[Firebase] Already initialized, returning existing instance');
|
|
100
|
-
}
|
|
101
|
-
return state.getApp();
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Don't retry if initialization already failed
|
|
105
|
-
if (state.getInitializationError()) {
|
|
106
|
-
if (__DEV__) {
|
|
107
|
-
console.log('[Firebase] Previous initialization failed, skipping retry');
|
|
108
|
-
}
|
|
109
|
-
return null;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
try {
|
|
113
|
-
if (__DEV__) {
|
|
114
|
-
console.log('[Firebase] Initializing with projectId:', config.projectId);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Validate configuration
|
|
118
|
-
FirebaseConfigValidator.validate(config);
|
|
119
|
-
|
|
120
|
-
// Initialize Firebase App
|
|
121
|
-
const app = FirebaseAppInitializer.initialize(config);
|
|
122
|
-
state.setApp(app);
|
|
123
|
-
|
|
124
|
-
if (__DEV__) {
|
|
125
|
-
console.log('[Firebase] Successfully initialized');
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return app;
|
|
129
|
-
} catch (error) {
|
|
130
|
-
const errorMessage =
|
|
131
|
-
error instanceof Error
|
|
132
|
-
? error.message
|
|
133
|
-
: 'Failed to initialize Firebase client';
|
|
134
|
-
state.setInitializationError(errorMessage);
|
|
135
|
-
|
|
136
|
-
if (__DEV__) {
|
|
137
|
-
console.error('[Firebase] Initialization failed:', errorMessage);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Auto-initialize Firebase from environment
|
|
146
|
-
*/
|
|
147
|
-
static autoInitialize(state: FirebaseClientState): FirebaseApp | null {
|
|
148
|
-
if (state.isInitialized() || state.getInitializationError()) {
|
|
149
|
-
return state.getApp();
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const autoConfig = loadFirebaseConfig();
|
|
153
|
-
if (autoConfig) {
|
|
154
|
-
if (__DEV__) {
|
|
155
|
-
console.log('[Firebase] Auto-initializing with environment config');
|
|
156
|
-
}
|
|
157
|
-
return this.initialize(autoConfig, state);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (__DEV__) {
|
|
161
|
-
console.log('[Firebase] No configuration found for auto-initialization');
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
38
|
+
export interface ServiceInitializationResult {
|
|
39
|
+
app: FirebaseApp | null;
|
|
40
|
+
auth: unknown;
|
|
41
|
+
analytics: unknown;
|
|
42
|
+
crashlytics: unknown;
|
|
166
43
|
}
|
|
167
44
|
|
|
168
45
|
/**
|
|
@@ -177,9 +54,6 @@ class FirebaseClientSingleton implements IFirebaseClient {
|
|
|
177
54
|
this.state = new FirebaseClientState();
|
|
178
55
|
}
|
|
179
56
|
|
|
180
|
-
/**
|
|
181
|
-
* Get singleton instance
|
|
182
|
-
*/
|
|
183
57
|
static getInstance(): FirebaseClientSingleton {
|
|
184
58
|
if (!FirebaseClientSingleton.instance) {
|
|
185
59
|
FirebaseClientSingleton.instance = new FirebaseClientSingleton();
|
|
@@ -187,86 +61,39 @@ class FirebaseClientSingleton implements IFirebaseClient {
|
|
|
187
61
|
return FirebaseClientSingleton.instance;
|
|
188
62
|
}
|
|
189
63
|
|
|
190
|
-
/**
|
|
191
|
-
* Initialize Firebase client with configuration
|
|
192
|
-
* Configuration must be provided by the application (not from .env)
|
|
193
|
-
*
|
|
194
|
-
* @param config - Firebase configuration
|
|
195
|
-
* @returns Firebase app instance or null if initialization fails
|
|
196
|
-
*/
|
|
197
64
|
initialize(config: FirebaseConfig): FirebaseApp | null {
|
|
198
65
|
return FirebaseInitializationOrchestrator.initialize(config, this.state);
|
|
199
66
|
}
|
|
200
67
|
|
|
201
|
-
/**
|
|
202
|
-
* Get the Firebase app instance
|
|
203
|
-
* Auto-initializes from Constants/environment if not already initialized
|
|
204
|
-
* Returns null if config is not available (offline mode - no error)
|
|
205
|
-
* @returns Firebase app instance or null if not initialized
|
|
206
|
-
*/
|
|
207
68
|
getApp(): FirebaseApp | null {
|
|
208
69
|
return FirebaseInitializationOrchestrator.autoInitialize(this.state);
|
|
209
70
|
}
|
|
210
71
|
|
|
211
|
-
/**
|
|
212
|
-
* Check if client is initialized
|
|
213
|
-
*/
|
|
214
72
|
isInitialized(): boolean {
|
|
215
73
|
return this.state.isInitialized();
|
|
216
74
|
}
|
|
217
75
|
|
|
218
|
-
/**
|
|
219
|
-
* Get initialization error if any
|
|
220
|
-
*/
|
|
221
76
|
getInitializationError(): string | null {
|
|
222
77
|
return this.state.getInitializationError();
|
|
223
78
|
}
|
|
224
79
|
|
|
225
|
-
/**
|
|
226
|
-
* Reset the client instance
|
|
227
|
-
* Useful for testing
|
|
228
|
-
*/
|
|
229
80
|
reset(): void {
|
|
230
81
|
this.state.reset();
|
|
231
82
|
}
|
|
232
83
|
}
|
|
233
84
|
|
|
234
|
-
/**
|
|
235
|
-
* Singleton instance
|
|
236
|
-
*/
|
|
237
85
|
export const firebaseClient = FirebaseClientSingleton.getInstance();
|
|
238
86
|
|
|
239
87
|
/**
|
|
240
|
-
* Initialize Firebase client
|
|
241
|
-
* This is the main entry point for applications
|
|
242
|
-
*
|
|
243
|
-
* @param config - Firebase configuration (must be provided by app, not from .env)
|
|
244
|
-
* @returns Firebase app instance or null if initialization fails
|
|
245
|
-
*
|
|
246
|
-
* @example
|
|
247
|
-
* ```typescript
|
|
248
|
-
* import { initializeFirebase } from '@umituz/react-native-firebase';
|
|
249
|
-
*
|
|
250
|
-
* const config = {
|
|
251
|
-
* apiKey: 'your-api-key',
|
|
252
|
-
* authDomain: 'your-project.firebaseapp.com',
|
|
253
|
-
* projectId: 'your-project-id',
|
|
254
|
-
* };
|
|
255
|
-
*
|
|
256
|
-
* const app = initializeFirebase(config);
|
|
257
|
-
* ```
|
|
88
|
+
* Initialize Firebase client with configuration
|
|
258
89
|
*/
|
|
259
|
-
export function initializeFirebase(
|
|
260
|
-
config: FirebaseConfig
|
|
261
|
-
): FirebaseApp | null {
|
|
90
|
+
export function initializeFirebase(config: FirebaseConfig): FirebaseApp | null {
|
|
262
91
|
return firebaseClient.initialize(config);
|
|
263
92
|
}
|
|
264
93
|
|
|
265
94
|
/**
|
|
266
95
|
* Get Firebase app instance
|
|
267
96
|
* Auto-initializes from Constants/environment if not already initialized
|
|
268
|
-
* Returns null if config is not available (offline mode - no error)
|
|
269
|
-
* @returns Firebase app instance or null if not initialized
|
|
270
97
|
*/
|
|
271
98
|
export function getFirebaseApp(): FirebaseApp | null {
|
|
272
99
|
return firebaseClient.getApp();
|
|
@@ -274,9 +101,6 @@ export function getFirebaseApp(): FirebaseApp | null {
|
|
|
274
101
|
|
|
275
102
|
/**
|
|
276
103
|
* Auto-initialize Firebase from Constants/environment
|
|
277
|
-
* Called automatically when getFirebaseApp() is first accessed
|
|
278
|
-
* Can be called manually to initialize early
|
|
279
|
-
* @returns Firebase app instance or null if initialization fails
|
|
280
104
|
*/
|
|
281
105
|
export function autoInitializeFirebase(): FirebaseApp | null {
|
|
282
106
|
const config = loadFirebaseConfig();
|
|
@@ -286,97 +110,30 @@ export function autoInitializeFirebase(): FirebaseApp | null {
|
|
|
286
110
|
return null;
|
|
287
111
|
}
|
|
288
112
|
|
|
289
|
-
/**
|
|
290
|
-
* Service initialization result interface
|
|
291
|
-
*/
|
|
292
|
-
interface ServiceInitializationResult {
|
|
293
|
-
app: FirebaseApp | null;
|
|
294
|
-
auth: any | null;
|
|
295
|
-
analytics: any | null;
|
|
296
|
-
crashlytics: any | null;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* Service initializer class for better separation of concerns
|
|
301
|
-
*/
|
|
302
|
-
class ServiceInitializer {
|
|
303
|
-
/**
|
|
304
|
-
* Initialize Firebase Auth from external package
|
|
305
|
-
*/
|
|
306
|
-
private static initializeAuth(): any | null {
|
|
307
|
-
try {
|
|
308
|
-
const authModule = require('@umituz/react-native-firebase-auth');
|
|
309
|
-
const initializeFirebaseAuth = authModule.initializeFirebaseAuth;
|
|
310
|
-
|
|
311
|
-
if (typeof initializeFirebaseAuth !== 'function') {
|
|
312
|
-
if (__DEV__) {
|
|
313
|
-
console.warn('[Firebase] initializeFirebaseAuth is not a function');
|
|
314
|
-
}
|
|
315
|
-
return null;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
const auth = initializeFirebaseAuth();
|
|
319
|
-
return auth;
|
|
320
|
-
} catch (error) {
|
|
321
|
-
if (__DEV__) {
|
|
322
|
-
console.warn('[Firebase] Auth package not available:', error instanceof Error ? error.message : 'Unknown error');
|
|
323
|
-
}
|
|
324
|
-
return null;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
/**
|
|
329
|
-
* Initialize all optional Firebase services
|
|
330
|
-
*/
|
|
331
|
-
static async initializeServices(): Promise<{
|
|
332
|
-
auth: any | null;
|
|
333
|
-
analytics: any | null;
|
|
334
|
-
crashlytics: any | null;
|
|
335
|
-
}> {
|
|
336
|
-
if (__DEV__) {
|
|
337
|
-
console.log('[Firebase] Initializing optional services...');
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// Initialize auth from external package
|
|
341
|
-
const auth = this.initializeAuth();
|
|
342
|
-
|
|
343
|
-
// Analytics and Crashlytics are local modules - use imported services directly
|
|
344
|
-
const analytics = firebaseAnalyticsService;
|
|
345
|
-
const crashlytics = firebaseCrashlyticsService;
|
|
346
|
-
|
|
347
|
-
if (__DEV__) {
|
|
348
|
-
console.log('[Firebase] Services initialized - Auth:', !!auth, 'Analytics:', !!analytics, 'Crashlytics:', !!crashlytics);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
return { auth, analytics, crashlytics };
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
113
|
/**
|
|
356
114
|
* Initialize all Firebase services (App, Auth, Analytics, Crashlytics)
|
|
357
115
|
* This is the main entry point for applications - call this once at app startup
|
|
358
|
-
*
|
|
359
|
-
*
|
|
360
|
-
*
|
|
116
|
+
*
|
|
117
|
+
* IMPORTANT: Auth initialization is handled via callback to avoid require() issues.
|
|
118
|
+
* The main app should pass the authInitializer callback from react-native-firebase-auth.
|
|
119
|
+
*
|
|
120
|
+
* @param config - Optional Firebase configuration
|
|
121
|
+
* @param options - Optional service initialization options including authInitializer
|
|
361
122
|
* @returns Object with initialization results for each service
|
|
362
|
-
*
|
|
123
|
+
*
|
|
363
124
|
* @example
|
|
364
125
|
* ```typescript
|
|
365
126
|
* import { initializeAllFirebaseServices } from '@umituz/react-native-firebase';
|
|
366
|
-
*
|
|
367
|
-
*
|
|
368
|
-
* const result = await initializeAllFirebaseServices(
|
|
369
|
-
*
|
|
370
|
-
* // Or provide config explicitly
|
|
371
|
-
* const result = await initializeAllFirebaseServices({
|
|
372
|
-
* apiKey: 'your-api-key',
|
|
373
|
-
* projectId: 'your-project-id',
|
|
374
|
-
* // ...
|
|
127
|
+
* import { initializeFirebaseAuth } from '@umituz/react-native-firebase-auth';
|
|
128
|
+
*
|
|
129
|
+
* const result = await initializeAllFirebaseServices(undefined, {
|
|
130
|
+
* authInitializer: () => initializeFirebaseAuth(),
|
|
375
131
|
* });
|
|
376
132
|
* ```
|
|
377
133
|
*/
|
|
378
134
|
export async function initializeAllFirebaseServices(
|
|
379
|
-
config?: FirebaseConfig
|
|
135
|
+
config?: FirebaseConfig,
|
|
136
|
+
options?: ServiceInitializationOptions
|
|
380
137
|
): Promise<ServiceInitializationResult> {
|
|
381
138
|
const app = config ? initializeFirebase(config) : autoInitializeFirebase();
|
|
382
139
|
|
|
@@ -389,15 +146,24 @@ export async function initializeAllFirebaseServices(
|
|
|
389
146
|
};
|
|
390
147
|
}
|
|
391
148
|
|
|
392
|
-
const { auth, analytics, crashlytics } =
|
|
149
|
+
const { auth, analytics, crashlytics } =
|
|
150
|
+
await FirebaseServiceInitializer.initializeServices(options);
|
|
393
151
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
await analytics.init();
|
|
152
|
+
if (analytics && typeof (analytics as { init?: () => Promise<void> }).init === 'function') {
|
|
153
|
+
await (analytics as { init: () => Promise<void> }).init();
|
|
397
154
|
}
|
|
398
155
|
|
|
399
|
-
if (crashlytics && typeof crashlytics.init === 'function') {
|
|
400
|
-
await crashlytics.init();
|
|
156
|
+
if (crashlytics && typeof (crashlytics as { init?: () => Promise<void> }).init === 'function') {
|
|
157
|
+
await (crashlytics as { init: () => Promise<void> }).init();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (__DEV__) {
|
|
161
|
+
console.log('[Firebase] All services initialized:', {
|
|
162
|
+
app: !!app,
|
|
163
|
+
auth: !!auth,
|
|
164
|
+
analytics: !!analytics,
|
|
165
|
+
crashlytics: !!crashlytics,
|
|
166
|
+
});
|
|
401
167
|
}
|
|
402
168
|
|
|
403
169
|
return {
|
|
@@ -424,8 +190,7 @@ export function getFirebaseInitializationError(): string | null {
|
|
|
424
190
|
|
|
425
191
|
/**
|
|
426
192
|
* Reset Firebase client instance
|
|
427
|
-
* Useful for testing
|
|
428
193
|
*/
|
|
429
194
|
export function resetFirebaseClient(): void {
|
|
430
195
|
firebaseClient.reset();
|
|
431
|
-
}
|
|
196
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firebase Initialization Orchestrator
|
|
3
|
+
* Handles the initialization logic for Firebase App
|
|
4
|
+
*
|
|
5
|
+
* Single Responsibility: Only orchestrates Firebase App initialization
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { FirebaseConfig } from '../../../domain/value-objects/FirebaseConfig';
|
|
9
|
+
import type { FirebaseApp } from '../initializers/FirebaseAppInitializer';
|
|
10
|
+
import { FirebaseConfigValidator } from '../validators/FirebaseConfigValidator';
|
|
11
|
+
import { FirebaseAppInitializer } from '../initializers/FirebaseAppInitializer';
|
|
12
|
+
import { loadFirebaseConfig } from '../FirebaseConfigLoader';
|
|
13
|
+
import type { FirebaseClientState } from '../state/FirebaseClientState';
|
|
14
|
+
|
|
15
|
+
declare const __DEV__: boolean;
|
|
16
|
+
|
|
17
|
+
export class FirebaseInitializationOrchestrator {
|
|
18
|
+
static initialize(
|
|
19
|
+
config: FirebaseConfig,
|
|
20
|
+
state: FirebaseClientState
|
|
21
|
+
): FirebaseApp | null {
|
|
22
|
+
if (state.isInitialized()) {
|
|
23
|
+
if (__DEV__) {
|
|
24
|
+
console.log('[Firebase] Already initialized, returning existing instance');
|
|
25
|
+
}
|
|
26
|
+
return state.getApp();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (state.getInitializationError()) {
|
|
30
|
+
if (__DEV__) {
|
|
31
|
+
console.log('[Firebase] Previous initialization failed, skipping retry');
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
if (__DEV__) {
|
|
38
|
+
console.log('[Firebase] Initializing with projectId:', config.projectId);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
FirebaseConfigValidator.validate(config);
|
|
42
|
+
const app = FirebaseAppInitializer.initialize(config);
|
|
43
|
+
state.setApp(app);
|
|
44
|
+
|
|
45
|
+
if (__DEV__) {
|
|
46
|
+
console.log('[Firebase] Successfully initialized');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return app;
|
|
50
|
+
} catch (error) {
|
|
51
|
+
const errorMessage =
|
|
52
|
+
error instanceof Error
|
|
53
|
+
? error.message
|
|
54
|
+
: 'Failed to initialize Firebase client';
|
|
55
|
+
state.setInitializationError(errorMessage);
|
|
56
|
+
|
|
57
|
+
if (__DEV__) {
|
|
58
|
+
console.error('[Firebase] Initialization failed:', errorMessage);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static autoInitialize(state: FirebaseClientState): FirebaseApp | null {
|
|
66
|
+
if (state.isInitialized() || state.getInitializationError()) {
|
|
67
|
+
return state.getApp();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const autoConfig = loadFirebaseConfig();
|
|
71
|
+
if (autoConfig) {
|
|
72
|
+
if (__DEV__) {
|
|
73
|
+
console.log('[Firebase] Auto-initializing with environment config');
|
|
74
|
+
}
|
|
75
|
+
return this.initialize(autoConfig, state);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (__DEV__) {
|
|
79
|
+
console.log('[Firebase] No configuration found for auto-initialization');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firebase Service Initializer
|
|
3
|
+
* Handles initialization of Firebase services (Analytics, Crashlytics)
|
|
4
|
+
*
|
|
5
|
+
* NOTE: Auth initialization is handled by the main app via callback.
|
|
6
|
+
* This removes the need for dynamic require() which causes issues in production.
|
|
7
|
+
*
|
|
8
|
+
* Single Responsibility: Only initializes Firebase services
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { firebaseAnalyticsService } from '../../../analytics';
|
|
12
|
+
import { firebaseCrashlyticsService } from '../../../crashlytics';
|
|
13
|
+
|
|
14
|
+
declare const __DEV__: boolean;
|
|
15
|
+
|
|
16
|
+
export type AuthInitializer = () => unknown;
|
|
17
|
+
|
|
18
|
+
export interface ServiceInitializationOptions {
|
|
19
|
+
authInitializer?: AuthInitializer;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ServiceInitializationResult {
|
|
23
|
+
auth: unknown;
|
|
24
|
+
analytics: typeof firebaseAnalyticsService;
|
|
25
|
+
crashlytics: typeof firebaseCrashlyticsService;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class FirebaseServiceInitializer {
|
|
29
|
+
static async initializeServices(
|
|
30
|
+
options?: ServiceInitializationOptions
|
|
31
|
+
): Promise<ServiceInitializationResult> {
|
|
32
|
+
if (__DEV__) {
|
|
33
|
+
console.log('[Firebase] Initializing services...');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let auth: unknown = null;
|
|
37
|
+
if (options?.authInitializer) {
|
|
38
|
+
try {
|
|
39
|
+
auth = options.authInitializer();
|
|
40
|
+
if (__DEV__) {
|
|
41
|
+
console.log('[Firebase] Auth initialized via callback');
|
|
42
|
+
}
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (__DEV__) {
|
|
45
|
+
console.warn(
|
|
46
|
+
'[Firebase] Auth initialization failed:',
|
|
47
|
+
error instanceof Error ? error.message : 'Unknown error'
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const analytics = firebaseAnalyticsService;
|
|
54
|
+
const crashlytics = firebaseCrashlyticsService;
|
|
55
|
+
|
|
56
|
+
if (__DEV__) {
|
|
57
|
+
console.log(
|
|
58
|
+
'[Firebase] Services initialized - Auth:',
|
|
59
|
+
!!auth,
|
|
60
|
+
'Analytics:',
|
|
61
|
+
!!analytics,
|
|
62
|
+
'Crashlytics:',
|
|
63
|
+
!!crashlytics
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return { auth, analytics, crashlytics };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firebase Client State Manager
|
|
3
|
+
* Manages the state of Firebase initialization
|
|
4
|
+
*
|
|
5
|
+
* Single Responsibility: Only manages initialization state
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { FirebaseApp } from '../initializers/FirebaseAppInitializer';
|
|
9
|
+
|
|
10
|
+
export class FirebaseClientState {
|
|
11
|
+
private app: FirebaseApp | null = null;
|
|
12
|
+
private initializationError: string | null = null;
|
|
13
|
+
|
|
14
|
+
getApp(): FirebaseApp | null {
|
|
15
|
+
return this.app;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
setApp(app: FirebaseApp | null): void {
|
|
19
|
+
this.app = app;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
isInitialized(): boolean {
|
|
23
|
+
return this.app !== null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
getInitializationError(): string | null {
|
|
27
|
+
return this.initializationError;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
setInitializationError(error: string | null): void {
|
|
31
|
+
this.initializationError = error;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
reset(): void {
|
|
35
|
+
this.app = null;
|
|
36
|
+
this.initializationError = null;
|
|
37
|
+
}
|
|
38
|
+
}
|