Issue with presenting viewcontroller in iOS 13, Xcode 11 - swift

I'm trying to present a viewcontroller on topMostViewController. It's working in iOS 12 and lower. But on iOS 13 I'm getting this error:
Manually adding the rootViewController's view to the view hierarchy is no longer supported. Please allow UIWindow to add the rootViewController's view to the view hierarchy itself.
I have checked on iOS 12 and lower, and the code below works fine. But on iOS 13 I am having trouble presenting the view controller. I printed on viewDidLoad; it's getting printed but the view doesn't appear.
func presentInWindow(animated flag: Bool = true, completion: (() -> Void)? = nil) {
DispatchQueue.main.async {
var alertWindow: UIWindow?
alertWindow = UIWindow(frame: UIScreen.main.bounds)
alertWindow?.windowLevel = UIWindow.Level.alert + 1
alertWindow?.rootViewController = UIApplication.topViewController()
if let rootViewController = alertWindow?.rootViewController {
alertWindow?.makeKeyAndVisible()
rootViewController.present(self, animated: flag, completion: completion)
}
}
}
static func topViewController() -> UIViewController? {
var topViewController: UIViewController?
if #available(iOS 13.0, *) {
topViewController = shared.connectedScenes
.filter({$0.activationState == .foregroundActive})
.map({$0 as? UIWindowScene})
.compactMap({$0})
.first?.windows
.filter({$0.isKeyWindow}).first?.rootViewController
} else {
topViewController = shared.delegate?.window??.rootViewController
}
while true {
if let presented = topViewController?.presentedViewController {
topViewController = presented
} else if let nav = topViewController as? UINavigationController {
topViewController = nav.visibleViewController
} else {
break
}
}
return topViewController
}

This code will work to create a view controller on top. You can adjust the size of the view controller on this line: popOverVC.view.frame = lSs I'm not sure if this code is exactly what you are asking for, but if you need a quick solution, it will present view controllers in swift 5, iOS 13, and xcode 11. Note that it is a child view controller, so if you remove the parent, it will leave too. Simply change self to ViewController that you want to present on.
let popOverVC = UIStoryboard(name: "yourSB", bundle: nil).instantiateViewController(withIdentifier: "vcYouWantID") as! vcYouWant
self.addChild(popOverVC)
let lSs = UIScreen.main.bounds
popOverVC.view.frame = lSs
popOverVC.view.tag = tag
self.view.addSubview(popOverVC.view)
popOverVC.didMove(toParent: self)

I received the same error while updating an app which worked fine previously. It looks like the new requirement for AppDelegate.swift is:
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
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>) { }
}
hth

I encountered the same issue tonight. I tried the methods suggested by people on the Net including Stackoverflow, but none had worked.
Just now, as if magic was working, I tried to form an outlet from the ViewController (visually the "background" layer for all subviews) in the Main.storyboard to the ViewConstroller class in ViewController.swift. It worked in my project for iOS 13 simulator in Xcode 12.2.
Hope this trick will also work for you.

I found this to fix my problem (in old Objective-C code):
// old way broken in iOS 13
//appDelegate.window.rootViewController = vc;
// this works in iOS 13
[appDelegate.window setRootViewController:vc];

Related

Detecting Source Application for Launch Depracated? Aternatives?

I am trying to detect if another application has launched my app in order to launch a different view to accept a pdf being passed to it. It appears that LaunchOptionsKey can be used in the AppDelegate, but the following code dosn't seem to work. Can anyone tell me if I'm using the code wrong or if there are alternatives. Most references use the sourceApplication to do this, but I read claims that is was depracated in 2019, yet apple still has it in their documentation? Also I notice that switching launch views only works in the SceneDelegate instead of the AppDelegate. Apparently this is also a recent change?
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
print("finished launching")
if launchOptions?[UIApplication.LaunchOptionsKey.sourceApplication] != nil {
print("Launched by another app")
startVC = "addFile"
} else {
print("source app not identified")
}
return true
}
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
print("Scene: finished launching")
//self.window = UIWindow(frame: UIScreen.main.bounds) // black sreen
let storyBoard = UIStoryboard.init(name: "Main", bundle: nil)
if startVC == "addFile" {
let initialVc = storyBoard.instantiateViewController(withIdentifier: "addFile") as! addFileViewController
self.window?.rootViewController = initialVc
self.window?.makeKeyAndVisible()
}
if startVC == "home" {
let initialVc = storyBoard.instantiateViewController(withIdentifier: "home") as! FirstController
self.window?.rootViewController = initialVc
self.window?.makeKeyAndVisible()
}
guard let _ = (scene as? UIWindowScene) else { return }
}

