Declaring Global Variables in Swift

How to create a global variable?

From the official Swift programming guide:

Global variables are variables that are defined outside of any
function, method, closure, or type context. Global constants and
variables are always computed lazily.

You can define it in any file and can access it in current module anywhere.
So you can define it somewhere in the file outside of any scope. There is no need for static and all global variables are computed lazily.

 var yourVariable = "someString"

You can access this from anywhere in the current module.

However you should avoid this as Global variables are not good for application state and mainly reason of bugs.

As shown in this answer, in Swift you can encapsulate them in struct and can access anywhere.
You can define static variables or constant in Swift also. Encapsulate in struct

struct MyVariables {
static var yourVariable = "someString"
}

You can use this variable in any class or anywhere

let string = MyVariables.yourVariable
println("Global variable:\(string)")

//Changing value of it
MyVariables.yourVariable = "anotherString"

Swift Global Variables

You shouldn't use global variables, I don't think that's recommended in any language.

Now here you have what looks like a Singleton class (BroadService), that's good because it's a nice solution for what you're looking for.

Next all you need to do is add a property to that class. Let's say videoLink is a string, you can add a string property to BroadService, for example storedVideoLink as an optional String, and the next time you need to obtain that value after you have already fetched it, you can access it like so: BroadService.sharedInstance.storedVideoLink.

One more thing, to have BroadService work properly as a singleton, you should make its init private.

To sum up, here's what I'm suggesting:

class BroadService {
static let sharedInstance = BroadService()
var storedVideoLink: String?

private init() {} // to ensure only this class can init itself

func fetchBroadcasts(completion: @escaping ([Games]?) -> ()) {
// your code here
}
}

// somewhere else in your code:
BroadService.sharedInstance.fetchBroadcasts { (games) in
if let games = games {
let game = games[indexPath]
let videoLink = game.videoLink
BroadService.sharedInstance.storedVideoLink = videoLink
}
}
// now you can access it from anywhere as
// BroadService.sharedInstance.storedVideoLink

This way it all stays cohesive in the same class. You can even add a getter method for storedVideoLink so you don't have to access it directly, and in this method you could state that if the string is nil then you fetch the data, store the link to the string, and then return the string.

Creating a global variable in swift

I ended up following holex's suggestion of creating a shared class (singleton):

import UIKit
class ModelData: NSObject {
static let shared: ModelData = ModelData()
var name = "Fred"
var age = 50
}

Writing in first view: Set age to 51:

ModelData.shared.age = 51

Reading in second view: Get age of 51

let age = ModelData.shared.age

Is it good practice to use global variables and constants? (Swift 5)

You can use a static variable of a struct to do this more elegant:

struct Repository {
static var googleUser: String?
}

and access/set googleUser using the struct type:

Repository.googleUser = user

Or even better, use a singleton class like this:

class Repository {
static let shared = Repository()
var googleUser: String?
}

then you can use it directly:

Repository.shared.googleUser = user

or inject the singleton instance to whichever class want to use googleUser.

Declaring Global Variables in Swift

From the Swift Programming Language -

Global variables are variables that are defined outside of any
function, method, closure, or type context

So you can simply declare your variable at the top of any file straight after the import statements.

However, I would suggest you seriously reconsider. Generally globals aren't a good idea. You are better off with properties on a singleton or using dependency injection.

Your second question "where would I instantiate the array?" is part of the reason why globals are bad - their lifecycle isn't well defined in terms of your other objects. A singleton that is initialised on first use eliminates this issue.

How use global Variables in views and classes?

Regarding your (second) question in your comment,
here is some code that shows an approach to "send" a value from a
class (does not have to be a view) and receive that value in a View.

You could use this approach in your "ts28bControllerDelegate",
to send the temperature updates as they arrive in your ts28bControllerDelegate and
receive them in your views using .onReceive(...)

public func controller(_ controller: TS28BController?, device: TS28B?, didUpdateTemperature value: Float, temperatureUnit unit: TemperatureUnit, measure date: Date?, measureLocation type: TemperatureType) {
print("Temperature UNIDAD:", unit)
print("Temperature:", value)
let valueFinal = ((value - 32) * 5/9)
let stringFloat = String(valueFinal)
// send a message with the temp value
NotificationCenter.default.post(name: GlobalTs28b.globalMsg, object: stringFloat)
}

class GlobalTs28b: ObservableObject {
@Published var temp: String = "----"
@Published var state: String = "----"

// for testing, does not have to be in this class
static let globalMsg = Notification.Name("GlobalTs28b")
}

