diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 07e980c..dd59616 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -12,6 +12,7 @@ android { ndkVersion = flutter.ndkVersion compileOptions { + isCoreLibraryDesugaringEnabled = true sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } @@ -55,6 +56,10 @@ android { } } +dependencies { + coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4") +} + flutter { source = "../.." } diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 6266644..7f9fd84 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -1,5 +1,6 @@ import Flutter import UIKit +import FirebaseMessaging @main @objc class AppDelegate: FlutterAppDelegate { @@ -10,4 +11,13 @@ import UIKit GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } + + // Forward APNs token to Firebase for FCM + override func application( + _ application: UIApplication, + didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data + ) { + Messaging.messaging().apnsToken = deviceToken + super.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken) + } } diff --git a/lib/config/firebase_config.dart b/lib/config/firebase_config.dart index 7f787a1..842a33c 100644 --- a/lib/config/firebase_config.dart +++ b/lib/config/firebase_config.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:firebase_core/firebase_core.dart'; import 'package:real_estate_mobile/config/app_config.dart'; @@ -5,44 +7,68 @@ class FirebaseConfig { static FirebaseOptions get currentOptions { switch (AppConfig.flavor) { case Flavor.dev: - return _dev; + return Platform.isIOS ? _devIos : _devAndroid; case Flavor.demo: - return _demo; + return Platform.isIOS ? _demoIos : _demoAndroid; case Flavor.local: - return _local; + return Platform.isIOS ? _localIos : _localAndroid; } } - // ── Dev ── - static const _dev = FirebaseOptions( - apiKey: 'AIzaSyAwb_9fc7n9gbN_IQqWeYRkHSSykzVHCyc', // Android + // ── Dev (Android) ── + static const _devAndroid = FirebaseOptions( + apiKey: 'AIzaSyAwb_9fc7n9gbN_IQqWeYRkHSSykzVHCyc', appId: '1:703616926518:android:4342b796fbabb690fca997', messagingSenderId: '703616926518', projectId: 'real-estate-2d71e', storageBucket: 'real-estate-2d71e.firebasestorage.app', - iosBundleId: 'com.requestn.realEstateMobile.dev', - iosClientId: null, ); - // ── Demo ── - static const _demo = FirebaseOptions( + // ── Dev (iOS) ── + static const _devIos = FirebaseOptions( + apiKey: 'AIzaSyD67KPio-70XW_yGjrRUyq9ejdZoZA1rmE', + appId: '1:703616926518:ios:301cac94994fbdc3fca997', + messagingSenderId: '703616926518', + projectId: 'real-estate-2d71e', + storageBucket: 'real-estate-2d71e.firebasestorage.app', + iosBundleId: 'com.requestn.realEstateMobile.dev', + ); + + // ── Demo (Android) ── + static const _demoAndroid = FirebaseOptions( apiKey: 'AIzaSyAwb_9fc7n9gbN_IQqWeYRkHSSykzVHCyc', appId: '1:703616926518:android:270639d6887d2cb4fca997', messagingSenderId: '703616926518', projectId: 'real-estate-2d71e', storageBucket: 'real-estate-2d71e.firebasestorage.app', - iosBundleId: 'com.requestn.realEstateMobile.demo', - iosClientId: null, ); - // ── Local ── - static const _local = FirebaseOptions( + // ── Demo (iOS) ── + static const _demoIos = FirebaseOptions( + apiKey: 'AIzaSyD67KPio-70XW_yGjrRUyq9ejdZoZA1rmE', + appId: '1:703616926518:ios:2d2f7d163774722dfca997', + messagingSenderId: '703616926518', + projectId: 'real-estate-2d71e', + storageBucket: 'real-estate-2d71e.firebasestorage.app', + iosBundleId: 'com.requestn.realEstateMobile.demo', + ); + + // ── Local (Android) ── + static const _localAndroid = FirebaseOptions( apiKey: 'AIzaSyAwb_9fc7n9gbN_IQqWeYRkHSSykzVHCyc', appId: '1:703616926518:android:2eb242f07b174c25fca997', messagingSenderId: '703616926518', projectId: 'real-estate-2d71e', storageBucket: 'real-estate-2d71e.firebasestorage.app', + ); + + // ── Local (iOS) ── + static const _localIos = FirebaseOptions( + apiKey: 'AIzaSyD67KPio-70XW_yGjrRUyq9ejdZoZA1rmE', + appId: '1:703616926518:ios:15652d299f9f2d7ffca997', + messagingSenderId: '703616926518', + projectId: 'real-estate-2d71e', + storageBucket: 'real-estate-2d71e.firebasestorage.app', iosBundleId: 'com.requestn.realEstateMobile.local', - iosClientId: null, ); } diff --git a/lib/core/services/push_notification_service.dart b/lib/core/services/push_notification_service.dart index be2280a..1e89e77 100644 --- a/lib/core/services/push_notification_service.dart +++ b/lib/core/services/push_notification_service.dart @@ -30,16 +30,8 @@ class PushNotificationService { void Function(String? actionUrl)? onNotificationTap; /// Initialize Firebase Messaging and local notifications. - /// Skipped on iOS until Firebase iOS app is configured. Future initialize() async { if (_initialized) return; - - // Skip on iOS — no Firebase iOS app configured yet - if (Platform.isIOS) { - debugPrint('[FCM] Skipped on iOS — no Firebase iOS app configured'); - return; - } - _initialized = true; _messaging = FirebaseMessaging.instance; @@ -188,7 +180,7 @@ class PushNotificationService { /// Unregister FCM token from backend (call on logout). Future unregister() async { - if (Platform.isIOS || _currentToken == null) return; + if (_currentToken == null) return; try { await _repo.removeFcmToken(_currentToken!); debugPrint('[FCM] Token unregistered from backend'); @@ -200,7 +192,6 @@ class PushNotificationService { /// Re-register token after login. Future registerAfterLogin() async { - if (Platform.isIOS) return; await _getAndRegisterToken(); } } diff --git a/lib/main.dart b/lib/main.dart index 11305f5..3e6dc0a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,12 +10,7 @@ Future mainCommon(Flavor flavor) async { WidgetsFlutterBinding.ensureInitialized(); await AppConfig.init(flavor); - // Initialize Firebase — iOS may fail if no iOS app configured in Firebase Console - try { - await Firebase.initializeApp(options: FirebaseConfig.currentOptions); - } catch (e) { - debugPrint('[Firebase] Init failed (expected on iOS without config): $e'); // ignore: avoid_print - } + await Firebase.initializeApp(options: FirebaseConfig.currentOptions); runApp(const ProviderScope(child: MyApp())); }