Access ViewController in didFinishLaunchingWithOptions

I want to access the initial view controller through
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
I happen to be using a navigation controller - but that doesn't matter really.
I've tried the following:
UIApplication.shared.keyWindow!.rootViewController
UIApplication.shared.delegate?.window
application.windows[0].rootViewController
self.window?.rootViewController
application.keyWindow
and all are nil.
I want to use the storyboard and inject my dependency here. I don't want to instantiate a view controller here, I still want to use the initial view from the storyboard.
How can I access the view in didFinishLaunchingWithOptions - I want this for iOS12?
If I understand your question you want to access the TopMost ViewController presented, if so you can try to use this function in your AppDelegate
func topMostController() -> UIViewController? {
if var topController = UIApplication.shared.keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
return topController
}
return nil
}
To use this:
if let vc = self.topMostController() {
// Your code to use vc instance of topMostController
}

Black when screen when settings initial controller programmatically

Could you please look at repo https://github.com/Rukomoynikov/InitialViewControllerProgrammatically and help me. Why I got an black screen when trying instantiateViewController.
This is my AppDelegate:
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow.init(frame: UIScreen.main.bounds)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "ViewController") as! ViewController
guard window != nil else { return true }
self.window!.backgroundColor = .darkGray
self.window!.rootViewController = viewController
self.window!.makeKeyAndVisible()
return true
}
}
Couple details.
App created in last Xcode version.
iOs deployment target changed from 13 to 12.
SceneDelegate removed.
In target settings option Main Interface cleared.
In info.plist StoryBoardName and DelegateClassName also removed.
The problem is that, in an attempt to make the project stop using the scene delegate and use the app delegate instead, you mangled the UIApplicationSceneManifest entry in the Info.plist. Instead, you would need to delete that entry entirely. Its mere presence is what causes the scene delegate architecture to be used.
It would be better, however, to make this work for both iOS 12 using an app delegate and iOS 13 using a scene delegate (as I have described at https://stackoverflow.com/a/58405507/341994).
iOS 13 has moved the windows setup from AppDeleagte to SceneDelegate to support the use of (possibly multiple) scenes rather than a single window. You now have to do the setup like this:
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
let storyboard = UIStoryboard(name: "Main", bundle: nil)
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = scene as? UIWindowScene else { return }
let vc = storyboard.instantiateViewController (withIdentifier: "Primary") as! ViewController
window = UIWindow(windowScene: windowScene)
window?.rootViewController = vc
window?.makeKeyAndVisible()
}
}

iOS firebase: FIRAuthUIDelegate.authUI not being called

