Calling Present on View Fails with modally active controller error - ios

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

Related

How to dismiss view controller from SceneDelegate?

I am working on an ios App (Swift 5) and I am trying to show a screen if the app is offline and then dismisses when the user re-connects.
I am expecting the OfflineViewController to appear when the user is offline, and the last screen the user on to appear if they are connected.
What is happening is the OfflineViewController is appearing when I disconnect from the network, however it does not go away when I connect back to the network. I tried adding a button to dismiss and this does not work either.
I've attached my code below, any idea what am I doing wrong?
SceneDelegate
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
let reachability = try! Reachability()
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
// Send to homepage if logged in, otherwise login screen.
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
}
}
reachability.whenUnreachable = { [self] _ in
print("Not reachable (Scene delegate)")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "OfflineViewController") as! OfflineViewController
vc.modalPresentationStyle = .fullScreen
guard let firstScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
return
}
guard let firstWindow = firstScene.windows.first else {
return
}
let rootVC = firstWindow.rootViewController
rootVC?.dismiss(animated: true, completion: nil)
rootVC!.present(vc, animated: true, completion: nil)
}
do {
try reachability.startNotifier()
} catch {
print("Unable to start notifier")
}
}
}
OfflineViewController
import UIKit
class OfflineViewController: UIViewController {
let reachability = try! Reachability()
override func viewDidLoad() {
super.viewDidLoad()
do {
try reachability.startNotifier()
} catch {
print("Unable to start notifier")
}
}
#IBAction func hitRefresh(_ sender: Any) {
reachability.whenReachable = { reachability in
self.dismiss(animated: true, completion: nil)
}
}
}
I would start be removing all of the reachability code from OfflineViewController. The logic to dismiss belongs with the logic to present.
Then update the reachability code in the scene delegate with a whenReachable block that dismisses the current OfflineViewController.
You should also avoid using UIApplication.shared.connectedScenes in the scene delegate code. You already know the scene. No need to go find it.
The updated whenUnreachable:
reachability.whenUnreachable = { [self] _ in
print("Not reachable (Scene delegate)")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "OfflineViewController") as! OfflineViewController
vc.modalPresentationStyle = .fullScreen
guard let winScene = (scene as? UIWindowScene) else { return }
guard let firstWindow = winScene.windows.first else {
return
}
let rootVC = firstWindow.rootViewController
rootVC?.dismiss(animated: true, completion: nil)
rootVC?.present(vc, animated: true, completion: nil)
}
The added whenReachable:
reachability.whenReachable = { [self] _ in
print("Reachable (Scene delegate)")
guard let winScene = (scene as? UIWindowScene) else { return }
guard let firstWindow = winScene.windows.first else {
return
}
let rootVC = firstWindow.rootViewController
rootVC?.dismiss(animated: true, completion: nil)
}

How to open a link from Smooch chat at webView instead Safari browser Swift

