No windows have a root view controller, cannot save application state - ios

When ever I try to open the application by clicking the notification this No windows have a root view controller, cannot save application state error is showing and it's not navigating to the correct page. the app just opens that all.
How should I solve this ?
AppleDelegate:
import UIKit
import Flutter
import Firebase
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate
}
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
override func application(_ application: UIApplication, shouldSaveSecureApplicationState coder: NSCoder) -> Bool {
return true
}
override func application(_ application: UIApplication, shouldRestoreSecureApplicationState coder: NSCoder) -> Bool {
return true
}
override func applicationDidBecomeActive(_ application: UIApplication) {
signal(SIGPIPE, SIG_IGN);
}
override func applicationWillEnterForeground(_ application: UIApplication) {
signal(SIGPIPE, SIG_IGN);
}
override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken
return super.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
}
}

Related

Flutter/Ios - Firebase Phone auth using with FCM App crashes on real device only for first time

Everything works fine after the second time. this issue only occurs once per device, making it hard to debug.
Even if I restarted the device issue doesn't appear again but as it always happens for new users the first time.
AppDelegate.Swift
import UIKit
import Flutter
import flutter_downloader
import Firebase
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
FlutterDownloaderPlugin.setPluginRegistrantCallback(registerPlugins)
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
}
if(!UserDefaults.standard.bool(forKey: "Notification")) {
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
UserDefaults.standard.set(true, forKey: "Notification")
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
override func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
super.application(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler)
let firebaseAuth = Auth.auth()
Messaging.messaging().appDidReceiveMessage(userInfo)
print(userInfo)
if (firebaseAuth.canHandleNotification(userInfo)){
completionHandler(.noData)
return
}
}
}
private func registerPlugins(registry: FlutterPluginRegistry) {
if (!registry.hasPlugin("FlutterDownloaderPlugin")) {
FlutterDownloaderPlugin.register(with: registry.registrar(forPlugin: "FlutterDownloaderPlugin")!)
}
}
Crash Log:
specialized AppDelegate.application(_:didReceiveRemoteNotification:fetchCompletionHandler:) + 600 (AppDelegate.swift:29)

Is there any different to install UNUserNotificationCenterDelegate in willFinishLaunchingWithOptions or didFinishLaunchingWithOptions?

Currently, this is my code-snippet, in handling tap action, of local notification.
The following code will launch a UIViewController, when user taps on the local notification.
import UIKit
extension UIApplication {
//
// https://stackoverflow.com/a/58031897/72437
//
var keyWindow: UIWindow? {
// Get connected scenes
UIApplication
.shared
.connectedScenes
.compactMap { $0 as? UIWindowScene }
.flatMap { $0.windows }
.first { $0.isKeyWindow }
}
}
#main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool {
UNUserNotificationCenter.current().delegate = self
return true
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
}
}
extension AppDelegate: UNUserNotificationCenterDelegate {
// This method is called when user clicked on the notification
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void)
{
let newViewController = NewViewController.instanceFromNib()
newViewController.postInit(response.notification.request.identifier)
UIApplication.shared.keyWindow?.rootViewController?.present(newViewController, animated: false, completion: nil)
completionHandler()
}
}
What I am puzzling is, should I install UNUserNotificationCenterDelegate during willFinishLaunchingWithOptions, or didFinishLaunchingWithOptions?
Install during willFinishLaunchingWithOptions
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool {
UNUserNotificationCenter.current().delegate = self
return true
}
Install during didFinishLaunchingWithOptions
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
UNUserNotificationCenter.current().delegate = self
return true
}
Based on https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate
You must assign your delegate object to the UNUserNotificationCenter
object before your app finishes launching. For example, in an iOS app,
you must assign it in the
application(:willFinishLaunchingWithOptions:) or
application(:didFinishLaunchingWithOptions:) method of your app
delegate. Assigning a delegate after the system calls these methods
might cause you to miss incoming notifications.
It seems like there is no difference between willFinishLaunchingWithOptions and didFinishLaunchingWithOptions
I was wondering, do you find any differences, between willFinishLaunchingWithOptions and didFinishLaunchingWithOptions, as far as UNUserNotificationCenterDelegate installation is concerned?
Thanks.

How can plugins be used in another plugin's callback in the background?

