Containing App crashes when opened from Today extension in swift 4 - ios

i have a swift app for which i have a today extension,it has a button which opens the containing app.
the button opens the app perfectly when the app is in the recent list but crashes when the app is moved from the recent list.
there's no crashlogs too
this is my code on app delegate :
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
var mainViewController : MainViewController!
mainViewController = UIApplication.shared.keyWindow?.rootViewController?.childViewControllers[1].childViewControllers[0] as! MainViewController
if url.scheme == "open"
{
switch url.host
{
case "1"?:
mainViewController.isTaxi = true
break
case "2"?:
mainViewController.isPfp = true
break
case "3"?:
mainViewController.isDarbi = true
break
default:
break
}
}
return true
}
this is how i open in main VC :
var isTaxi : Bool? {
didSet{
if UserDefaults.getUser() != nil {
self.taxiRegViewController.show()
} else {
self.taxiNotRegViewController.show()
}
}
}
this is where i fire tap event in extension :
#IBAction func bookTaxiTapped(_ sender: UIButton) {
if let url = URL(string: "open://\(sender.tag)")
{
self.extensionContext?.open(url, completionHandler: nil)
}
}

I solved my problem,by modifying the app delegate's method like this :
Actual Problem was : we had SWRevealViewController which has to be initiated before calling other view controller's
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
var mainViewController : MainViewController!
self.window = UIWindow(frame: UIScreen.main.bounds)
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyBoard.instantiateViewController(withIdentifier: "swRevealController") as! SWRevealViewController
mainViewController = storyBoard.instantiateViewController(withIdentifier: "mainView") as! MainViewController
self.window?.rootViewController = viewController
self.window?.makeKeyAndVisible()
viewController.setFront(mainViewController, animated: true)
if url.scheme == "open"
{
switch url.host
{
case "1"?:
mainViewController.isTaxi = true
break
case "2"?:
mainViewController.isPfp = true
break
case "3"?:
mainViewController.isDarbi = true
break
default:
break
}
}
return true
}

Related

Presenting different UIViewController on the app Launch

I need to present different UIViewController on the launch of my app. I have a "login" page for the user to interact with. Once the user logs in or creates an account it takes to a different UIViewController with a map to interact with. I've looked online and so far I know that we should do it within AppDelegate.swift file. I have not completed the statement whether or not the user has logged in since I am still running into some errors
AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Thread.sleep(forTimeInterval: 2.0)
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
window?.rootViewController = MainNavigationContoller()
return true
}
I also have another swift file with MainNavigationContoller that should call the mainviewController
override func viewDidLoad() {
super.viewDidLoad()
let isloggedIn = false
if isloggedIn == false {
self.present(mainViewController(), animated: true, completion: nil)
} else {
self.present(mapViewController(), animated: true, completion: nil)
}
}
The app launches with the launchScreen but then sends errors to mainViewController such as Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
You are not instantiating the View Controllers properly.
Here is a function that I use to make a root view controller, put this into your AppDelegate.
func makeRootVC(storyBoardName : String, vcName : String) {
let vc = UIStoryboard(name: storyBoardName, bundle: Bundle.main).instantiateViewController(withIdentifier: vcName)
let nav = UINavigationController(rootViewController: vc)
nav.navigationBar.isHidden = true
self.window?.rootViewController = nav
let options: UIView.AnimationOptions = .transitionCrossDissolve
let duration: TimeInterval = 0.6
UIView.transition(with: self.window!, duration: duration, options: options, animations: {}, completion: nil)
}
Now in your Appdelegate, replace your code with this:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Thread.sleep(forTimeInterval: 2.0)
self.makeRootVC(storyboardName: "Main", vcName : "YourVCStoryboardId")
return true
}
and in your MainNavigationController,
override func viewDidLoad() {
super.viewDidLoad()
let isloggedIn = false
let appDelegateObj = UIApplication.shared.delegate as! AppDelegate
if isloggedIn == false {
appDelegateObj.makeRootVC(storyboardName: "Main", vcName: "mainViewController")
} else {
appDelegateObj.makeRootVC(storyboardName: "Main", vcName: "mapViewController")
}
}
NOTE: Open storyboard and give every controller a StoryboardID. What I prefer is naming them the same as ViewController's name as it is easy to remember. and in vcName, we need to pass the storyboarID of the controller we want to present.
UPDATE:
Above code is for making a root view controller, if you want to push controllers, you can use this code instead:
extension UIViewController {
func pushVC(storyboardName : String, vcname : String) {
let vc = UIStoryboard.init(name: storyboardName, bundle: Bundle.main).instantiateViewController(withIdentifier: vcname)
vc.hidesBottomBarWhenPushed = true
self.navigationController?.pushViewController(vc, animated: true)
}
}
In your MainNavigationController if instead of making root view controllers in viewDidLoad, you want to just push the controller, then use the above code like this:
override func viewDidLoad() {
super.viewDidLoad()
let isloggedIn = false
if isloggedIn == false {
self.pushVC(storyboardName: "Main", vcName: "mainViewController")
} else {
self.pushVC(storyboardName: "Main", vcName: "mapViewController")
}
}

