Swift singleton or global variable - or something else? - ios

I am programming a board game. There are a couple of screen and many functions. I often need to change some variable like "money" or "wood".
I added "didset" so I can update a View that displays the amount of money.
I see two options for this. Either a global variable
var money = 0 {didSet {NotificationCenter.default.post(name: NSNotification.Name(rawValue: "showMoney"), object: nil)}}
or a singleton
class resources {
static let shared = resources()
var money = 0 {didSet {NotificationCenter.default.post(name: NSNotification.Name(rawValue: "ResourcenAnzeigen"), object: nil)}}
private init() {}
}
Now I read singletons are always preferred instead of globals. But I wonder why. In my example both seem to do the same. The only difference is I either have to write
money += 1
or
resources.shared.money += 1
The first one looks easier.
And is there a third better way? I read one could give over the needed variables to every function or viewcontroller - but that looks to me like much unneccessary extra code?

Actually a Singleton is a special case of a global variable.
Accessing the global Singleton instance is just shadowed.
This helps to keep the global namespace cleaner.
But a Singleton SHOULD NOT be used for global accesses.
In iOS Singletons are used for unique resources like file system, network, user data, etc.
I suggest you to create an instance at the root of your application and inject this where you need it.
This makes also testing easier and keeps your code clean.
There you can also use the observer pattern to avoid the global NotificationCenter.
FYI: Communication Patterns

Singleton would be the right way to do.
The main problem with using a global variable instead of a singleton for these purposes would be that in case you have a local variable money in some other entity, the compiler would not be implicitly able to identify which money you were referring to.
So, in case you decide to add money as a variable at some other location, and you try to update this global money variable inside the same, it would end up modifying your local variable, and would not update your global one.
But, when you are using a singleton to set or get such a property, you would be safe from this situation.
var money = 0
class MyClass {
var money = 0
func doSomethingAndIncrementGlobalMoney() {
money += 1
}
}
print(money) // Prints 0
let myObject = MyClass()
myObject.doSomethingAndIncrementGlobalMoney()
print(money) // Prints 0, while the expectation was to increment the global 'money' by 1.
Or, you could just go with using something like:
struct MyGlobalItems {
static var money: Int = 0
}
// Using the variable:
MyGlobalItems.money += 1

Related

What are the disadvantages of passing data between ViewControllers using Global Variables?

I am new to Swift, coming from C and Java background.
I am trying to pass a struct (called 'Player') object from one view controller to another, and as i learned one proper way to do is first defining it in the target VC like:
class TargetVC: UIViewController {
var playerVar: Player!
}
And then assigning it in prepare function inside source VC:
if let targetVC = segue.destination as? TargetVC
{
targetVC.playerVar = myPlayer
}
But i thinked about simply using a global variable to pass the Player object to the target view controller by defining it like:
var myPlayer = Player() // Global Variable
class SourceVC: UIViewController {
}
Then reaching it in target VC:
class TargetVC: UIViewController {
myPlayer.name = // Stuff
}
Would this method cause any problems in runtime or does it have any disadvantages? If yes, what are them?
Thanks in advance.
Passing data gives to you to conform programming principles to build a good classes, even in your example. It makes classes more extendable and reusable in many cases.
Global var gives some advantages to reach needed data, but you have to care about reference cycles, memory managing, handle threads, queues and blocks, etc.
You can also read out about singletones, static vars,... – where you can get some data from class scope vars.
One disadvantage is, as the name suggests, everything has access to global variables, even things that have no reason to see, much less modify, that data.

In Swift what's the difference between a singleton and a global let variable? [duplicate]

What is the difference between global variable and shared instance in Swift? what are their respective field of use? Could anyone clarify their concept based upon Swift.
A global variable is a variable that is declared at the top level in a file. So if we had a class called Bar, you could store a reference to an instance of Bar in a global variable like this:
var bar = Bar()
You would then be able to access the instance from anywhere, like this:
bar
bar.foo()
A shared instance, or singleton, looks like this:
class Bar {
static var shared = Bar()
private init() {}
func foo() {}
}
Then you can access the shared instance, still from anywhere in the module, like this:
Bar.shared
Bar.shared.foo()
However, one of the most important differences between the two (apart from the fact that global variables are just generally discouraged) is that the singleton pattern restricts you from creating other instances of Bar. In the first example, you could just create more global variables:
var bar2 = Bar()
var bar3 = Bar()
However, using a singleton (shared instance), the initialiser is private, so trying to do this...
var baaar = Bar()
...results in this:
'Bar' initializer is inaccessible due to 'private' protection level
That's a good thing, because the point of a singleton is that there is a single shared instance. Now the only way you can access an instance of Bar is through Bar.shared. It's important to remember to add the private init() in the class, and not add any other initialisers, though, or that won't any longer be enforced.
If you want more information about this, there's a great article by KrakenDev here.
Singleton (sharing instance)
Ensure that only one instance of a singleton object is created & It's provide a globally accessible through shared instance of an object that could be shared even across an app.
The dispatch_once function, which executes a block once and only once for the lifetime of an app.
Global variable
Apple documentation says Global variables are variables that are defined outside of any function, method, closure, or type context.

Is it common/ok to declare global class instances in Swift/iOS