I'm using this background_locator plugin and a geofencing plugin in my Flutter app. In the location callback function of the background_locator plugin I create geofences using the geofencing plugin. This works fine on Android. On iOS the background_locator plugin functions as it should, but calling geofencing methods in the callback has no effect. Calling the geofencing methods outside of the callback works without problems.
I found some resources addressing this: https://github.com/flutter/flutter/issues/21925 and https://github.com/flutter/engine/pull/7843 and changed the code in my AppDelegate.swift accordingly, but this issue still occurs. Any advice is appreciated!
Below is my AppDelegate.swift
import UIKit
import Flutter
import GoogleMaps
func registerPlugins(registry: FlutterPluginRegistry) -> () {
if (!registry.hasPlugin("BackgroundLocatorPlugin")) {
BackgroundLocatorPlugin.register(with: registry.registrar(forPlugin: "BackgroundLocatorPlugin"))
}
if (!registry.hasPlugin("GeofencingPlugin")) {
GeofencingPlugin.register(with: registry.registrar(forPlugin: "GeofencingPlugin"))
}
if (!registry.hasPlugin("FlutterLocalNotificationsPlugin")) {
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
}
if (!registry.hasPlugin("FLTSharedPreferencesPlugin")) {
FLTSharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "FLTSharedPreferencesPlugin"))
}
if (!registry.hasPlugin("FlutterSecureStoragePlugin")) {
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
}
}
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GMSServices.provideAPIKey("$GOOGLE_MAPS_API_KEY")
GeneratedPluginRegistrant.register(with: self)
GeofencingPlugin.setPluginRegistrantCallback(registerPlugins)
BackgroundLocatorPlugin.setPluginRegistrantCallback(registerPlugins)
// iOS notification setup
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
}
// Prevent "old" notifications showing up after reinstallation
if(!UserDefaults.standard.bool(forKey: "Notification")) {
UIApplication.shared.cancelAllLocalNotifications()
UserDefaults.standard.set(true, forKey: "Notification")
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
I have face the issue with "FlutterGeofencing" that app will caught an exception "failed to set registerPlugins" and resolve the issue by set the callback
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
GeofencingPlugin.setPluginRegistrantCallback({_ in
})
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}

Confusion connecting iOS portion of Flutter App to Firebase

I'm attempting to connect the iOS portion of my Flutter app to Firebase. And as I go through the steps on Firebase - "Add Firebase to your iOS app" - I hit a step that says "Add the initialization code below to your main AppDelegate class" (Swift version):
import UIKit
import Firebase
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?)
-> Bool {
FirebaseApp.configure()
return true
}
}
But my AppDelegate class already has this code:
import UIKit
import Flutter
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
Not sure what to do. Do I replace the existing code with the code provided by Firebase or do I reconcile the two somehow?
In the given (predefined) AppDelegate class, there are 2 things you need to do additionally.
They are
import Firebase // <-- 1st add
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(_ application: UIApplication,didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
FirebaseApp.configure() // <-- 2nd add
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
Merge both code together:
import UIKit
import Flutter
import Firebase
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
FirebaseApp.configure()
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}

iOS push notification via Firebase not working

Hello I'm making an iPhone app where I send push notifications via Google's Firebase. I'm using Swift and Xcode for the programming. When I open the app I get asked to allow push notifications, however I don't receive any when I send them from the Firebase Console. I was wondering if you could help me out. I'm using Ad Hoc export to transfer an .isa to my friend's iPhone and test it that way. I followed exactly the Firebase tutorial - adding the .plist, the cocoa pod and I uploaded a certificate in the project settings. I have a paid apple developer account.
import UIKit
import CoreData
import Firebase
import FirebaseMessaging
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
FIRApp.configure()
let notificationTypes: UIUserNotificationType = [UIUserNotificationType.Alert,UIUserNotificationType.Badge,UIUserNotificationType.Sound]
let notificationSettings = UIUserNotificationSettings(forTypes:notificationTypes, categories:nil)
application.registerForRemoteNotifications()
application.registerUserNotificationSettings(notificationSettings)
return true
}
func applicationWillResignActive(application: UIApplication) {
}
func applicationDidEnterBackground(application: UIApplication) {
}
func applicationWillEnterForeground(application: UIApplication) {
}
func applicationDidBecomeActive(application: UIApplication) {
}
func applicationWillTerminate(application: UIApplication) {
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject],
fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
print("MessageID : \(userInfo["gcm_message_id"]!)")
print("%#", userInfo)
}
}
There are a few more things you need to do. When you registerForRemoteNotifications, you receive an APNS Token which you need to give to Firebase. This is done in application didRegisterForRemoteNotificationsWithDeviceToken.
import UIKit
import CoreData
import Firebase
import FirebaseMessaging
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
override init() {
super.init()
FIRApp.configure()
}
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
application.registerForRemoteNotifications()
application.registerUserNotificationSettings(
UIUserNotificationSettings(
forTypes: [.Alert, .Badge, .Sound],
categories: nil
)
)
NSNotificationCenter.defaultCenter().addObserver(self,
selector: #selector(tokenRefreshNotification),
name: kFIRInstanceIDTokenRefreshNotification,
object: nil
)
return true
}
func applicationDidEnterBackground(application: UIApplication) {
FIRMessaging.messaging().disconnect()
}
func applicationDidBecomeActive(application: UIApplication) {
FIRMessaging.messaging().connectWithCompletion { (error) in
switch error {
case .Some:
print("Unable to connect with FCM. \(error)")
case .None:
print("Connected to FCM.")
}
}
}
func applicationWillResignActive(application: UIApplication) {}
func applicationWillEnterForeground(application: UIApplication) {}
func applicationWillTerminate(application: UIApplication) {}
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: .Sandbox)
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject],
fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
print("MessageID : \(userInfo["gcm.message_id"]!)")
print("%#", userInfo)
}
func tokenRefreshNotification(notification: NSNotification) {
if let refreshedToken = FIRInstanceID.instanceID().token() {
print("Instance ID token: \(refreshedToken)")
}
applicationDidBecomeActive(UIApplication.sharedApplication())
}
}

Resources