I'm using Smooch library for integration chat into the app. In addition, I've implemented Universal Links.
Programming language - Swift / iOS version - 12.4+ / Smooch SDK version 7.12
Problem:
When I am tapping on the link from Smooch chat it opens me Safari browser instead webView!!!
Universal link works correctly, I can be redirected by tapping on the link from other Apps (WhatsApp, Safari, telegram ...)
But just taping in the link from the smooch chat redirects me to the Safari browser. How I can stay in the App?
Here main VC where I'm loading webView and button with Smooch chat:
override func viewDidLoad() {
super.viewDidLoad()
if let data = userDefaults.dictionary(forKey: "data"){
responseData = data
}
loadWebKit()
checkToken()
initSmooch()
setupChatBtnView()
}
//showing smooch chat view
#IBAction func chatBtnDidPress(_ sender: UIButton) {
Smooch.show()
}
//chat button design
func setupChatBtnView(){
chatBtnOutlet.layer.cornerRadius = chatBtnOutlet.bounds.self.width / 2.0
chatBtnOutlet.clipsToBounds = true
chatBtnOutlet.layer.shadowRadius = 1
chatBtnOutlet.layer.shadowColor = UIColor(red: 255/255, green: 170/255, blue: 0/255, alpha: 0.5).cgColor
chatBtnOutlet.layer.shadowOpacity = 0.5
chatBtnOutlet.layer.shadowOffset = CGSize(width: 0.0, height: 3.0)
chatBtnOutlet.layer.masksToBounds = false
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
Smooch.close()
}
//webkit view url load and adding js event listeners
func loadWebKit(){
webView.navigationDelegate = self
webView.configuration.userContentController.add(self, name: "finishLoading")
webView.configuration.userContentController.add(self, name: "logout")
webView.isHidden = true
activityIndicator.isHidden = false
if appDelegate.externalUrl == nil {
webView.load(URLRequest(url: URL(string: "https://example.com")!))
} else {
deepLinkURL = self.appDelegate.externalUrl!
let urlRequest = URLRequest(url: deepLinkURL!)
self.webView.load(urlRequest)
isFromDeepLing = true
webView.isHidden = true
activityIndicator.isHidden = false
print(urlRequest)
}
}
//when loading url finish call a js functions
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
guard let dataForJS = userDefaults.string(forKey: "dataJs") else {
self.webView.isHidden = false
self.activityIndicator.isHidden = true
return
}
if !isFromDeepLing {
webView.evaluateJavaScript("handleNativeLogin('\(dataForJS)')") { (result, error) in
self.webView.isHidden = false
self.activityIndicator.isHidden = true
}
} else {
if !isUsedDeepLink {
webView.evaluateJavaScript("handleNativeLogin('\(dataForJS)')") { (result, error) in
}
webView.isHidden = true
activityIndicator.isHidden = false
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
let urlRequest = URLRequest(url: self.deepLinkURL!)
self.webView.load(urlRequest)
self.isUsedDeepLink = true
}
} else {
self.webView.isHidden = false
self.activityIndicator.isHidden = true
}
}
}
//segue to login screen and logout from smooch
func goToLoginScreen(){
Smooch.logout {(error:Error?, userInfo:[AnyHashable : Any]?) in
}
userDefaults.removeObject(forKey: "data")
performSegue(withIdentifier: "loginViewController", sender: self);
}
// check if token is valid . if not, go to login screen
func checkToken(){
if let token = userDefaults.dictionary(forKey: "data")?["access_token"]{
serverManager.checkToken(token: token as! String) { (code) in
if(code == "200"){
print("token is valid")
}else if(code == "401"){
print("token expierd")
self.goToLoginScreen()
}
}
}
}
// connect to smooch service , init and login
func initSmooch(){
guard let appId = responseData["smoochAppId"] ,let userId = responseData["smoochUserId"], let smoochToken = responseData["smoochJWTToken"] else {
chatBtnOutlet.isHidden = true
return
}
Smooch.initWith(SKTSettings(appId: appId as! String)) { (error:Error?, userInfo:[AnyHashable : Any]?) in
if(error == nil){
print("smooch init success")
Smooch.login(userId as! String , jwt: smoochToken as! String, completionHandler: { (error:Error?, userInfo: [AnyHashable : Any]?) in
if(error == nil){
print("smooch user login success")
}else{
print("smooch user login failed")
}
})
}else{
print("smooch init failed")
}
}
}
// listen to js event
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if(message.name == "finishLoading"){
print("finishLoading")
}else if (message.name == "logout"){
userDefaults.set(false, forKey: "isLoggedIn")
self.goToLoginScreen()
}
}
And here appDelegate where I'm using Universal link:
// MARK: - Deep Linking
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL else { return false }
var viewController = UIViewController()
let userDefaults = UserDefaults.standard
let isLoggedIn = userDefaults.bool(forKey: "isLoggedIn")
if url.path == "/login" || isLoggedIn == false {
viewController = self.getDestinationLogin(for: url)
} else {
viewController = self.getDestinationMain(for: url)
}
externalUrl = url
window?.rootViewController = viewController
window?.makeKeyAndVisible()
return (parseAppLinks(from: url) != nil)
}
func getDestinationLogin(for url: URL) -> UIViewController {
let storyboard = UIStoryboard(name: "Main", bundle: .main)
let loginVC = storyboard.instantiateViewController(withIdentifier: "loginViewController") as? LoginViewController
return loginVC!
}
func getDestinationMain(for url: URL) -> UIViewController {
let storyboard = UIStoryboard(name: "Main", bundle: .main)
let mainVC = storyboard.instantiateViewController(withIdentifier: "MainViewController") as? MainViewController
let userDefaults = UserDefaults.standard
userDefaults.set(true, forKey: "isLoggedIn")
return mainVC!
}
private func parseAppLinks(from url: URL) -> String? {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else { return .none }
guard let urlString = components.queryItems?.first?.value else { return .none }
return urlString
}