struct ContentView: View {
@StateObject var global = GlobalTs28b() // for testing

var body: some View {
NavigationView {
VStack (spacing: 55) {
NavigationLink("go to next view", destination: ViewTest2())
Text(global.temp).foregroundColor(.red)
}
.onReceive(NotificationCenter.default.publisher(for: GlobalTs28b.globalMsg)) { msg in
if let temp = msg.object as? String {
// update the StateObject with the new info, so the view will update
global.temp = temp
}
}
}.navigationViewStyle(.stack)
}
}

struct ViewTest2: View {
var body: some View {
VStack {
Button(action: {
// send a message to all listeners with the new temp,
// could be used in any class such as ts28bControllerDelegate
NotificationCenter.default.post(name: GlobalTs28b.globalMsg, object: "new temp")
}) {
Text("click to update temp")
}
}
}
}

Why global variable in swift file doesn't look like initialized correctly at debugging?

Note that global variables and static properties are computed lazily (see the Language Guide on this):

Global constants and variables are always computed lazily, in a similar manner to Lazy Stored Properties. Unlike lazy stored properties, global constants and variables don’t need to be marked with the lazy modifier.

Local constants and variables are never computed lazily.

When the breakpoint is hit, "Line 1" is not run yet, so the global constant tab0 is no yet computed, and so its value is all zeros. When interpreted as a String, that represents the empty string.

You can verify this by adding an initialiser that prints something:

struct NavTab {
let name: String

init(name: String) {
self.name = name
print("init!")
}
}

@main
struct testApp: App {
var body: some Scene {
print("ContentView!")
return WindowGroup {
ContentView()
}
}
}

and see that it is not called until after the print("tab0.name is \(tab0.name)") line. Output:

ContentView!
init!
tab0.name is tab0

But that's only half of the picture. Why doesn't evaluating tab0.name in LLDB call the getter of tab0 and cause it to be initialised?

I didn't find any documentation for this, but I suspect that this is a quirk of LLDB. I suspect that when you do po tab0.name, LLDB directly accesses the area of memory where tab0 is stored, rather than call its getter (as an optimisation perhaps?). It knows where tab0 is because you are currently in the same file as where tab0 is declared. If tab0 were declared in another file, then po tab0.name would cause tab0 to be initialised as you would expect:

(lldb) po tab0.name
init!
"tab0"

which is why I suspect that this is just a quirk of LLDB.

Declaring a global variable in swift while using UITableView

As the other posters have mention using global variables is not the best idea. It's not good coding practice, and can become very confusing once your app scales. When passing information between a TableView and another View (a ViewController, a Cell etc.) you have two options (not including using global variables).

First option: Use prepareForSegue

On your destination ViewController you need to create a variable called chosenFeed. Then at the bottom of your TableView you can use this prepareForSegue function to pass the data:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 
if let destinationVC = segue.destinationViewController as ViewController {
destinationVC.chosenFeed = self.chosenFeed
}
}

OR You could use didSelectRowAtIndexPath

If this data needs to be sent and is based on which cell is selected you can use didSelectRowAtIndexPath. You still need to add on your destination ViewController a variable called chosenFeed. If you choose this option you also need to name your segue in the Storyboard.

 override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

// Youll need the name of your ViewController here to pass the variable
let destinationVC = "nameOfTheDestination_VC()"
destinationVC.chosenFeed = self.chosenFeed

//This is where you use the named segue from the Storyboard
destinationVC.performSegueWithIdentifier("segueName", sender: self)

}

Either of those suggestions should be what you're looking for. If after all that, you still decide you need a global variable (after all this was the question asked) you just need to declare the variable above the class declaration:

var chosenFeed : String? 
class About_VC: UIViewController {
//code here
}

One last note, from the example pictures you posted be careful with force unwrapping. Cheers!

Is there a way to have a global variable between a SwiftUI view and classes indirectly related to it?

To make changes in a SwiftUI view, as it is a static element because of its struct declaration, your object GlobalString must conform to a StateObject to be able to make changes in real-time to a view, and its variables must conform to @Published.

class GlobalString: ObservableObject {
@Published var stringy = ""
@Published var temperature = 0.0
}

struct ContentView: View {

@StateObject var globalString = GlobalString()

var body: some View {
VStack {
Text(globalString.stringy)
.frame(maxWidth: .infinity, maxHeight: .infinity)
}.onAppear {
k.start()
}
}
}


Related Topics



Leave a reply



Submit