How to access class constants and variables - ios

Why can I not perform operations with a variable or constant at the class level? Is this not allowed or is there a keyword I need to use to access them? Is this bad practice?
Controller:
class ViewController: UIViewController {
let one = 1
let two = 2
var sum = one + two
}
Error:
ViewController.Type does not have a member named 'one'

Class variables and constants must be static, e.g., static let one = 1.
Here it suffices for the two let constants to be static for them to be usable in initializing both class and instance variables. The following works for me:
class MyClass {
static let one = 1
static let two = 2
static var sum = one + two
var instanceProduct = one * two
}
MyClass.one
MyClass.sum
MyClass().instanceProduct
Note that in the above example you can do MyClass.sum = 5. If you meant the sum to be constant as well, simply change it to static let sum = one + two.
The requirement is that the constants you use outside of any functions and closures be declared static let. The implication of this is that they are truly constant for the entire class. If you need instance-specific constants, you cannot use them outside of functions or closures (as you've noticed) – as a workaround for a variable sum I would suggest a lazy variable initialized by a closure:
class MyClass {
let one: Int // value given in `init`
let two = 2
lazy var sum: Int = { self.one + self.two }()
init(one: Int = 1) {
self.one = one
}
}
MyClass().sum // 3
MyClass(one: 2).sum // 4

Simple fix
override func viewDidLoad() {
super.viewDidLoad()
var result = one + two
}

Related

Why I can't change value in nested Models?

I have 2 class , First class has a property which inheritance from second class
class FirstModel{
var firstType : Secondmodel?
}
class Secondmodel{
var secondType : Int?
}
Now I want to set a value to secondType so I code
var Modelll = FirstModel()
Modelll.firstType?.secondType = 100
When I try to read this property with print(Modelll.firstType?.secondType) it return nil
So first question is why I couldn't read this
But I try to do this
var Modelll = FirstModel()
var ModelSecond = Secondmodel()
ModelSecond.secondType = 100
Modelll.firstType = ModelSecond
print(Modelll.firstType?.secondType)
It works perfectly , printed out Optional(100) I really dont understand whats going on behind the scene. Can anyone explain this?
First of all, all variables and constant should be named with lowercased symbol. With Uppercased only names of classes, structs, protocols, enums etc
You problem is that when You init FirstModel, firstType variable is nil by default
var model = FirstModel()
print(model.firstType) //prints nil
so You need to do
var model = FirstModel()
model.firstType = SecondModel() //use camel case in naming
model.firstType?.secondType = 100
print(model.firstType?.secondType) // prints 100
Ammm.. Because you don't initiate firstType with an instance in your first try.
You can add something like this
class FirstModel{
var firstType : Secondmodel?
init() {
self.firstType = Secondmodel()
}
}
But this is not exactly how things should be done. Here is not a problem of nesting, you just don't give a value in firstyType property.
The best case should be like:
class Secondmodel{
var secondType : Int?
init(secondType: Int) {
self.secondType = secondType
}
}
var modelll = FirstModel()
modelll.firstType = SecondModel(secondType: 100)
You should read more about OOP.
..and objects should not be named with capital letters

Swift: Store multiple Types in a single array

How do I store multiple Types in a single array?
I'm doing the following:
var vehicles = [string]()
let mustang = Car() // car is a class name
mustang.brandName = "Ford"
mustang.modelName = "Mustang" // string type
mustang.modelYear = 1968
mustang.isConvertibible = true
mustang.isHatchback = false
mustang.hasSunroof = false // bool type
mustang.numberOfDoors = 2 // int type
mustang.powerSource = "gas engine"
// Add it to array
vehicles.append(mustang.brandName)
vehicles.append(mustang.modelName)
vehicles.append(mustang.modelYear) // Error: int type not supported
vehicles.append(mustang.isConvertibible) // Error: bool type not supported
vehicles.append(mustang.brandName)
vehicles.append(mustang.brandName)
How should I achieve this? I'm new to Swift / iOS.
Instead of creating string array you can create the array of car like this and store directly car object
var cars = [Car]()
let mustang = Car() // car is a class name
mustang.brandName = "Ford"
mustang.modelName = "Mustang" // string type
mustang.modelYear = 1968
mustang.isConvertibible = true
mustang.isHatchback = false
mustang.hasSunroof = false // bool type
mustang.numberOfDoors = 2 // int type
mustang.powerSource = "gas engine"
cars.append(mustang)
Or if you want to store different types of object then you cant create array of AnyObject, So that it will store Any type of instance or object inside that array.
var arr = [AnyObject]()
let car = Car()
let bike = Bike()
arr.append(car)
arr.append(bike)
Swift 5
Using protocol oriented programming
A little more swifty solution could be to use protocols. This is type safe, so you won't have to deal with unwrapping and casting optionals.
1. Define a protocol
protocol ModeOfTransportation {
// Define anything in common between objects
// Example:
var color: UIColor { get set }
}
2. Make your models conform to the protocol
(The compiler will ensure you give it the correct types: ex. color)
struct Car: ModeOfTransportation {
var color = UIColor.red
}
struct Bicycle: ModeOfTransportation {
var color = UIColor.blue
}
3. Give your array the protocol as a type
class Example {
var myTransportList: [ModeOfTransportation] = []
func addModesOfTransport() {
let car = Car()
let bike = Bicycle()
myTransportList.append(car)
myTransportList.append(bike)
}
}
I think you're doing something really wrong.
Arrays are designed to hold only one type of stuff. If you really want to hold different types of stuff. Here's some possible methods:
you can try creating an array of AnyObjects:
-
var vehicles = [AnyObject]()
This won't work if the type you want to store does not conform to AnyObject. If you have such a type, you will have to use Any.
you can create an array of strings and convert all the values you want to store to a string
You can just create an array of Cars to store all the properties of a car. Then you can add more cars to it later on. I think this is what you intended.
Per the Swift documentation:
AnyObject can represent an instance of any class type.
Any can represent an instance of any type at all, including function types.
So you could make an array of
var vehicles = [AnyObject]()
and this would take objects (class instances) of any type.
However, the documentation goes on to say:
Use Any and AnyObject only when you explicitly need the behavior and capabilities they provide. It is always better to be specific about the types you expect to work with in your code.
So, ideally you are specific about the type your array can hold.

When to use static constant and variable in Swift?

There are some posts for how to write code for static constant and static variable in Swift. But it is not clear when to use static constant and static variable rather than constant and variable. Can someone explain?
When you define a static var/let into a class (or struct), that information will be shared among all the instances (or values).
Sharing information
class Animal {
static var nums = 0
init() {
Animal.nums += 1
}
}
let dog = Animal()
Animal.nums // 1
let cat = Animal()
Animal.nums // 2
As you can see here, I created 2 separate instances of Animal but both do share the same static variable nums.
Singleton
Often a static constant is used to adopt the Singleton pattern. In this case we want no more than 1 instance of a class to be allocated.
To do that we save the reference to the shared instance inside a constant and we do hide the initializer.
class Singleton {
static let sharedInstance = Singleton()
private init() { }
func doSomething() { }
}
Now when we need the Singleton instance we write
Singleton.sharedInstance.doSomething()
Singleton.sharedInstance.doSomething()
Singleton.sharedInstance.doSomething()
This approach does allow us to use always the same instance, even in different points of the app.
There are some posts for how to write code for static constant and static variable in Swift. But it is not clear when to use static constant and static variable rather than constant and variable. Can someone explain?
When you define a static var/let into a class (or struct), that value will be shared among all the instances (or values).
static variables/class are variables can be accessed without need of creation of any instance/object.
class Human {
static let numberOfEyes = 2 //human have only 2 eyes
static var eyeDefect = false //whether human have side-effect or not. he can have defect later so its variable
//other variables and functions
}
//you can access numberOfEyes like below no object of Human is created
print(Human.numberOfEyes)
print(Human.eyeDefect)
//Object of Human
let john = Human()
I think you know difference between constant and variable. In short, constant is that whose value never changes; numberOfEyes in above example and variable is that whose value changes; eyeDefect in above example.
static constant or variables are placed in memory(RAM) separate then the Objects. i.e. numberOfEyes have different memory space allocated than John object, its not inside John.
now, when to use static constants/variables:
When you use singleton design pattern: static let sharedInstance = APIManager()
class APIManager(){
static let sharedInstance = APIManager()
//Your other variables/functions here below
}
//Use it as to get singleton instance of APIManager from anywhere in your application
let instanceOfAPIManager = APIManager.sharedInstance
When you need value of anything that is globally the same without need to make instance of the class under which it is defined like numberOfEyes in human class.
Use of static variables/constants are not much recommended because of memory issues because once it's instantiated/assigned, it remains in memory until your application gets removed from the memory. I have found till now the best place to use static variables/constants is only while making singleton pattern and sometimes pointers for other normal variables and constants don't use static because: memory issue, it will be difficult to run unit testing in your code with static variables/constants. Not recommended to use as like in Human class also. instead use them as just constant or variables and access them by making instance.
class Human {
let numberOfEyes = 2 //human have only 2 eyes
var eyeDefect = false //whether human have side-effect or not. he can have defect later so its variable
//other variables and functions
}
//you can access numberOfEyes like below if you need just those values.
print(Human().numberOfEyes)
print(Human().eyeDefect)
Static constants and variables do belong to the class itself, not to a particular instance. A class can also have static methods that can be called without creating an instance of a class.
So when you have a class MyClass with a static var x, you can also access it through MyClass.x directly. x will be shared among all instances of a class
This is more of an important comment:
class Person {
static var name = "Static John" // a property of Person 'type'
var name = "Alex" // a property of Person 'instance'
var nonStaticName = "Peter"
static var staticName = "Sara"
static func statFunc() {
let x = Person.name // Static John
let y = name // Static John or Alex?! Static John!!!!
let r = staticName // Sara
let k = nonStaticName // ERROR: instance member 'nonStaticName' cannot be used on type 'Person'
// The compiler is like: I'm referring to the `nonStaticName` property of which instance?! There is no instance! Sorry can't do!
}
func nonStaticFunc() {
let x = Person.name // Static John
let y = name // Static John or Alex?! Alex!!! Because we're in a instance scope...
let k = nonStaticName // Obviously works
let r = staticName // ERROR: static member 'staticName' cannot be used on instance of type 'Person'. Person.staticName will work
}
}
Interesting observations:
First:
static var name = "Static John" // a property of Person 'type'
var name = "Alex" // a property of Person 'instance'
creates no conflicts.
Second:
You can't ever use instance variables inside static variables. You can use static variables inside instance functions if you refer to it by prefixing it with the type ie do Person.name, whereas
static variables can be accessed inside static functions with or without prefixing the type ie Person.staticName or staticName both work.

How to achieve object merging through generics in swift?

Imagine I have a class Number:
class Number {
var val: Double?
}
and have two instances of that class, A and B.
Now imagine I want to merge Binto Athrough a statement like
merge(B, into: A)
Now of course I could write the function like this:
func merge(from: Number, into: Number){
into.val = from.val
}
But that isn't reusable at all. Is there a way I could write a generic merge class?
UPDATE: Although some of the answers offer good and viable solutions, none of them are "generic" enough (generic here is meant in a non-technical way).So looking at the answers, I got some inspiration, and here is the solution I am now considering: make Number a NSObject subclass and declare all the properties that can be merged as dynamic. For example:
class Number: NSObject {
//Put the required init and initWithCoder: here
dynamic var val: Double?
}
Then declaring a protocol that mergeable classes must respect
protocol Mergeable: class {
var mergeablePropertyKeys:[String] {get}
}
And then declaring a global function that performs a merge:
func merge<U: Mergeable, Mergeable where U.Type == V.Type>(from: U, into:V){
for property in U.mergeablePropertyKeys {
V.setValue(U.valueForKey(property), property)
}
}
And I know that this will not work because the arguments to merge are not necessarily NSObjects.
How can I make sure that the arguments to merge are both NSObjects?
Can avoid having to specify the names of all my mergeable values by simply obtaining a list of my object's dynamic values?
It sounds like what you want is a generic function that uses reflection to merge properties. Reflection is limited in Swift, but it is doable using the MirrorType. I have used this method before to build a generic json parser in swift - you could do something similar but instead of parsing a json dictionary to properties map your two object's properties.
An example of using reflection to do this in swift:
func merge<T>(itemToMerge:T) {
let mirrorSelf = Mirror(reflecting: self)
let mirrorItemToMerge = Mirror(reflecting: itemToMerge)
for mirrorSelfItem in mirrorSelf.children {
// Loop through items in mirrorItemToMerge.
for mirrorImageItem in mirrorItemToMerge.children {
// If you have a parameter who's name is a match, map the value
// OR You could add any custom mapping logic you need for your specific use case
if mirrorSelfItem.label == mirrorImageItem.label {
// To set values, use self.setValue(valueToSet, forKey: propertyName)
self.setValue(mirrorImageItem.value as? AnyObject, forKey: mirrorImageItem.label!)
}
}
}
}
This assumes the object defining the merge method is a subclass of NSObject (so it can take advantage of NSKeyValueCoding). You could also make this a static method that could merge any 2 objects of any NSObject type:
static func merge<T1: NSObject, T2: NSObject>(itemChanging:T1, itemToMerge:T2) {
let mirrorSelf = Mirror(reflecting: itemChanging)
let mirrorItemToMerge = Mirror(reflecting: itemToMerge)
for mirrorSelfItem in mirrorSelf.children {
// Loop through items in mirrorItemToMerge.
for mirrorImageItem in mirrorItemToMerge.children {
// If you have a parameter who's name is a match, map the value
// OR You could add any custom mapping logic you need for your specific use case
if mirrorSelfItem.label == mirrorImageItem.label {
// To set values, use self.setValue(valueToSet, forKey: propertyName)
self.setValue(mirrorImageItem.value as? AnyObject, forKey: mirrorImageItem.label!)
}
}
}
}
Im not sure what you are expecting but there is generic solution:
class Number<T> {
var val: T?
}
protocol Merge {
func merge(from: Self, into: Self)
}
extension Number: Merge {
func merge(from: Number, into: Number) {
into.val = from.val
}
}
Protocol
Lets define a HasValue protocol (available only for classes) like this
protocol HasValue: class {
typealias T
var val: T? { get set }
}
Merge
Now we can define a generic function
func merge<U: HasValue, V:HasValue where U.T == V.T>(from: U, into:V) {
into.val = from.val
}
The constraints in the function signature do guarantee that
Both params do conform to HasValue (therefore are classes)
val types for both params are equals
Scenario 1: params have the same type
class Number: HasValue {
var val: Double?
}
let one = Number()
one.val = 1
let two = Number()
two.val = 2
merge(one, into: two)
print(two.val) // Optional(1.0)
Scenario 2: params have different types but their values have the same type
I did not constrain the 2 params of Merge to having the same type, I am only checking that the val properties of the 2 params must have the same type.
So we could also merge different instances of different classes having val of the same type like
class Phone: HasValue {
var val: Int?
}
class Computer: HasValue {
var val: Int?
}
let iPhone = Phone()
iPhone.val = 10
let iMac = Computer()
iMac.val = 9
merge(iPhone, into: iMac)
print(iMac.val) // Optional(10)
Scenario 3: params have generic types
class Box<S>: HasValue {
var val: S?
}
let boxOfString = Box<String>()
boxOfString.val = "hello world"
let boxOfInt = Box<Int>()
boxOfInt.val = 12
merge(boxOfString, into: boxOfInt) // << compile error
let boxOfWords = Box<String>()
boxOfWords.val = "What a wonderful world"
merge(boxOfString, into: boxOfWords)
print(boxOfWords.val) // Optional("hello world")

Swift: subscript for array with custom type

I have a class, MyClass, and in another class I have a global variable array:
var myArray:[MyClass]
In MyClass I don't have any array.
Now, if I want to do this:
for index in 0..10 {
self.myArray[index] = //a new object
}
I get an error that MyClass doesn't have a subscript. Any thoughts on why? Remember that MyClass doesn't have any arrays in it.
Some more code to show you what I mean:
class TheirClass {
var myArray: [MyClass] = [] // or use an initializer
func test() -> Void {
for index in 0..10 {
self.myArray[index] = MyClass()
}
}
}
class MyClass {
var prop1 = ""
var prop2 = 0
}
If you are calling a var from another class you would have to address it like I do below:
class TheirClass {
var myArray: [MyClass] = [] // or use an initializer
}
class MyClass {
var objectArray = TheirClass().myArray
}
Not sure if this is what you planned to do or not. Let me know and I can alter the code to fit your purpose.
You have .. in the code which doesn't exist in Swift. You probably meant ... but there is ..< as well.
However, you have an array of size zero in your initializer, which means that you can't add values to it. You'll either have to create a new array of the right size first of all, or append to it using myArray += [MyClass()].
Also note that the -> Void is unnecessary - if you don't have a return type, it will implicitly be a void type.

Resources