how to call func application from didFinishLaunchingWithOptions [duplicate] - ios

This question already has answers here:
loading url from scheme not processing first time - appdelegate vs viewcontroller
(2 answers)
Closed 3 years ago.
I would like to know how to call a function from another function within AppDelegate. It would be better to call this function from ViewController but could not get it to work.
I have in my AppDelegate.m the following code:
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
let geturl = url.host?.removingPercentEncoding;
UserDefaults.standard.set(geturl, forKey: "DeepLinkUrl")
return true
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
//I WANT CALL the upper function to set the URL IN HERE
return true
}
Since I don't know how to call the open url function from the ViewController.m I did this calling didFinishLaunchingWithOptions func from AppDelegate.m
My ViewController.m looks like:
#objc func appWillEnterForeground() {
print("app on foreground")
let appDelegate: AppDelegate? = UIApplication.shared.delegate as? AppDelegate
appDelegate?.application(UIApplication.shared, didFinishLaunchingWithOptions: nil)
//ACTUALLY I WANT TO CALL THE SET URL FUNCTION IN STEAD OF didFinishLaunchingWithOption BUT DON'T KNOW HOW. SO I FOUND THIS WHICH IS BEING CALLED
let user = UserDefaults.standard
if user.url(forKey: "DeepLinkUrl") != nil{
let str = user.value(forKey: "DeepLinkUrl") as! String
print(str)
}
}
Any ideas?

You don't call this method at all. It is called by the operating system when your application is launched. You are absolutely not supposed to ever call it yourself.
Same with the other method, which will be called by the operating system when your application is asked to open a URL. Which might be the URL of a file, or a URL with a scheme that you registered for.
Set a breakpoint on appDidFinishLaunching, then on your viewDidLoad method, start debugging to get some idea what is going on. You might also consider reading Apple's documentation.

Related

How to wait for didFinishLaunchingWithOptions to finish before handling universal link?

In my Swift iOS app I asynchronously set up the initial view controller in the AppDelegate inside didFinishLaunchingWithOptions. Now I'm adding a Universal Link such that if you go to the corresponding URL on your phone it will automatically open the right page in the app (in this case it's a password reset URL that, if correct, will open the password reset form inside the app).
I am handling the linking inside application(_:continue:restorationHandler:) (continueUserActivity) in my AppDelegate.
It works if the app is already running but otherwise the link handling happens before my app has finished setting up the initial view controller and the view controller for the deep link gets dismissed when didFinishLaunchingWithOptions returns.
How can I ensure the app is finished setting up before I present view controllers from continueUserActivity? I can't/shouldn't prevent didFinishLaunching from returning until the view controllers are set up, so the method returns before the initial view controller is presented.
I know I can check the user activity dictionary inside didFinishLaunchingWithOptions but continueUserActivity is still called (it would be much simpler if it was only called if the app was running, like how didReceiveRemoteNotification works). Is there a condition to check inside this method to defer the link handling to didFinishLaunching?
Some simplified UIApplicationDelegate code to help explain:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
doSomeAsyncSetup(
completion: {
presentSomeViewController()
}
)
return true
}
func application(_: UIApplication, continue userActivity: NSUserActivity, restorationHandler _: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL,
let components = NSURLComponents(url: url, resolvingAgainstBaseURL: true),
let params = components.queryItems,
components.path == pathThatIExpect
{
handleUniversalLink(params)
}
return true
}
Note there seems to be a slightly different distinction when the app has opted into Scenes but my app is not using that (https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app).
A DispatchGroup should be able to handle this for you.
private let dispatchGroup = DispatchGroup()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.dispatchGroup.enter()
doSomeAsyncSetup(
completion: {
presentSomeViewController()
self.dispatchGroup.leave()
}
)
return true
}
func application(_: UIApplication, continue userActivity: NSUserActivity, restorationHandler _: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL,
let components = NSURLComponents(url: url, resolvingAgainstBaseURL: true),
let params = components.queryItems,
components.path == pathThatIExpect
{
self.dispatchGroup.notify {
handleUniversalLink(params)
}
}
return true
}
In the case where your app is being launched, the dispatch group is entered in didFinishLaunching and it is left once the view controller is presented.
Once the dispatch group is empty, the notify will execute the user activity code; so only after your view controller is presented.
In the case where your app is already launched, the dispatch group will be empty since didFinishLaunching isn't called and so the notify will execute immediately
You can use serial queue for that. For example:
serialQueue.async {
self.presentSomeViewController()
}
And
serialQueue.async {
self.handleUniversalLink(params)
}

Unable to access viewcontroller from didFinishLaunchingWithOptions when app launches

