How can scene delegate be uses pre iOS 13? - swift

So iOS 13 is really causing trouble for me! I cant get my head around how the SceneDelegate can be uses within my existing application.
Currently I'm using a boolean check in my AppDelegate to determine which ViewController is presented first.. I need to figure out how to replicate this using the SceneDelegate.
My main concern is how can I ensure my app will work on both iOS 13 and any iOS version before? If I add the SceneDelegate won't it crash my app running on pre iOS 13?
Here is my current method of setting the rootVC
if (loggedIn == nil){
Utilities.setLoginAsInitialViewContoller(window: window)
}
else
{
if(termsAgree == nil){
Utilities.setTermsAsInitialViewController(window: window)
}
if(loggedIn != nil){
if(termsAgree != nil){
Utilities.setHomeAsInitialViewContoller(window: window)
}
}
}
class func setLoginAsInitialViewContoller(window:UIWindow) {
let storyboard = UIStoryboard(name: “Login”, bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: “LoginViewController”)
window.rootViewController = controller
window.makeKeyAndVisible()
}
class func setHomeAsInitialViewContoller(window:UIWindow) {
let storyboard = UIStoryboard(name: “Home”, bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: “MainViewNavController”)
window.rootViewController = controller
window.makeKeyAndVisible()
}
class func setTermsAsInitialViewController(window:UIWindow){
let storyboard = UIStoryboard(name: “Terms”, bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: “TermsViewController”)
window.rootViewController = controller
window.makeKeyAndVisible()
}
The above code is contained in my Utilities.swift class and is called in my AppDelegate.swift like this:
Utilities.decideInitialViewController(window: self.window!)

