For Xcode 11 & Swift 5 + Storyboard + Dependency Injection Approach
Assuming you are using a storyboard this is a method I have devised.
Step 1:
Put an identifier on your tabBarController like I did in the image below.

Step 2:
In the scenedelegate.swift file (NOT appDelegate.swift), add the following code to the appropriate func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
method.
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).
self.window = self.window ?? UIWindow()//@JA- If this scene's self.window is nil then set a new UIWindow object to it.
//@Grab the storyboard and ensure that the tab bar controller is reinstantiated with the details below.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let tabBarController = storyboard.instantiateViewController(withIdentifier: "tabBarController") as! UITabBarController
for child in tabBarController.viewControllers ?? [] {
if let top = child as? StateControllerProtocol {
print("State Controller Passed To:")
print(child.title!)
top.setState(state: stateController)
}
}
self.window!.rootViewController = tabBarController //Set the rootViewController to our modified version with the StateController instances
self.window!.makeKeyAndVisible()
print("Finished scene setting code")
guard let _ = (scene as? UIWindowScene) else { return }
}
You will notice that the scenedelgate.swift file has a member variable; var window: UIWindow?
. This used to be part of appDelegate but was changed in xCode 11 and Swift 5 so a lot of similar answers and tutorials will be out of date.
The part in the code that says storyboard.instantiateViewController(withIdentifier:
you will want to add the name you used for the parameter. In my screenshot you will see I called it tabBarController.
To make this function work on any type of viewController without having to instantiate each one separately on an index, I've used a protocol strategy called StateControllerProtocol. We will be creating this next along with the StateController which will hold the global variables.
Step 3:
In stateController.swift or whatever you want to name this file, add the following code removing aspects that do not apply to your project.
import Foundation
struct tdfvars{
var lateBED:Double = 0.0
var acuteBED:Double = 0.0
var rbe:Double = 1.4
var t1half:Double = 1.5
var alphaBetaLate:Double = 3.0
var alphaBetaAcute:Double = 10.0
var totalDose:Double = 6000.00
var dosePerFraction:Double = 200.0
var numOfFractions:Double = 30
var totalTime:Double = 168
var ldrDose:Double = 8500.0
}
//@JA - Protocol that view controllers should have that defines that it should have a function to setState
protocol StateControllerProtocol {
func setState(state: StateController)
}
class StateController {
var tdfvariables:tdfvars = tdfvars()
}
The variables you want to share between views I recommend adding to the struct. I named mine tdfvariables but you will want to name this something relevant to your project. Note the protocol defined here as well. This is a protocol that will be added to each viewController as an extension that defines that there should be a function to set its stateController member variable (which we have not defined yet, but will in a later step).
Step 4:
In my case I have 2 views controlled by the tabBarController. StandardRegimenViewController and settingsViewController. This is the code you will want to add for your viewControllers.
import UIKit
class SettingsViewController: UIViewController {
var stateController: StateController?
override func viewDidLoad() {
super.viewDidLoad()
}
}
//@JA - This adds the stateController variable to the viewController
extension SettingsViewController: StateControllerProtocol {
func setState(state: StateController) {
self.stateController = state
}
}
The extension here adds the protocol to your class and adds the function as required by it that we defined earlier in the stateController.swift file. This is what will eventually get the stateController and it's struct values into your viewController.
Step 5:
Use the stateController to get access to your variables! You are done!
Here is some examples of how I did this in one of my controllers.
stateController?.tdfvariables.lateBED = 100
You can read the variables the same way! The advantage of this approach is you are NOT using Singletons and instead Dependency Injection for your viewControllers and anything else that may need access to your variables. Read more about dependency injection to see the benefits vs singletons to learn more.