Calling Present on View Fails with modally active controller error

When calling present on a hamburger menu, it succeeds on the first load but fails on the second.
Once the app launches you log in. It then calls the listener to perform actions after log in. You can click the burger menu on the left and it works. Now, if you log out, log back in and then hit the burger menu again, it throws the error Application tried to present modally an active controller HomePageViewController. It seems as if something was loaded correctly when the app first loads and it's trying to do it again which is causing to fail. I can't figure out what. Any help would be appreciated here. I've included AppDelegate.swift and AppController.swift which both are relevant here.
#objc func burgerButtonAction(_ sender: Any) {
present(SideMenuManager.default.menuLeftNavigationController!, animated: true, completion: nil)
}
AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
AppController.sharedInstance.enableCognitoClientWithAuthentication()
// setup logging
self.window = UIWindow(frame: UIScreen.main.bounds)
AppController.sharedInstance.launchInWindow(aWindow: self.window)
let signedIn = AWSMobileClient.sharedInstance().isSignedIn
self.navigationController = UINavigationController()
if !signedIn {
navigationInit()
} else {
AppController.sharedInstance.showLoggedInStateAndReturn(true)
}
//pool.delegate = self
return true
}
func navigationInit() {
let loginViewController = LoginViewController()
self.navigationController!.pushViewController(loginViewController, animated: false)
}
AppController.swift
class AppController: NSObject {
func launchInWindow(aWindow: UIWindow?){
self.window = aWindow
self.initializeSDKs()
self.globalCustomization()
self.AWSUnAuthedClient.apiKey = Constants.APIKeys.AWSAPIKey
DispatchQueue.main.async {
self.window!.rootViewController = self.createAndReturnRootVC()
self.window!.makeKeyAndVisible()
}
}
private func createAndReturnRootVC() -> UIViewController {
// Auth Status
let cognitoAuth = AWSCognitoAuth.default()
if AWSMobileClient.sharedInstance().isSignedIn {
return self.showLoggedInStateAndReturn(true)!
} else {
let loginVC = LoginViewController()
return loginVC
}
}
func enableCognitoClientWithAuthentication() {
AWSMobileClient.sharedInstance().initialize { (userState, error) in
if let userState = userState {
print("UserState: \(userState.rawValue)")
}else if let error = error {
print("error: \(error.localizedDescription)")
}
}
AWSMobileClient.sharedInstance().addUserStateListener(self) { (userState, info) in
switch (userState) {
case .signedOut:
// user clicked signout button and signedout
print("user signed out")
self.window?.rootViewController = LoginViewController()
case .signedOutUserPoolsTokenInvalid:
print("need to login again.")
AppController.sharedInstance.showLogin()
//Alternatively call .showSignIn()
case .signedIn:
self.registerForPush()
self.hydrateLocalUser()
self.launchInWindow(aWindow: self.window)
// DispatchQueue.main.async {
// self.window!.rootViewController = self.createAndReturnRootVC()//self.showLoggedInStateAndReturn(true)
// self.window!.makeKeyAndVisible()
// }
default:
print(userState)
print("unsupported")
}
}
}
#discardableResult func showLoggedInStateAndReturn(_ shouldReturn: Bool) -> UIViewController? {
//AppController.sharedInstance.enableCognitoClientWithAuthentication()
//self.registerForPush()
self.tabBarController = ESTabBarController()
//tabBarController.delegate = delegate
self.tabBarController?.title = "Irregularity"
self.tabBarController?.tabBar.shadowImage = UIImage.image(with: UIColor("FFFFFF", alpha: 0.0)!)
self.tabBarController?.tabBar.backgroundImage = UIImage.image(with: UIColor("2A2A27")!)
self.tabBarController?.shouldHijackHandler = {
tabbarController, viewController, index in
if index == 1 {
return true
}
return false
}
self.tabBarController?.didHijackHandler = {
[weak tabBarController] tabbarController, viewController, index in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
let newProjectNavCon = UINavigationController(rootViewController: NewProjectViewController())
newProjectNavCon.hero.isEnabled = true
newProjectNavCon.setNavigationBarHidden(true, animated: false)
newProjectNavCon.hero.navigationAnimationType = .fade
tabBarController?.present(newProjectNavCon, animated: true, completion: nil)
}
}
let centerVC = UINavigationController(rootViewController: HomeViewController())
let v1 = centerVC
let v2 = BaseViewController()
let v3 = UINavigationController(rootViewController: ProfileViewController())
v1.tabBarItem = ESTabBarItem.init(TabBarBouncesContentView(), title: "Projects", image: UIImage(named: "tabBarTruck"), selectedImage: UIImage(named: "tabBarTruck"))
v2.tabBarItem = ESTabBarItem.init(TabBarIrregularityContentView(), title: nil, image: UIImage(named: "tabBarPlusButton"), selectedImage: UIImage(named: "tabBarPlusButton"))
v3.tabBarItem = ESTabBarItem.init(TabBarBouncesContentView(), title: "Profile", image: UIImage(named: "tabBarProfile"), selectedImage: UIImage(named: "tabBarProfile"))
self.tabBarController?.setViewControllers([v1, v2, v3], animated: true)
if shouldReturn {
return self.tabBarController
} else {
self.window?.rootViewController = self.tabBarController
return nil
}
}
}

