2

In one of the SceneDelegate methods, I want to assign a value to a variable that is in another ViewController. How can i do this? Always getting nil. I tried different methods, but for some reason they do not work, I wanted to use the completion handler, but it seems I cannot do it in SceneDelegate methods

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
        
var viewController: ViewController?
   ...
   //This method works when I click on a cell
   func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
   if let url = URLContexts.first?.url {

      let oauthCompletion: DropboxOAuthCompletion = {
      if let authResult = $0 {
      switch authResult {
      case .success:
         self.dropboxManager.fetchFiles()
         UserDefaults.standard.set(true, forKey:"userAuthorizedInDropbox")
    
         //I want to assign a value here
         self.viewController?.someString = "Some Text"
    
         print("Success! User is logged into Dropbox.")
      case .cancel:
          print("Authorization flow was manually canceled by user!")
      case .error(_, let description):
          print("Error: \(String(describing: description))")
          }
      }
   }
   DropboxClientsManager.handleRedirectURL(url, completion: oauthCompletion)
  }
} 

...
class ViewController: UIViewController {
   ...
   //But it's still nil
   var someString: String?
   ...
   func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
   dropboxManager.openDropboxAutorization(controller: self)
   }
}

.....
class MainTabBarController: UITabBarController {
let viewController: ViewController = ViewController.loadFromStoryboard()
   override func viewDidLoad() {
        super.viewDidLoad()
    viewControllers = [
            generateVC(rootViewController: viewController, image:           nil, title: "Import")
            ...
        ]

private func generateVC(rootViewController: UIViewController, image: UIImage?, title: String) -> UIViewController {
        let navigationVC = UINavigationController(rootViewController: rootViewController)
        navigationVC.tabBarItem.image = image
        navigationVC.tabBarItem.title = NSLocalizedString(title, comment: "")
        rootViewController.navigationItem.title = NSLocalizedString(title, comment: "")
        return navigationVC
    }
   }
...
}
2
  • Show when and where you are allocating viewController: ViewController?. Commented Aug 18, 2020 at 14:22
  • I do this in the tab bar controller. I added code to question
    – Oranutachi
    Commented Aug 19, 2020 at 6:02

1 Answer 1

2

You can get access to your desired ViewController from your rootViewController in scene delegate. In the following example i am considering your desired view controller is in the first tab of your tab bar controller.

if let tabbarController = self.window?.rootViewController as? UITabBarController,
   let navigationController = tabbarController.viewControllers?.first as? UINavigationController,
   let desiredController = navigationController.viewControllers.first as? ViewController {
       desiredController.someString = "some value"
}

But in my opinion it is not a recommended approach as there could be many cases that may depend on the application's or navigation stack's state.

What you can do is add an observer in your desired ViewController and post a notification from the SceneDelegate like this:

NotificationCenter.default.post(name: Notification.Name(rawValue: "someeventname"), object: "some value")

You can add and listen to the observer like this:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        NotificationCenter.default.addObserver(self, selector: #selector(someEventNotificationReceived(notification:)), name: Notification.Name(rawValue: "someeventname"), object: nil)
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self, name: Notification.Name(rawValue: "someeventname"), object: nil)
    }

    @objc func someEventNotificationReceived(notification: Notification) {
        if let value = notification.object as? String {
            //handle the event here
        }
    }
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.