Assigning values to properties in a singleton implemented with static let in Swift - swift

From reading it seems like the best advice for creating a singleton in Swift is to use static let like this:
struct GameManager {
static let defaultManager = GameManager()
var gameScore = 0
var saveState = 0
private init() {}
}
GameManager.defaultManager.gameScore = 1024 // Error
GameManager.defaultManager.saveState = 12 // Error
let score = GameManager.defaultManager.gameScore
let savedProgress = GameManager.defaultManager.saveState
Because the defaultManager is declared as a constant (with "let"), there is an error when I try to assign gameScore and saveState.
I'm running Xcode 7.0 beta 6 (7A192o) with Swift 2.0 (swiftlang-700.0.57.3 clang-700.0.72).
If I change the defaultManager to be declared as a variable (with "var"), will it no longer be considered a proper singleton?
If I change the GameManager to be declared as a class instead of a structure, then the code works as expected.
class GameManager {
static let defaultManager = GameManager()
var gameScore = 0
var saveState = 0
private init() {}
}
GameManager.defaultManager.gameScore = 1024 // No error, why?
GameManager.defaultManager.saveState = 12 // No error, why?
let score = GameManager.defaultManager.gameScore // 1,024
let savedProgress = GameManager.defaultManager.saveState // 12
Can you explain why the reference-type class is better for implementing the singleton than the value-type structure?

You can't really change it because on your struct you have a let constant named defaultManager. As you know already a struct is a copy type while a class is a pass by reference one. if you want to use it as a struct you will have to replace that let with var instead. Another take of this problem is to change the struct to a class that way you will be able to change the defaultManager value although it is declared as a let.
cheers
edit: The main difference in your code is that when you have a constant in a class and you change that constant you are actually referring to its address rather than its actual value.Same doesn't happen with a struct cause as a value type you can't do that call by reference thingy you do with class and so you are forced to have a constant(let) value that cannot be mutated. Change the let to var inside your struct and see the magic happen

Related

Default Memberwise Initializers for Structure Types with private(set)

Why does Swift 4 allow us to modify private(set) variables in the "Default Memberwise Initializer"? Is that a bug or a feature? And more important, what is the easiest way to avoid this and keep the same functionality of private(set)?
Example Code:
import Foundation
struct MyStruct {
private(set) var counter = 0
}
let myStruct = MyStruct(counter: 5) //Should not be allowed in my opinion
print(myStruct.counter) //Returns 5
The rules are different at the moment of initialization than at any other time, because it is necessary to initialize all properties as part of initialization. That is why even a let property without a value can be given a value in the initializer; that is the only time a let constant can be assigned to by other code. This is therefore legal:
struct MyStruct {
let counter : Int
init(counter:Int) { self.counter = counter } // ok to assign to `let`
}
Keep in mind that the supplied memberwise initializer is just an initializer like any other. You could have written it explicitly. Suppose you had written this:
struct MyStruct {
private(set) var counter : Int
init(counter:Int) { self.counter = counter }
}
let myStruct = MyStruct(counter: 5)
You do not deny, I suppose, that that makes sense? There is now no default initialization in the counter declaration, so the only way to give it an initial value would be the initializer. And this would also be necessary, since we must initialize all properties as part of object initialization.
Well, your code is not appreciably different. You do provide default initialization, but the memberwise initializer is still an initializer and is permitted to give the property a value.
If your goal was to make it impossible to change the value of counter at all after its initial value was provided, then it should have been a let constant, not a var variable.
If your goal was to make it legal to initializer MyStruct with or without an explicit counter, then you could have written the same thing this way:
struct MyStruct {
private(set) var counter : Int
init(counter:Int = 0) { self.counter = counter }
}

Creating a global variable in swift