I am having an issue where I am unable to access a function in my viewcontroller.swft file when lunching from a killed app in didFinishLaunchingWithOptions.
I am trying to access the function with viewController?.loadRequestnotificationwaiting(for: url as! String) from AppDelegate which I pass data to the viewcontroller where I can do some stuff. But when I place an alert in the function in the viewcontroller loadRequestnotificationwaiting. The data is not being passed.
Now I use this same method in other areas to pass data to the viewcontroller from the appdelegate and they work fine. It seems to not work when using it in didFinishLaunchingWithOptions
Is the viewcontroller not available yet when trying to access it from didFinishLaunchingWithOptions?
AppDelegate.swift
class AppDelegate : UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
weak var viewController : ViewController?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
UNUserNotificationCenter.current().delegate = self
ConnectionManager.sharedInstance.observeReachability()
// Override point for customization after application launch.
FirebaseApp.configure()
registerForPushNotifications()
// opened from a push notification when the app is closed
if let userInfo = launchOptions?[.remoteNotification] as? [String : AnyObject] {
if let object = userInfo["aps"] {
let url = object["url"]
viewController?.loadRequestnotificationwaiting(for: url as! String)
}
}
return true
}
}
ViewController.swift
class ViewController: UIViewController, WKNavigationDelegate {
func loadRequestnotificationwaiting(for notification_url_wait : String) {
notification_url_final_wait = notification_url_wait
let url = URL(string: notification_url_final_wait!)
let URLrequest = URLRequest(url: url!)
self.webView.load(URLrequest)
}
}
You are relying on your AppDelegate's viewController property, but you are setting this property in your viewDidLoad method of your view controller; This is fine when a notification is received when your app is already running, since the viewController property is already set.
When a notification causes your application to be launched, the viewController property is not set and the function isn't called.
Assuming that the view controller you need is the initial view controller from your storyboard, you can get the root view controller from your app delegate's window property;
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
UNUserNotificationCenter.current().delegate = self
ConnectionManager.sharedInstance.observeReachability()
// Override point for customization after application launch.
FirebaseApp.configure()
registerForPushNotifications()
// opened from a push notification when the app is closed
if let userInfo = launchOptions?[.remoteNotification] as? [String : AnyObject] {
if let object = userInfo["aps"],
let url = object["url"] as? String,
let viewController = self.window?.rootViewController as? ViewController {
viewController.loadRequestnotificationwaiting(for: url)
}
}
return true
}
if your view controller is root, you can summon it by call UIApplication.shared.keyWindow!.rootViewController! or overwrite it with your viewController
you can raise a notification using -[NotificationCenter postNotificationName:object:userInfo:] and observe this notification name in View Controller

Realm in appdelegate.swift not working on other files

This is a simple one probably but am I doing something wrong here :
I have this in my appdelegate.swift :
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
//configure Firebase
FirebaseApp.configure()
let realm = try! Realm()
return true
}
then I want to use refer to it in my view controller files like this :
do {
try realm.write {
realm.add(workoutData)
}
} catch {
print("Sorry no good")
}
but it says "use of unresolved identifier 'realm'".
But I thought the point of putting in app delegate was so you didn't need to do the 'let realm = try! Realm()' on every single view controller?
I have a firebase instance which is configured in appdelegate by doing :
FirebaseApp.configure()
and that works fine.
Am I missing something here? I can't find another answer that covers this so basically (there are some relating to migrations etc) so I'm assuming this is something really simple!
David Pastor answered this for me..
For any noobs that come this way....
You shouldn't declare it as a global variable anyway because that leads to issues as your app scales and if you did then the way i've done it wouldn't be right anyway!

How to show a specific view controller even if user does not tap on the notification

I have 2 ViewControllers which will pop up depending on the local notification. How can I show these ViewControllers when user taps on the app icon directly instead of the notification ?
Is there any way to call
- (void)application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification
from
- (void)applicationDidBecomeActive:(UIApplication *)application
?
You can just call that method directly by referencing your app delegate and giving it those parameters (you will have to create a dummy UILocalNotification).
However, this is weird.
What you should do is properly SEPARATE the code that shows the view controllers into its own function. Then you can call this function in either of the methods you specified above.
Wherever you schedule the local notification, add a key to NSUserDefaults specifying which VC to load.
UserDefaults.standard.setValue("name_of_vc_to_load", forKey: "vcToLoad")
UserDefaults.standard.synchronize()
Finally in
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
and in
func applicationDidBecomeActive(_ application: UIApplication) {
Check if the VC to load available and load the VC accordingly.
if let vcName : String = UserDefaults.standard.value(forKey: "vcToLoad") as? String {
switch vcName {
case "VCA" :
//load VCA
break
default:
//load VCB
break
}
}
You can either present a VC of your choice over the rootVC or you can replace the rootVC itself.

Trying to execute code from a different page. Not working

I'm calling this function in a swift file but it doesn't seem to work. For example, the timer isn't stopping when it's supposed to. Any ideas on how to fix it from the code shown below? Thanks.
var vc = ViewController()
func application(application: UIApplication, didFinishLaunchingWithOptions: [NSObject: AnyObject]?) -> Bool
{
vc.endGame()
vc.startButton!.hidden = false
vc.timer!.invalidate()
vc.timer = nil
// This is calling the function from the view controller
return true
}

Resources