Thread Error in Xcode w/ Swift When Launching New Screen

Once the user logs into my app, I set the window, set the rootViewController and makeKeyAndVisible and when I do I get a Thread 9: signal SIGABRT error. FWIW, I get a purple warning -[UIWindow initWithFrame:] must be used from main thread only when setting self.window = UIWindow(frame: UIScreen.main.bounds). See code below to see my setup.
The code dies with that error right here - self.window!.makeKeyAndVisible() in the launchWindow(aWindow: UIWindow?) function below in AppController.swift.
AppDelegate.swift
//
// AppDelegate.swift
import UIKit
import AWSCognitoAuth
import AWSSNS
import AWSCognitoIdentityProvider
import UserNotifications
import ESTabBarController_swift
import AWSMobileClient
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
var window: UIWindow?
var navigationController: UINavigationController?
var storyboard: UIStoryboard?
var loginViewController: LoginViewController?
var pool = AWSCognitoIdentityUserPool.init(forKey: "UserPool")
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
AppController.sharedInstance.enableCognitoClientWithAuthentication()
// setup logging
// pool.delegate = self
// AWSDDLog.sharedInstance.logLevel = .verbose
// let credentialsProvider = AWSCognitoCredentialsProvider(regionType: .USEast1,
// identityPoolId: Constants.APIKeys.AWSIdentityPoolID)
//
// // setup service configuration
// let serviceConfiguration = AWSServiceConfiguration(region: AWSRegionType.USEast1, credentialsProvider: credentialsProvider)
//
// // create pool configuration
// let poolConfiguration = AWSCognitoIdentityUserPoolConfiguration(clientId: Constants.APIKeys.AWSClientID,
// clientSecret: Constants.APIKeys.AWSSecret,
// poolId: Constants.APIKeys.AWSPoolID)
// AWSServiceManager.default().defaultServiceConfiguration = serviceConfiguration
// // initialize user pool client
// AWSCognitoIdentityUserPool.register(with: serviceConfiguration, userPoolConfiguration: poolConfiguration, forKey: "UserPool")
//
// pool.currentUser()?.getSession()
// fetch the user pool client we initialized in above step
//let pool = AWSCognitoIdentityUserPool(forKey: "UserPool")
let signedIn = AWSMobileClient.sharedInstance().isSignedIn
self.navigationController = UINavigationController()
if !signedIn {
navigationInit()
}
// } else {
// AppController.sharedInstance.showLoggedInStateAndReturn(true)
// }
//pool.delegate = self
self.window = UIWindow(frame: UIScreen.main.bounds)
AppController.sharedInstance.launchInWindow(aWindow: self.window)
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
navigationInit()
return true
}
func navigationInit() {
let loginViewController = LoginViewController()
self.navigationController!.pushViewController(loginViewController, animated: false)
}
//MARK: Push Notification
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
/// Attach the device token to the user defaults
var token = ""
for i in 0..<deviceToken.count {
token = token + String(format: "%02.2hhx", arguments: [deviceToken[i]])
}
print(token)
UserDefaults.standard.set(token, forKey: "deviceTokenForSNS")
/// Create a platform endpoint. In this case, the endpoint is a
/// device endpoint ARN
let sns = AWSSNS.default()
let request = AWSSNSCreatePlatformEndpointInput()
request?.token = token
request?.platformApplicationArn = Constants.APIKeys.AWSSSNSARN
sns.createPlatformEndpoint(request!).continueWith(executor: AWSExecutor.mainThread(), block: { (task: AWSTask!) -> AnyObject? in
if task.error != nil {
print("Error: \(String(describing: task.error))")
} else {
let createEndpointResponse = task.result! as AWSSNSCreateEndpointResponse
if let endpointArnForSNS = createEndpointResponse.endpointArn {
print("endpointArn: \(endpointArnForSNS)")
Settings.setPushArn(endpointArnForSNS)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "RegisteredForPush"), object: nil)
}
}
return nil
})
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
let visible = window?.visibleViewController()
if let data = userInfo["aps"] as? [AnyHashable: Any] {
if let route = data["route"] as? String {
switch route {
case "matchRequested":
var projectId = ""
if let project = data["projectId"] as? String {
projectId = project
}
var matchId = ""
if let match = data["matchId"] as? String {
matchId = match
}
let projectMatches = MatchRequestedViewController(withNotificationPayload: projectId, matchId: matchId)
visible?.navigationController?.pushViewController(projectMatches, animated: true)
case "projectDetails":
var projectId = ""
if let project = data["projectId"] as? String {
projectId = project
}
let projectMatches = ProjectDetailsViewController(withProject: TERMyProject(), orProjectId: projectId)
visible?.navigationController?.pushViewController(projectMatches, animated: true)
case "matched":
var projectId = ""
if let project = data["projectId"] as? String {
projectId = project
}
var matchId = ""
if let match = data["matchId"] as? String {
matchId = match
}
var originProject: TERMyProject = TERMyProject()
var matchedProject: TERMatchedProject = TERMatchedProject()
AppController.sharedInstance.AWSClient?.projectsGet(id:projectId).continueWith(block: { (task: AWSTask) -> Any? in
if let error = task.error {
print("Error: \(error)")
} else if let result = task.result {
if result is NSDictionary {
DispatchQueue.main.async {
let array = [result]
let parsedProject: [TERMyProject] = TERMyProject.parseFromAPI(array:array as! [NSDictionary])
for project in parsedProject {
originProject = project
}
// self.initialSetup()
}
AppController.sharedInstance.AWSClient?.projectsGet(id:matchId).continueWith(block: { (task: AWSTask) -> Any? in
if let error = task.error {
print("Error: \(error)")
} else if let result = task.result {
if result is NSDictionary {
DispatchQueue.main.async {
let array = [result]
let parsedProject: [TERMatchedProject] = TERMatchedProject.parseFromAPI(array:array as! [NSDictionary])
for project in parsedProject {
matchedProject = project
}
let projectMatches = MatchedViewController(withProject: originProject, match: matchedProject, isComplete: false)
visible?.navigationController?.pushViewController(projectMatches, animated: true)
}
}
}
return nil
})
}
}
return nil
})
default:
break
}
}
}
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print(error.localizedDescription)
}
// Called when a notification is delivered to a foreground app.
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("User Info = ",notification.request.content.userInfo)
completionHandler([.alert, .badge, .sound])
}
//MARK: Boiler-plate methods
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
extension AppDelegate: AWSCognitoIdentityInteractiveAuthenticationDelegate {
// func startPasswordAuthentication() -> AWSCognitoIdentityPasswordAuthentication {
// if (self.navigationController == nil) {
//
// self.navigationController = self.storyboard?.instantiateViewController(withIdentifier: "LoginViewController") as? UINavigationController
// }
//
// if (self.loginViewController == nil) {
// self.loginViewController = self.navigationController?.viewControllers[0] as? LoginViewController
// }
//
// DispatchQueue.main.async {
// self.navigationController!.popToRootViewController(animated: true)
// if (!self.navigationController!.isViewLoaded
// || self.navigationController!.view.window == nil) {
// self.window?.rootViewController?.present(self.navigationController!,
// animated: true,
// completion: nil)
// }
//
// }
// return self.loginViewController!
// }
}
// MARK:- AWSCognitoIdentityRememberDevice protocol delegate
extension AppDelegate: AWSCognitoIdentityRememberDevice {
func didCompleteStepWithError(_ error: Error?) {
}
func getRememberDevice(_ rememberDeviceCompletionSource: AWSTaskCompletionSource<NSNumber>) {
}
}
extension UIWindow {
func visibleViewController() -> UIViewController? {
if let rootViewController: UIViewController = self.rootViewController {
return UIWindow.getVisibleViewControllerFrom(vc: rootViewController)
}
return nil
}
class func getVisibleViewControllerFrom(vc:UIViewController) -> UIViewController {
switch(vc){
case is UINavigationController:
let navigationController = vc as! UINavigationController
return UIWindow.getVisibleViewControllerFrom( vc: navigationController.visibleViewController!)
break;
case is UITabBarController:
let tabBarController = vc as! UITabBarController
return UIWindow.getVisibleViewControllerFrom(vc: tabBarController.selectedViewController!)
break;
default:
if let presentedViewController = vc.presentedViewController {
//print(presentedViewController)
if let presentedViewController2 = presentedViewController.presentedViewController {
return UIWindow.getVisibleViewControllerFrom(vc: presentedViewController2)
}
else{
return vc;
}
}
else{
return vc;
}
break;
}
}
}
AppController.swift
func launchInWindow(aWindow: UIWindow?){
self.window = aWindow
self.initializeSDKs()
self.globalCustomization()
self.AWSUnAuthedClient.apiKey = Constants.APIKeys.AWSAPIKey
self.window!.rootViewController = self.showLoggedInStateAndReturn(true)
self.window!.makeKeyAndVisible()
}
func initializeSDKs() {
// Google places
GMSPlacesClient.provideAPIKey(Constants.APIKeys.GooglePlaces)
}
func globalCustomization() {
self.styleNavigationBar()
}
#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: "tabBar"), selectedImage: UIImage(named: "tabBar"))
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
}
}
You should try to execute the piece of code in the main thread:
func launchInWindow(aWindow: UIWindow?){
self.window = aWindow
self.initializeSDKs()
self.globalCustomization()
self.AWSUnAuthedClient.apiKey = Constants.APIKeys.AWSAPIKey
DispatchQueue.main.async {
self.window!.rootViewController = self.showLoggedInStateAndReturn(true)
self.window!.makeKeyAndVisible()
}
}
Also, your question codebase looks a little bit weird in this part in AppDelegate.swift:
self.window = UIWindow(frame: UIScreen.main.bounds) //-- Purple warning here
AppController.sharedInstance.launchInWindow(aWindow: self.window)
return true
Looks like those three lines are not in a function but in class scope, which is weird and you shouldn't be able to compile it. Probably you pasted your code wrong?

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