You cannot use the SceneDelegate pre iOS 13, this will actually lead to a compilation error.
xxx is only available in iOS 13.0 or newer
All existing app doesn't require the SceneDelegate to be compatible with iOS 13.
You can just build your application with the latest Xcode version, and you should be good to go (with few adjustments, like dark mode compatibility and some new UI changes, but this won't prevent your app from running in an iOS 13+ device).
Even if I would not recommend that, you still can use the SceneDelegate, but you must use the #available tag to tell Xcode that this code will only be used for iOS 13+ devices.
This means that iOS 13+ devices will use the SceneDelegate, and iOS 12 and less, will continue to use the AppDelegate. This method require to maintain some backward compatibility only code.
There is some explanation on that here

Related

How do I change viewControllers in swift with Foundation Xcode

I am trying to programmatically switch to another viewController scene when a certain event happens in Xcode for a MacOS app but am having trouble.
The second view controller is in the same storyboard as the one I am trying to switch from.
I currently have
let secondViewController = ViewController(nibName: "outcome", bundle: nil)
self.present(secondViewController, animator: true as! NSViewControllerPresentationAnimator)
but it seems to crash when the event is triggered:
Could not cast value of type 'Swift.Bool' (0x2010043a8) to '__C.NSViewControllerPresentationAnimator' (0x2007787d0).
it also shows an error Thread 1: signal SIGABRT on the second command where I self.present
you are trying to cast a bool to NSViewControllerPresentationAnimator
try this code instead:
let secondViewController = ViewController(nibName: "outcome", bundle: nil)
self.present(secondViewController, animator: true)

EXC_BAD_ACCESS on device, but works on a Simulator

I had written a function below which is inside Framework A:
open func invokeTracking(key: String, bundle: Bundle? = Bundle.main) {
let qos = DispatchQoS.QoSClass.default
DispatchQueue.global(qos: qos).async {
self.invokeAsyncTracking(key: String, bundle: bundle)
}
And I am calling this in an Xcode project in multiple places and also from other frameworks.
In Xcode project:
override func viewDidLoad() {
super.viewDidLoad()
HelperClass.invokeTracking(key: "Camino", bundle: Bundle? = Bundle.main)
}
Similarly calling it from Framework B
func callMainTracking() {
super.viewDidLoad()
HelperClass.invokeTracking(key: "Carthage", bundle: Bundle? = Bundle.main)
}
This is working fine and has worked for almost a year. Recently I added another additional default parameter to the invoke tracking method like this so as not to affect the existing method calls like below. And I am trying to set the value of this parameter of a global variable:
open func invokeTracking(key: String, bundle: Bundle? = Bundle.main, conventDict: [String: Any] = [:]) {
mainDict = conventDict
let qos = DispatchQoS.QoSClass.default
DispatchQueue.global(qos: qos).async {
self.invokeAsyncTracking(key: String, bundle: bundle)
}
The app is crashing in the main thread on the line mainDict = conventDict when trying to access that default parameter. If I do it inside the async its crashing in that async thread. But this crash is happening only when it's called from Framework B and not when its called from the Xcode project. Also this crash is happening only on the device.
I have tried the following to resolve this:
Tried to declare default parameter as optional and using if let to unwrap it.
Tried with different data types.
Tried by changing arguments order.
And the only way it does not crash is if I send a value for this default parameter from Framework B while calling this method.
func callMainTracking() {
super.viewDidLoad()
HelperClass.invokeTracking(key: "Carthage", bundle: Bundle? = Bundle.main, conventDict: ["test": "123"])
}
But this defeats the purpose of a usage of a default parameter. Im new to debugging Xcode crashes so any help would be appreciated.
I used the Instruments and tried to find a Zombie but there wasnt one. The app simply just crashed and no zombie process was detected.

instantiateViewController throws SIGABRT on Unit Test

I'm writing unit tests for a controller that has elements (like buttons) created in storyboard. I'm trying to instantiate the view controller in my Unit Test so I can access those elements and not have the app crash. However, the way that I am trying to instantiate my storyboard is causing Xcode to throw a SIGABRT error on the line that I am calling the instantiateViewController command.
This is the code that I am using to try and instantiate my storyboard in my unit test file:
func testAreaActionsViewController() {
let storyboard = UIStoryboard(name: "Main", bundle: Bundle(for: self.classForCoder))
let viewController = storyboard.instantiateViewController(withIdentifier: "AreaActionsViewController") as! AreaActionsViewController
// view.loadView()
//
// view.viewDidLoad()
}
Does anyone have an idea as to why it is throwing SIGABRT? I verified in my storyboard that the Identifier is AreaActionsViewController, and also set the target in my Main.storyboard to include my testing target.
Sorry for late but I had same issue and I solved removing this:
#testable import ProjectName

Instantiating Window from AppDelegate Failing in Swift 5.0 [duplicate]

This question already has an answer here:
Xcode 11 & iOS13, using UIKIT can't change background colour of UIViewController
(1 answer)
Closed 3 years ago.
I'm using Xcode 11 and Swift 5.0. I copied over my code from another app that used Swift 4.2. That code instantiates a tabBarViewController in the AppDelegate, changes the window to the tabBarVC and shows it if certain conditions are met. But it is not working in Swift 5.0 and Xcode 11. Here's the code:
var window: UIWindow?
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
// Run Firebase
FirebaseApp.configure()
// Check Firebase Auth to see if the user is signed in already.
if Auth.auth().currentUser != nil {
print ("User is already authenticated and signed in.")
// Check to make sure the user is stored in local settings.
if let appUser = LocalStorageService.loadCurrentUser() {
print ("Local storage has a user: \(appUser)")
// Create a tab bar controller.
let tabBarVC = UIStoryboard(name: "Main", bundle: .main).instantiateViewController(withIdentifier: Constants.Storyboard.tabBarController)
// Show it
window?.rootViewController = tabBarVC
window?.makeKeyAndVisible()
}
return true // If auth has a user and there is local user, window changes.
} else {
print ("No Authenticated current user, must proceed with Login.")
return false
}
}
The tabBarVC is populated successfully, but window remains nil. Then the app shows a different view controller. Nothing is different in the code from the original app that I can see. And that one still works. Thoughts would be appreciated.
I deleted the SceneDelegate from the Navigator and the following sections from the info.plist dealing with Scenes and now it works as expected (the other app did not have the SceneDelegate or these sections in the info.plist. I assume they were added when the project was created in Xcode 11):
I read about scenes for iOS 13 in the Apple Documentation. I don't plan to use scenes, but if someone does want to use scenes, they will have to determine how to configure the info.plist to allow the instantiation of a VC in the AppDelegate.

Relaunch view when app is closed

I am creating a status bar app for Mac with a settings view.
I have created a NSMenuItem to launch the settings but I don't find any solutions to launch this view.
What I have tried:
NSWorkspace.shared().launchApplication("AppName")
and
StatusMenuController.swift
func showSettings() {
var mainWindow: MainWindowController!
mainWindow = MainWindowController()
mainWindow.showWindow(nil)
}
MainWindowController.swift
override func windowDidLoad() {
super.windowDidLoad()
self.window?.center()
self.window?.makeKeyAndOrderFront(nil)
NSApp.activate(ignoringOtherApps: true)
}
I saw this problem was faced in many cases and I found a solution that I don't completely understand but that I'll try to explain.
Here is how documents are managed in macOS :
From my experience, you need to check the box "Create Document-Based Application" when creating a new Mac app to have a NSDocument class (You can also add it later) which will handle how your views are being showed or not.
Adding this NSDocument class to my project made the following code work (and wasn't before) :
let storyboard = NSStoryboard(name: "Main", bundle: nil)
var windowController: NSWindowController!
windowController = storyboard.instantiateController(withIdentifier: "ScanWindowController") as! NSWindowController
windowController.showWindow(nil)