Detecting Source Application for Launch Depracated? Aternatives? - swift

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 }
}

Related

Unable to access root view after adding SceneDelegate swift 5

I have an app where users can login / sign out and the functionality stopped working recently when I added a new SceneDelegate view and moved over the code from the AppDelegate. I'm not sure why it is not working but I suspect it has to do with using the shared delegate in my signOut function.
Something strange is happening, when I tap the sign out button nothing happens. However, when I close the app and open it again, I will be signed out.
Here is the code on my home screen for the sign out button:
#IBAction func signOutButtonTapped(_ sender: Any) {
KeychainWrapper.standard.removeObject(forKey: "accessToken")
KeychainWrapper.standard.removeObject(forKey: "userID")
// send user to splash page
let signInPage = self.storyboard?.instantiateViewController(withIdentifier: "splashController") as! splashViewController
let appDelegate = UIApplication.shared.delegate
appDelegate?.window??.rootViewController = signInPage
}
This is the code from my SceneDelegate.swift file:
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
let accessToken: String? = KeychainWrapper.standard.string(forKey: "accessToken")
// If access token exists, skip login page
if accessToken != nil {
if let windowScene = scene as? UIWindowScene {
self.window = UIWindow(windowScene: windowScene)
let mainStoryboard:UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = mainStoryboard.instantiateViewController(withIdentifier: "homeTabController") as! TabBarController
self.window!.rootViewController = vc
}
}
}
}
You've answered your own question. With a scene delegate, the window belongs to the scene delegate. The app delegate window is nil. So this line does nothing:
appDelegate?.window??.rootViewController = signInPage
Figured it out - add a new var in SceneDelegate
static weak var shared: SceneDelegate?
Then replace the appDelegate.window line with
let appDelegate = SceneDelegate.shared

go directly to different page using quick Action shortcut in swift

my app has 2 pages(VCs) which are created and being controlled in a BossPageViewController and I want to give the user an option of going directly to second page using a Shortcut action.
now I know how to create shortcuts and how to trigger them and I also know how to open that page as root but when I open it as root I no longer can swipe to first page and also the pageDots aren't there so I should close the app and run it again to be able to swipe between pages and see pageDots.
code below is where second page shortcut triggers, I did copy and past the whole sceneDelegate here, maybe someone wants to test it:
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
var shortcutItemManager: UIApplicationShortcutItem?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if let shortcutItem = connectionOptions.shortcutItem {
shortcutItemManager = shortcutItem
}
guard let _ = (scene as? UIWindowScene) else { return }
}
func windowScene(_ windowScene: UIWindowScene, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: #escaping (Bool) -> Void) {
shortcutItemManager = shortcutItem
}
//MARK:- Shortcuts are Triggered here.
func sceneDidBecomeActive(_ scene: UIScene) {
if let shortcutItem = shortcutItemManager {
if shortcutItem.type == "com.x.appname.openSecondPage" {
///- tag: code lines that run secondPage as root VC
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
window.rootViewController = storyboard.instantiateViewController(withIdentifier: "SecondPageVC")
self.window = window
window.makeKeyAndVisible()
}
shortcutItemManager = nil
}
}
}
}
I also don't want to instantiate the page as popover or modal or anything else I mean just want that page as it is and not the copy of that page. code example of what I mean as copy:
let openSecondPage = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "SecondPageVC") as! SecondPageViewController
window?.rootViewController?.present(openSecondPage, animated: false, completion: nil)
I have been searching for a month but there wasn't anything exactly like this, they were similar but their code solution wasn't for me, that's why I did post this.
Swift 5.1
Ok by mixing and doing trial and error on different codes posted on Stackoverflow for different questions, finally I made that work for me as I wanted and I thought It's better to share it cause I know someone will need it or may have better approach to this that can share with us.
Use the code bellow where shortcut Item gets triggered or if you want to run it on app launch use it inside *****func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)***** in sceneDelegate.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
let bossPageVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "BossPageVCID") as! BossPageViewController
self.window = window
UIView.transition(with: window, duration: 0.1, options: .transitionCrossDissolve, animations: {
window.rootViewController = bossPageVC
window.makeKeyAndVisible()
}, completion: { completed in
// viewControllerPages is a viewController array I created in BossPageViewController and index of 1 means the second page
if let secondPage = bossPageVC.viewControllerPages[1]
bossPageVC.setViewControllers([secondPage], direction: .forward, animated: true, completion: nil)
})
}

Completely black screen on setting initial view controller in AppDelegate

All I want to do is go to the login screen if the user = nil. Otherwise it goes to the main/home section of the app which consists of the Tab Bar Controller's first view controller
Here is my app delegate:
import UIKit
import Firebase
#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.
FirebaseApp.configure()
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if Auth.auth().currentUser == nil && !UserDefaults.standard.bool(forKey: "hasViewedWalkthrough") {
let initialViewController = storyboard.instantiateViewController(withIdentifier: "WalkthroughViewController") as? WalkthroughViewController
self.window?.rootViewController = initialViewController
} else if Auth.auth().currentUser == nil && UserDefaults.standard.bool(forKey: "hasViewedWalkthrough") {
let initialViewController = storyboard.instantiateViewController(withIdentifier: "loginViewController") as? LoginViewController
self.window?.rootViewController = initialViewController
} else {
let initialViewController = storyboard.instantiateViewController(withIdentifier: "homeTabBarController") as? MainTabBarViewController
self.window?.rootViewController = initialViewController
}
self.window?.makeKeyAndVisible()
return true
}
I am not able to get to the onboarding or login screen. I am always led to the main screen. I know the screens are rendering fine if I manually set them as initial view controllers in storyboard.
I have tried numerous solutions and none of them are working.
Update
Try this:
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController = storyBoard.instantiateViewControllerWithIdentifier("homeTabBarController") as? TabBarViewController // whatever your swift file is called
self.window?.rootViewController = storyBoard
use that code for each statement for the if statement, obviously change the names of the view controllers to the login one respectively.
For people reading this in the future, iOS 13 introduced SceneDelegate and now all UI related stuff should be configured from that point.
I faced the same issue and solved it by using this code.
The custom window should be configured from here now.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let sceneWindow = (scene as? UIWindowScene) else { return }
window = UIWindow(frame: sceneWindow.coordinateSpace.bounds)
window?.windowScene = sceneWindow
window?.makeKeyAndVisible()
if let navigationController = Storyboard.Main.initialViewController as? UINavigationController,
let viewController = navigationController.children.first as? CountriesViewController {
viewController.inject(CountriesViewModel(provider: CountryAPI(),
informTo: viewController),
navigator: CountriesNavigator(navigationController: navigationController))
window?.rootViewController = navigationController
}
}

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()
}
}

Issue with presenting viewcontroller in iOS 13, Xcode 11

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];