How to redirect to a specific view controller using today extension?

I can't redirect to a specific view controller when clicking on a button of today extension widget.
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
let urlHost : String = url.host as String!
let mainStoryboard: UIStoryboard = UIStoryboard(name: "BLE", bundle: nil)
if(urlHost == "BLELoginViewController")
{
let innerPage: BLELoginViewController = mainStoryboard.instantiateViewController(withIdentifier: "BLELoginViewController") as! BLELoginViewController
self.window?.rootViewController = innerPage
}
return true
}
class TodayViewController: UIViewController, NCWidgetProviding {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
}
func widgetPerformUpdate(completionHandler: (#escaping (NCUpdateResult) -> Void)) {
// Perform any setup necessary in order to update the view.
// If an error is encountered, use NCUpdateResult.Failed
// If there's no update required, use NCUpdateResult.NoData
// If there's an update, use NCUpdateResult.NewData
completionHandler(NCUpdateResult.newData)
}
#IBAction func extensionAction(_ sender: Any) {
let url: NSURL = NSURL(string: "UEM://BLELoginViewController")!
self.extensionContext?.open(url as URL, completionHandler: nil)
}
}
Make sure that UEM is installed in your device.And have a try follow code:
let urlString = "UEM://BLELoginViewController"
if let url = URL(string: urlString) {
if #available(iOS 10, *) {
self.extensionContext?.open(url, options: [:],
completionHandler: {
(success) in
})
} else {
self.extensionContext?.open(url)
}
}
And Comment out the code first to test whether can open it not with special view:
//let urlHost : String = url.host as String!
//let mainStoryboard: UIStoryboard = UIStoryboard(name: "BLE", bundle: nil)
//if(urlHost == "BLELoginViewController")
//{
// let innerPage: BLELoginViewController = mainStoryboard.instantiateViewController(withIdentifier: "BLELoginViewController") as! BLELoginViewController
//self.window?.rootViewController = innerPage
//}

Navigation Controller becomes nil from todayextension