I am trying to create a global variable from my view controller (inside the class but outside the functions) as follows:
var modelData: CustomTabBarController.model // THE ERROR IS HERE
This is how that class is defined:
CustomTabBarController.swift:
import UIKit
// This class holds the data for my model.
class ModelData {
var name = "Fred"
var age = 50
}
class CustomTabBarController: UITabBarController {
// Instantiate the one copy of the model data that will be accessed
// by all of the tabs.
var model = ModelData()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
However I am getting the following error:
"'model' is not a member type of "CustomTabBarController"
How do I declare it so that I can access model? Thanks.
Update #1
Sorry I forgot to mention this:
I need the model data to be the SAME in every tab of the tabbar. For example if I change the age to 51 in the first tab, the second tabbar should retrieve 51. Which would be the correct method above to use it this way?
Update #2
I am able to create the variable inside a function with dean's suggestion:
func setupModelData()
{
var modelData = (self.tabBarController as! CustomTabBarController).model
}
However this does not work, since I need to access the modelData from other functions. When I attempt to move this line outside of the function as follows:
import UIKit
class FirstViewController: UIViewController, UITableViewDelegate, UITableViewDataSource
{
var modelData = (self.tabBarController as! CustomTabBarController).model
...
I receive the error:
Value of type '(NSObject) -> () -> FirstViewController' has no member 'tabBarController'
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
I'm not sure whether you truly want a global variable (i.e. a single instance of ModelData shared between all your view controllers) or an instance variable which is public, so I'll try to answer both :)
1) global model
This line attempts to get the model property from the CustomerTabBarController class - i.e. if you made multiple tab bar controllers they would all use the same model.
var modelData: CustomTabBarController.model
If this is what you want, then you need to change this line to include the static keyword.
static var model = ModelData()
However, this almost certainly isn't what you're after.
2) shared instance variable
This means that the model is part of each instance of CustomTabBarController. Here, you would need to change the line which is throwing the error to be something like this:
var modelData: myCustomTabBarController.model
Without knowing more about your architecture, I can't help you get hold of your tab bar controller instance, but something like this might work (inside other view controllers):
var modelData = (self.tabBarController as! CustomTabBarController).model
model is an instance variable.
Either create an instance of CustomTabBarController
var modelData = CustomTabBarController().model
Or declare model as class variable
static var model = ModelData()
...
var modelData = CustomTabBarController.model
However to use ModelData as a single global variable with the two members, use a struct rather than a class and declare the members as static.
struct ModelData {
static var name = "Fred"
static var age = 50
}
You can access the name from everywhere for example
let name = ModelData.name
and there is no need to create an extra variable in another class.
An – instance based –  alternative is a singleton
struct ModelData {
static let shared = ModelData()
var name = "Fred"
var age = 50
}
and use it
let name = ModelData.shared.name

Swift - Declare nested variable names using dot

I'll keep it short. I'm trying to accomplish the following:
class Media {
var likes.count : Int? = 0
}
Obviously the complier throws me an error:
Consecutive declarations on a line must be separated by ';'
Is there a way to work around this? I know that i can eventually do some kind of String Replace using Mirror(reflecting:object) but i'd like to keep it efficient. Would love any help. Thanks.
UPDATE:
I wasn't clear enough, sorry. The issue is that the complier won't let me use . inside the variable declaration name.
The issue is that the complier won't let me use . inside the variable declaration name.
Exactly, a property name in Swift cannot contain the . character.
A possible approach
Now, if you want to be able to write something like this
let media = Media()
media.likes.count = 1
then you need to define your class like shown below
class Media {
class Likes {
var count = 0
}
var likes = Likes()
}
or
class Likes {
var count = 0
}
class Media {
var likes = Likes()
}
A few suggestions
PLEASE don't use implicitly unwrapped optionals like this one
var likes.count : Int! = 0
They are like a gun ready to fire and crash your entire app!
And finally the class keyword begins with a lowercase character: class not Class.
I recommend using a Struct. A Struct is basically the same as a class that is referenced by value. So you can have as many Structs as you want with their own nested variables and functions, just like a class! And the best part is, you never have to use the Struct as a functional piece of code, just as something to namespace your variables in. I do this frequently with a Constants swift file.
struct Constants {
struct MainMenu {
static var height:CGFloat = 420
static var width:CGFloat = 240
static var backgroundColor:UIColor = .red
}
struct MainViewController {
static var toolBarHeight:CGFloat = 49
static var backgroundColor:UIColor = .blue
}
}
Usage:
func getRemainingHeight() ->CGFloat {
let viewHeight = self.view.bounds.size.height
let menuHeight = Constants.MainMenu.height
let toolBarHeight = Constants.MainViewController.toolBarHeight
return viewHeight - (menuHeight + toolBarHeight)
}

What is the use of "static" keyword if "let" keyword used to define constants/immutables in swift?