I recently watched a video tutorial in Lynda.com and I noticed that the author declared a few global variables which included two class instances. The main reason for my question is because I have heard that we need to try to avoid using global variables unless your really need them. I can see using global variables for NSUserDefaults' Keys but I'm not sure about global instances.
Here is the code showing the global variables declarations. I'm only concerned about variables masterView and detailViewController.
import UIKit
var objects:[String] = [String]()
var currentIndex:Int = 0
var masterView:MasterViewController?
var detailViewController:DetailViewController?
let kNotes:String = "notes"
let BLANK_NOTE:String = "(New Note)"
class MasterViewController: UITableViewController {
// class code
}
Is it ok to declare your class instances as global variables?
It is certainly OK to use global variables in your Swift code from the technical point of view, in the sense that your program is not going to crash or otherwise "misbehave" because of that. When you are building a quick example for a demonstration, global variables provide a convenient way to shorten the code.
However, there are drawbacks to using mutable global variables that makes their use questionable. Specifically, they break encapsulation. Looking at the code that you provided, two variables
var objects:[String] = [String]()
var currentIndex:Int = 0
should be hidden in a model object, over which you have some degree of control.
Using globals may produce some unexpected behavior if you are not careful about clearing them. For example
var masterView:MasterViewController?
var detailViewController:DetailViewController?
may retain references to view controllers that are no longer visible.
That's okay to declare a class instances as a global variable, if you want to prefer use of only one instance of that class through out your app work.
NOTE: you can hide those properties & functions of that instance by making those Private, which you don't want to be accessed by global instance variable.

iOS Swift storing simple data globally

New to iOS development and trying to develop my app with correct practices. My main issue is trying to figure out the best way to store a bunch of simple data globally.
The app connects to a computer adhoc wifi and gathers information about the current state. (ex// gpu temp, computer name, display resolution, date/time, etc). The various view controllers I have then allow the user to adjust some of these settings so I want to be able to keep track of these changes across all view controllers.
I have been wrestling between using a plist, core data, or singleton class to store all the information. Since the user will be connected to one computer for the life of the app I am leaning towards singleton since it seems easiest to get and set data. However, I have read that using a singleton is really not the best practice for various reasons. I have read up on plists and core data a little and seems like a decent amount of work just to get and set values. Should I spend the time using those or would some other method be a better way to accomplish all of this.
Update:
After both answers from Aaoli and Swift Rabbit. I wanted to clarify for my use case that the data I am storing did not need to be "saved" when the app was closed. So if you are coming to this question and need to store your data even if the app closes look at #SwiftRabbit's answer.
To use Global variable in Swift you need to define struct outside the class and because you don't need the variable to be swimming in the whole app memory without binding:
struct GlobalVar {
static var myvar = 0
}
To use simply the global variable from anywhere use :
GlobalVar.myvar = 10
In my case, using "simple" data, which is not sensitive, I usually use the user defaults. They can be accessed globally through the project. For example, it can be set this way :
var prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
prefs.setObject(myValue, forKey: "myKey") // myValue is of type typeOfValue
prefs.synchronize()
It can be accessed this way :
var prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
prefs.valueForKey("myKey") as? typeOfValue
Using setObject can be used for any kind of value, that's why I cast it when retrieving it from the userDefaults. You could actually use other methods to insert it and retrieve it that would make your task easier. For example, you could tell you are inserting a bool with :
prefs.setBool(<#value: Bool#>, forKey: <#String#>)
And specify you are retrieving one with :
prefs.boolForKey(<#defaultName: String#>)
All methods can be found in the apple documentation here
My opinion: create a singleton, e.g. in this way:
class MyClass {
/// The singleton instance
private static var singletonInstance: MyClass!
class var sharedInstance: MyClass! {
get {
singletonInstance = singletonInstance ?? MyClass()
return singletonInstance
}
}
private init() { }
}
Or in this way:
class MySingletonClass {
static let sharedInstance = MySingletonClass()
private init() {
}
Many people consider Singleton a poor design pattern, but in my opinion it is not. It is good to have a pattern which ensures that a piece of information is only available once. What is not good is to have global access to this Singleton from anywhere in the application. Some people counter-act this with "Dependency Injection", which basically means that your classes does not "pull" the information from the Singleton but the information is "pushed" into the class from (some other instance) outside. This promotes loose coupling and better test-ability. But this is an advanced topic which needs much more information. (Google "typhoon framework" for iOS if you are interested in this)

Cocoa dispatch_once per instance

How to use dispatch_once, so the given code is executed once, per instance lifetime.
Equivalent would be to have a property inside the object, and use it like this:
- (void)viewWillAppear:(..).. {
// ...
if (self.isDispatched == NO) {
// ...
self.isDispatched = YES;
}
}
But I don't want to use an extra property, but the dispatch_once_t or similar.
Your requirements can't be satisfied. dispatch_once can only be used on memory that's never been written to before, and you have no way to guarantee this with instance variables. Greg Parker says so, and he's the guy who would know.
Instead, use the code in your question, but wrap it in a #synchronized block.
If you really want to avoid adding instance variables, you could use a separate singleton to manage a mutable set. You'd have to register instances with it and remove them on deallocation. Make sure this helper class only holds weak references; see NSMapTable and NSMapTableWeakMemory.
For those of you who are curious and as an extra tip, for me this approach has been useful for this purpose:
class SomeVC : UIViewController {
private var token: dispatch_once_t = 0
public override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
dispatch_once(&token) { () -> Void in
self.doSomethingOnce()
}
}
}
By not declaring a static var it has the expected behaviour. That being said, this is definitely NOT RECOMMENDED for any serious project, since in the Docs (as your well said) it states:
The predicate must point to a variable stored in global or static scope. The result of using a predicate with automatic or dynamic storage (including Objective-C instance variables) is undefined.
If we don't want to run into any future weird bugs and undefined behaviour I would just stick to what Apple says. But it's still nice to play around with these things, isn't it? =)

Resources