i have a today extenstion in which there is a button to launch the app,when the app is launched from this button navigation controller becomes nil
i have no idea why this happens ?
this is my code in appdelegate :
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
var storyBoard: UIStoryboard!
var mainViewController: MainViewController!
self.window = UIWindow(frame: UIScreen.main.bounds)
if UserDefaults.getLanguage() == "ar" {
storyBoard = UIStoryboard(name: "MainAR", bundle: nil)
} else {
storyBoard = UIStoryboard(name: "Main", bundle: nil)
}
let viewController = storyBoard.instantiateViewController(withIdentifier: "swRevealController") as! SWRevealViewController
mainViewController = storyBoard.instantiateViewController(withIdentifier: "mainView") as! MainViewController
self.window?.rootViewController = viewController
self.window?.makeKeyAndVisible()
viewController.setFront(mainViewController, animated: true)
if url.scheme == "open"
{
switch url.host
{
case "1"?:
mainViewController.isTaxi = true
break
case "2"?:
mainViewController.isPfp = true
break
case "3"?:
mainViewController.isDarbi = true
break
default:
break
}
}
return true
}
please anyone can help ?
This is because of this line that ovewrites the storyboard navigation
self.window?.rootViewController = viewController
you have to embed it inside a navigation like this
self.window?.rootViewController = UINavigationController(rootViewController: viewController)
Edit: before this line init isTaxi
mainViewController.isTaxi = // set it to true / false
viewController.setFront(mainViewController, animated: true)
//
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
var storyBoard: UIStoryboard!
var mainViewController:MainViewController!
self.window = UIWindow(frame: UIScreen.main.bounds)
if UserDefaults.getLanguage() == "ar" {
storyBoard = UIStoryboard(name: "MainAR", bundle: nil)
} else {
storyBoard = UIStoryboard(name: "Main", bundle: nil)
}
mainViewController = storyBoard.instantiateViewController(withIdentifier: "mainView") as! MainViewController
if url.scheme == "open"
{
switch url.host
{
case "1"?:
mainViewController.isTaxi = true
break
case "2"?:
mainViewController.isPfp = true
break
case "3"?:
mainViewController.isDarbi = true
break
default:
break
}
}
let viewController = storyBoard.instantiateViewController(withIdentifier: "swRevealController") as! SWRevealViewController
viewController.setFront(mainViewController, animated: true)
self.window?.rootViewController = UINavigationController(rootViewController: viewController)
self.window?.makeKeyAndVisible()
return true
}

Swift UIBarButtonItem is hidden but works

I have an problem with an UIBarButtonItem. If I protect my app with an ViewController, to authenticate the User through Touch ID, then the UIBarButtonItem in the Navigation Controller is hidden.
The code in the AppDelegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let touchIDonoff = UserDefaults.standard.object(forKey: "touchIDActive") as! Bool!
if touchIDonoff == true {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginVC = storyboard.instantiateViewController(withIdentifier: "TouchIDViewController") as! TouchIDViewController
self.window?.rootViewController = loginVC
}
return true
}
Code in the ViewController for TouchID-Request:
override func viewDidLoad() {
super.viewDidLoad()
DispatchQueue.main.async {
self.touchIDrequest()
}
#IBAction func buttonTouchID(_ sender: AnyObject) {
touchIDabfrage()
}
func touchIDrequest() {
let authenticationContext = LAContext()
var error: NSError?
if authenticationContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
authenticationContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Please authenticate", reply: { (success: Bool, error: Error?) in
if success {
self.navigatetoAuthenticatedVC()
} else {
if let evaluateError = error as? NSError {
print(error)
/*let message = self.errorMessageForLAErrorCode(errorCode: evaluateError.code)
self.showAlertViewAfterEvaluatingPolicyWithMessage(message: message)
*/
}
}
})
} else {
showAlertViewForNoBiometrics()
return
}
}
func navigatetoAuthenticatedVC() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let tabBarController = storyboard.instantiateViewController(withIdentifier: "LoggedInVC") as! UITabBarController
let appDelegate = UIApplication.shared.delegate as! AppDelegate
DispatchQueue.main.async {
appDelegate.window?.rootViewController = tabBarController
}
}
If I hit the hidden UIBarButtonItem it works and bring me to the next ViewController and when I go back, the UIBarButtonItem is visible.
What did I wrong? Any help please?
Best regards

Resources