NavigationTitle blink

If app is killed and user click on push notification app takes it directly to the respective screen(detail screen) but before pushing to the detail screen we push to the list screen.When user click on back button of detail screen navigation title of list screen comes empty for a sec then gets correctly.What could be the reason.
switch(model!.action[1].lowercased()){
case "profile":
NotificationUtils.sharedInstance.moveToDirectoryModuleWithLTDS() //Will shift to Directory module
NotificationUtils.sharedInstance.moveToLocationList(false)
NotificationUtils.sharedInstance.moveToLocation(Guid(locID),rightPanel: false, needAnimation: false) //Move to department detail screen with Info tab selected
hideLoader() //Will stop loading indicator
break
}
func moveToLocationList(_ animate: Bool = true) {
hideNavigationTitle() //This function is used to clear the back button title.
let dropVC = DepartmentViewController(nibName: "DepartmentViewController", bundle: nil)
dropVC.employeeGroupInfo = EmployeeGroupInfo.locationInfo
self.pushWithDelayToVC(dropVC, animation: animate)
}
func pushWithDelayToVC(_ viewController:UIViewController,animation:Bool = true)
{
let appDelegate = UIApplication.shared.delegate as! AppDelegate
AppDelegate.removeUserProfileSetupScreens()
self.removeComposer { () -> Void in
UIUtils.pushViewWhenHideBottom((appDelegate.tabBarController.selectedViewController as! UINavigationController).visibleViewController!, anotherVC: viewController, needAnimation: animation)
self.hideSidePanel()
}
}
func removeComposer(_ completion: #escaping (() -> Void))
{
let appDelegate = UIApplication.shared.delegate as! AppDelegate
if let selectedController = appDelegate.tabBarController.selectedViewController
{
if let selectNavController = selectedController as? UINavigationController
{
if let visibleController = selectNavController.visibleViewController
{
if (NSStringFromClass(type(of: visibleController)) == "MFMailComposeInternalViewController" || NSStringFromClass(type(of: visibleController)) == "CKSMSComposeController" || NSStringFromClass(type(of: visibleController)) == "PLUICameraViewController" || NSStringFromClass(type(of: visibleController)) == "UIAlertController")
{
visibleController.dismiss(animated: false, completion: { () -> Void in
completion()
return
})
}
else
{
completion()
}
}
else { completion() }
}
else { completion()}
}
else { completion() }
}
And code of viewWillAppear is :
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if employeeGroupInfo == EmployeeGroupInfo.departmentInfo {
UIUtils.setTitleFontWhiteOnNavigationController(viewCtrl: self, titleStr: AppMessage.EDByDepartment)
} else if employeeGroupInfo == EmployeeGroupInfo.locationInfo {
UIUtils.setTitleFontWhiteOnNavigationController(viewCtrl: self, titleStr: AppMessage.EDByLocation)
} else if employeeGroupInfo == EmployeeGroupInfo.teamInfo {
UIUtils.setTitleFontWhiteOnNavigationController(viewCtrl: self, titleStr: AppMessage.EDMoreByTeamsMob)
}
self.tabBarController?.tabBar.isHidden = false
}
public class func setTitleFontWhiteOnNavigationController(viewCtrl: UIViewController, titleStr: String, needBorder: Bool = true,needTitleImage: Bool = false)
{
viewCtrl.extendedLayoutIncludesOpaqueBars = true
viewCtrl.navigationController?.navigationBar.isTranslucent = false
viewCtrl.navigationController?.navigationBar.barTintColor = UIColor.LTColor()
// Set navigation bar background color
viewCtrl.navigationController?.navigationBar.tintColor = UIColor.white
viewCtrl.navigationController?.navigationBar.titleTextAttributes = [NSFontAttributeName: UIFont.boldSystemFont(ofSize: 18.0),NSForegroundColorAttributeName: UIColor.white]
if needTitleImage {
setImageWithTitle(viewCtrl: viewCtrl,titleStr: titleStr)
}
else {
viewCtrl.title = titleStr
}
if needBorder
{
//Used to hide shadow line in navigation bar by Nikhil
viewCtrl.navigationController?.navigationBar.setBackgroundImage(nil, for: UIBarMetrics.default)
viewCtrl.navigationController?.navigationBar.shadowImage = nil
}
else
{
//Used to hide shadow line in navigation bar by Nikhil
viewCtrl.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
viewCtrl.navigationController?.navigationBar.shadowImage = UIImage()
}
}

Resources