I'm little bit confused about using static keyword in swift. As we know swift introduces let keyword to declare immutable objects. Like declaring the id of a table view cell which most likely won't change during its lifetime. Now what is the use of static keyword in some declaration of struct like:
struct classConstants
{
static let test = "test"
static var totalCount = 0
}
whereas let keyword do the same.In Objective C we used static to declare some constant like
static NSString *cellIdentifier=#"cellId";
Besides which makes me more curious is the use of static keyword along with let and also var keyword. Can anybody explain me where to use this static keyword? More importantly do we really need static in swift?
I will break them down for you:
var : used to create a variable
let : used to create a constant
static : used to create type properties with either let or var. These are shared between all objects of a class.
Now you can combine to get the desired out come:
static let key = "API_KEY" : type property that is constant
static var cnt = 0 : type property that is a variable
let id = 0 : constant (can be assigned only once, but can be assigned at run time)
var price = 0 : variable
So to sum everything up var and let define mutability while static and lack of define scope. You might use static var to keep track of how many instances you have created, while you might want to use just varfor a price that is different from object to object. Hope this clears things up a bit.
Example Code:
class MyClass{
static let typeProperty = "API_KEY"
static var instancesOfMyClass = 0
var price = 9.99
let id = 5
}
let obj = MyClass()
obj.price // 9.99
obj.id // 5
MyClass.typeProperty // "API_KEY"
MyClass.instancesOfMyClass // 0
A static variable is shared through all instances of a class. Throw this example in playground:
class Vehicle {
var car = "Lexus"
static var suv = "Jeep"
}
// changing nonstatic variable
Vehicle().car // Lexus
Vehicle().car = "Mercedes"
Vehicle().car // Lexus
// changing static variable
Vehicle.suv // Jeep
Vehicle.suv = "Hummer"
Vehicle.suv // Hummer
When you change the variable for the static property, that property is now changed in all future instances.
Static Variables are belong to a type rather than to instance of class. You can access the static variable by using the full name of the type.
Code:
class IOS {
var iosStoredTypeProperty = "iOS Developer"
static var swiftStoredTypeProperty = "Swift Developer"
}
//Access the iosStoredTypeProperty by way of creating instance of IOS Class
let iOSObj = IOS()
print(iOSObj.iosStoredTypeProperty) // iOS Developer
//print(iOSObj.swiftStoredTypeProperty)
//Xcode shows the error
//"static member 'swiftStoredTypeProperty' cannot be used on instance of type IOS”
//You can access the static property by using full name of the type
print(IOS.swiftStoredTypeProperty) // Swift Developer
Hope this helps you..
to see the difference between type properties and / or methods and class properties and / or methods, please look at this self explanatory example from apple docs
class SomeClass {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 27
}
class var overrideableComputedTypeProperty: Int {
return 107
}
}
Static properties may only be declared on type, not globally. In other words static property === type property in Swift. To declare type property you have to use static keyword.
"The let keyword defines a constant" is confusing for beginners who are coming from C# background (like me). In C# terms, you can think of "let" as "readonly" variable.
(answer to How exactly does the “let” keyword work in Swift?)
Use both static and let to define constant
public static let pi = 3.1416 // swift
public const double pi = 3.1416; // C#
public static final double pi = 3.1416 // Java
Whenever I use let to define constant, it feels like I am using readonly of C#. So, I use both static and let to define constant in swift.
Let me explain it for those who need Objective-C reference.
Hope you all remember that we were using a constant file in our Objective-C project to keep all the static API keys like below.
/ In your *.m file
static NSString * const kNSStringConst = #"const value";
Here the keyword Static does not mean that the kNSStringConst is a constant, it just defines that the kNSStringConst can be accessed globally. They keyword const makes it constant.
Now let's move to Swift.
In Swift, Static let and Static var are considered as Type Properties, which means that they can be accessed by their type.
For Example:
class World {
static let largestPopulation = "China"
static var secondLargestPopulation = "India"
}
World.largestPopulation = "German" // Cannot assign to property: 'largestPopulation' is a 'let' constant
World.secondLargestPopulation = "UK"
World.secondLargestPopulation // UK
In this example, two properties have static keyword, one is constant and another one is variable.
As you can see, the constant Static let declared can be accessed by it's type, but cannot be
changed.
The Static var declared can not only be accessed by it's type but also can be modified. Consequently, all the future instances will be changed.
[Swift's property]
[var vs let]
[class vs static]
In a nutshell class and static are type property and belongs to Type in a single copy

How can I access a constant outside a class in swift?

class myClass {
let x = 0
}
How can I access the x constant outside myClass?
x is not defined as a constant, but as a mutable instance property. In order to make it immutable (which is technically different than constant, but the result doesn't change) you have to use let.
That said, if it's an instance property, you need a class instance, because the property is created as part of the class instantiation:
let instance = MyClass()
instance.x
If you want to make it a static property, accessible with the type and not with an instance of it, you should declare it as static:
class MyClass {
static let x = 0
}
However static stored properties are available in swift 1.2 only.
For previous versions you can either use a computed property:
class var x: Int { return 0 }
or turn the class into a struct:
struct MyClass {
static let x = 0
}
An alternative solution is to use a nested struct:
class MyClass {
struct Static{
static let x = 0
}
}
MyClass.Static.x
You could declare it as static var, like this:
class MyClass {
static var x = 0
}
and then you can access it outside the class with MyClass.x. If you declare it "class var", it will give you error message "Class stored properties not yet supported in classes; did you mean 'static'?", so they probably will be part of the language later. At this moment, you should do computed properties for class:
class MyClass {
static var x: Int {
return 3
}
} // This actually makes no sense to be a computed property though
You can find some info about Type Properties at: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html