I am trying to launch google login from AppDelegate.swift and then launch my app's main screen upon login success.
I am able to
show the google login button as shown above
the user is sent to google to sign in
the user is sent back to original (step 1)
After step 3. I'd like to send the user to my app's main page.
My code is below. The problem I'm having is that authUI is not being called.
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, FIRAuthUIDelegate {
var window: UIWindow?
var authUI: FIRAuthUI?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
FIRApp.configure()
authUI = FIRAuthUI.defaultAuthUI()
authUI?.delegate = self
let providers: [FIRAuthProviderUI] = [FIRGoogleAuthUI()]
authUI?.providers = providers
// show google login button
let authViewController = authUI?.authViewController()
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = authViewController
self.window?.makeKeyAndVisible()
return true
}
func application(application: UIApplication, openURL url: NSURL, options: [String: AnyObject]) -> Bool {
return GIDSignIn.sharedInstance().handleURL(url, sourceApplication: options[UIApplicationOpenURLOptionsSourceApplicationKey] as? String, annotation: options[UIApplicationOpenURLOptionsAnnotationKey])
}
func authUI(authUI: FIRAuthUI, didSignInWithUser user: FIRUser?, error: NSError?) {
// launch main view controller
}
}
EDIT: This appears to be a duplicate of another question. The other question's title is quite general and only gets to the details a few lines deep. In any case, I believe Chris's answer is more thorough than the one there. I think both the question and answers here are clearer, more pointed and more thorough so it would be a mistake to just direct people here to go there as would happen if this was marked as a duplicate.
I think your problem lies here, in the - (void)signInWithProviderUI:(id<FIRAuthProviderUI>)providerUI method.
The delegate method is called in the dismissViewControllerAnimated:completion: completion block.
[self.navigationController dismissViewControllerAnimated:YES completion:^{
[self.authUI invokeResultCallbackWithUser:user error:error];
}];
As you can see from the Apple docs, this method is expected to be called on a modally presented viewController. You are displaying it as a root view controller. Try displaying it with a modal from a UIViewController, and things should work out. To debug this try and set a breakpoint at line 193 to see that it won't get hit. I would be very surprised if this doesn't work when you display the authController modally.
To come up with a possible solution to your problem (I am assuming you want to ensure a user is signed in before using your app). The below is a simplification of what I am using in an app currently.
EDIT: Updated for the new 1.0.0 FirebaseUI syntax.
class MainTabController: UITabBarController, FIRAuthUIDelegate {
let authUI: FUIAuth? = FUIAuth.defaultAuthUI()
override func viewDidLoad() {
super.viewDidLoad()
var authProviders = [FUIFacebookAuth(), FUIGoogleAuth()]
authUI.delegate = self
authUI.providers = authProviders
//I use this method for signing out when I'm developing
//try! FIRAuth.auth()?.signOut()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !isUserSignedIn() {
showLoginView()
}
}
private func isUserSignedIn() -> Bool {
guard FIRAuth.auth()?.currentUser != nil else { return false }
return true
}
private func showLoginView() {
if let authVC = FUIAuth.defaultAuthUI()?.authViewController() {
present(authVC, animated: true, completion: nil)
}
}
func authUI(_ authUI: FUIAuth, didSignInWith user: FIRUser?, error: Error?) {
guard let user = user else {
print(error)
return
}
...
}
It must be a problem of reference.
class AppDelegate: UIResponder, UIApplicationDelegate, FIRAuthUIDelegate {
var window: UIWindow?
let authUI = FIRAuthUI.defaultAuthUI()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
FIRApp.configure()
authUI.delegate = self
let providers: [FIRAuthProviderUI] = [FIRGoogleAuthUI()]
authUI.providers = providers
// show google login button
let authViewController = authUI.authViewController()
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window?.rootViewController = authViewController
self.window?.makeKeyAndVisible()
return true
}
}
Try this. AppDelegate will hold the reference of authUI and its delegate.

how to use 'presentViewController' with cover View and main page

A lots app, they have the cover view, like facebook, wechat...
but when I try to use presentViewController let the cover present to main page, that still in the cover vie. The following is my code:
var window: UIWindow?
var coverVC:CoverView?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window = UIWindow(frame: UIScreen.mainScreen().bounds)
window?.backgroundColor = UIColor.lightGrayColor()
coverVC = ViewController()
coverVC?.refresh(window!.frame)
window!.makeKeyAndVisible()
return true
}
Coverview
class CoverView: UIViewController {
func refresh(frame:CGRect){
self.view.frame = frame
self.view.backgroundColor = UIColor.whiteColor()
var mainVC:mainView = mainView()
mainVC.refresh(frame)
self.dismissViewControllerAnimated(false, completion: nil)
self.presentViewController(mainVC, animated: false, completion: nil)
}
}
Main view:
class mainView: UIViewController {
func refresh(frame:CGRect){
self.view.frame = frame
self.view.backgroundColor = UIColor.blueColor()
}
}
Maybe can use other easy way to achieve the goal, that's ok.
otherwise, please don't use "UInavigationcontroller", I know that can present view, but I don't want the bar in cover view, thanks.
I find the correct with cover/login view, it is called SplashScreen.
And in xcode 9, you can find at [TARGETS]>[APP icons and Launch Images] (Image).(The past version, you need add "launch image" in plist file.)
Otherwise, If you doesn't want use this view, just delete "LaunchScreen".