How to access member variables in Swift - uiview

Behold:
class CatView : UIView {
#IBOutlet var textView: UITextView
}
Behold further:
self.myCatView.textView.text = "meow"
I get this error:
CatView? does not have a member named textView
To me, it seems like CatView does have a member named textView. What am I missing?

myCatView must be declared as Optional in your class definition -- this is the error you get when you're trying to access a property of an Optional variable. You need to unwrap it first:
if let catView = self.myCatView {
catView.textView.text = "meow"
}
or
self.myCatView!.textView.text = "meow"
if you're absolutely sure self.myCatView has a value.

Related

What's the difference between : and = in swift

Sorry if the title is rather confusing, but I'm curious to know the difference between these two lines:
var title = String()
var title: String
Is one being initialized and one only be declared? Which is more correct?
For example, if I have a struct should I use one of the other?
So the reason I ask this is because I'm learning about how to grab some JSON from a url and then display it in my app. One of the new ways of doing so is using Decodable. So, I have a struct in a model class like so:
struct Videos: Decodable {
var title = String()
var number_of_views : Int
var thumbnail_image_name: String
var channel: Channel
var duration: Int
}
In another class I have this:
URLSession.shared.dataTask(with: url){(data,response,error) in
if(error != nil){
print(error!)
return
}
guard let data = data else { return }
do{
self.Videos2 = try JSONDecoder().decode([Videos].self, from: data)
//self.collectionView?.reloadData()
}catch let jsonErr{
print(jsonErr)
}
}.resume()
So, should I declare or initialize the variables in my struct? I'm assuming I should just declare them like so:
var title: String?
Would that be the correct syntax in my struct?
UPDATE:
I understand this question was more broad then I originally proposed it to be. I'm sorry about that, but thank you so much for all your great answers that clarified a lot up for me.
The difference is that : defines the type of your variable, whereas = assigns an actual value to the variable.
So:
var title = String()
This calls the initializer of the String type, creating a new String instance. It then assigns this value to title. The type of title is inferred to be String because you're assigning an object of type String to it; however, you could also write this line explicitly as:
var title: String = String()
This would mean you are declaring a title variable of type String, and assigning a new String to it.
var title: String
This simply says you're defining a variable of type String. However, you are not assigning a value to it. You will need to assign something to this variable before you use it, or you will get a compile error (and if this is a property rather than just a variable, you'll need to assign it before you get to the end of your type's init() method, unless it's optional with ? after it, in which case it gets implicitly initialized to nil).
EDIT: For your example, I'd probably declare all the variables using let and :, assuming that your JSON provides values for all of those properties. The initializer generated by Decodable should then set all the properties when you create the object. So, something like:
struct Videos: Decodable {
let title: String
let number_of_views : Int
let thumbnail_image_name: String
let channel: Int
let duration: Int
}
This initializes a value
var title = String()
This declares a value but does not initialize it
var title: String
If you attempt to use the latter, such as print(title), you will get a compiler error stating Variable 'title' used before being initialized
It does not matter whether the value is a class or a struct.
The = operator is the assignment operator, it assigns a value to the object on the left of the =
Typically, class or struct properties are declared but not initialized until the init() is called. A simple class might be
class MyClass {
let myProperty: String
init(aString: String) {
self.myProperty = aString
}
}
Whereas inside the scope of a function you may declare a local variable that only lives inside the scope of the function.
func doSomethingToAString(aString: String) -> String {
let extraString = "Something"
let amendedString = aString + extraString
return amendedString
}
In your specific example, the struct synthesizes an initializer that will allow you to initialize the struct with all the values needed to fill your properties. The initializer generated by Decodable should then set all the properties when you create a Videos struct, you will do it something like:
let aVideos = Videos(title: "My Title", number_of_views: 0, thumbnail_image_name: "ImageName", channel: Channel(), duration: 10)
Is one being initialized and one only be declared?
Yes, meaning that the declared cannot be used. If you tried to set a value for it, you would get a compile-time error:
variable 'title' passed by reference before being initialized
Which is more correct?
There is no rule of thumb to determine which is more correct, that would be depends on is there a need to initialize title directly.
On another hand, when it comes to declare properties for a class, saying var title = String() means that you are give title an initial value ("") which means that you are able to create an instance of this class directly, example:
class Foo {
var title = String()
}
let myFoo = Foo()
However, if title declared as var title: String, you will have to implement the init for Foo:
class Foo {
var title: String
init(title: String) {
self.title = title
}
}
let myFoo = Foo(title: "")
Also, you have an option to declare it as lazy:
lazy var title = String()
which means:
A lazy stored property is a property whose initial value is not
calculated until the first time it is used. You indicate a lazy stored
property by writing the lazy modifier before its declaration.
Properties - Lazy Stored Properties

Unable to understand behaviour of inheritance in swift?

I have created a parent class which has one property. Now i'm inheriting this class into my child class. It is obvious that member of parent class will become member of child class. So, when i change the value in child class, the same variable value is also getting changed in parent class.
I have used below mentioned code.
class SomeClass {
var avar:String = "Hello"
var bvar:String?
func someFunc() {
}
}
class Bclass:SomeClass {
func myFunc() {
self.avar = "Bye"
super.avar
}
}
let c = Bclass()
c.myFunc() // "Bye"
let d = SomeClass()
d.someFunc() // "Hello"
Here output is Bye but it should be Hello as i'm not changing parent class member value. When i access with instance of SomeClass then it shows output as Hello
Few questions-
Does it make copy of parent class variable in child class or refrence?
super means i want to access variable of parent class then why value changed ?
EDIT: As per Frankie answer
In below scenario why it still prints "Hello".
class SomeClass {
var avar:String = "Hello"
var bvar:String?
func someFunc() {
self.avar
}
}
class Bclass:SomeClass {
func myFunc() {
self.avar = "Bye"
super.avar
self.avar
}
}
class Cclass:SomeClass {
func myFunc() {
super.avar
self.avar
}
}
let c = Bclass()
c.myFunc()
let d = SomeClass()
d.someFunc()
let e = Cclass()
e.myFunc()
When you create an instance with let c = Bclass() there is one and only one instance created. There is no 'super instance' that is additionally created, so the notion of copying or reference doesn't make sense.
The subclass merely inherits all the properties and functions of the super class, or more plainly, it inherits only what defines the super class. Therefore self.avar and super.avar are the exact same thing.
Example:
class SomeClass {
var avar: String = "Hello"
}
class Bclass: SomeClass {
//imagine there is a (var avar: String = "Hello") here because it was defined in the super class
func myFunc() {
print(super.avar) //prints 'Hello'
self.avar = "Bye"
print(super.avar) //prints 'bye'
}
}
EDIT
From the OP's edit:
let c = Bclass()
c.myFunc() //assigns self.avar = "Bye", therefore prints "Bye"
let d = SomeClass()
d.someFunc() //does not make any assignment in someFunc, therefore prints the assigned value of "Hello"
let e = Cclass()
e.myFunc() //does not make any assignment in myFunc, therefore 'e' looks to its
//superclass for the value since it was never overridden and prints "Hello"
//It does NOT look to 'c' for the value because 'c' is a completely separate instance
A subclass takes all of the properties, function and methods of its superclass. The superclass is some kind of starting place / foundation for a subclass.
For instance, when you create a normal ViewController, you define it as:
class mySubclass: UIViewController{
// UIViewController's variables, properties, methods, functions...
// your code
}
You can imagine that all of the code from the superclass now lies inside the subclass as well. A good example of usage might be:
class Person{
var name: String = ""
var age: Int = 0
var job: String? = ""
}
class John: Person {
self.name = "John" // equivalent of super.name
self.age = 26
self.job = "Programmer"
}
You can say that the second class is some kind of customisation for the superclass, copying and changing its properties.

Lazy var giving 'Instance member can not be used on type' error

I had this error several times now and I resorted to different workarounds, but I'm really curious why it happens. Basic scenario is following:
class SomeClass {
var coreDataStuff = CoreDataStuff!
lazy var somethingElse = SomethingElse(coreDataStuff: coreDataStuff)
}
So I understand I can not use self before class is fully initialised, but in this case I'm using self property coreDataStuff to initialise a lazy var which will not happen until my instance is ready.
Anybody could explain me why I'm getting
Instance member can not be used on type error?
Try that :
class SomeClass {
var coreDataStuff = CoreDataStuff!
lazy var somethingElse: SomethingElse = SomethingElse(coreDataStuff: self.coreDataStuff)
}
It is important to precise the type of your lazy var and to add self. to the argument you pass
There are two requirements that are easily overlooked with lazy variables in Swift, and, unfortunately, the warnings are cryptic and don't explain how to fix it.
Lazy Variable Requirements
Use self.: When referring to instance members, you must use self.. (E.g. self.radius.)
If you forget to use self., you'll get this error:
Instance member 'myVariable' cannot be used on type 'MyType'
Specify the type: The type cannot be inferred, it must be explicitly written. (E.g. : Float.)
If you forget to specify the type, you'll get this error:
Use of unresolved identifier 'self'
Example
struct Circle {
let radius: Float
lazy var diameter: Float = self.radius * 2 // Good
//lazy var diameter = radius * 2 // Bad (Compile error)
}

Cannot assign a value of type '[Thing]' to a value of type '[Any]'

I'm struggling with what seems like a simple Swift problem.
I've declared a struct with a static function that returns some instances:
struct Thing {
static func allTheThings() -> [Thing] {
...
}
}
I've got a CustomViewController with an property declared:
var objects = [Any]()
In the subclass of that controller in viewDidLoad(), I'm trying to assign the objects property.
objects = Thing.allTheThings()
But I'm getting a compiler error
Cannot assign a value of type '[Thing]' to a value of type '[Any]'
Isn't that the whole point of Any?
This works:
objects = Thing.allTheThings().map { $0 }
But this doesn't
let things = Thing.allTheThings().map { $0 }
objects = things
Any ideas what's going on here?
It seems Swift can convert Thing to Any but not [Thing] to [Any].
The reason this works
objects = Thing.allTheThings().map { $0 }
is that the compiler can infer that the type of $0 is Any, but in the second example
let things = Thing.allTheThings().map { $0 }
it infers $0 to be of type Thing. You end up with the variable things being of type [Thing], which means that the assignment
objects = things
will mean a conversion from [Thing] to [Any] which does not work.

iOS: create an object class with Swift

I created this class for my object City
class City: NSObject {
var _name:String = ""
var name:String {
get {
return _name
}
set (newVal) {
_name = newVal
}
}
}
then when I create my object I do:
var city:City!
city.name = "London" //crash here
println("name city is\(city.name)");
it crash when I set the name with message "fatal error: unexpectedly found nil while unwrapping an Optional value"
This is not actually an answer (see other answers for a solution, such as #Greg's and #zelib's), but an attempt to fix some mistakes I see in your code
No need to create computed + stored property (unless you have a reason for that):
class City: NSObject {
var name: String = ""
}
If you inherit from NSObject, you automatically lose all swift features - avoid it (unless you have a reason for that)
class City {
var name: String = ""
}
You are using an empty string as absence of value - swift provides optionals for that
class City {
var name: String?
}
Alternative to 3., a city without a name wouldn't make much sense, so you probably want each instance to have a name. Use non optional property and an initializer:
class City {
var name: String
init(name: String) {
self.name = name
}
}
Avoid implicitly unwrapped optionals (unless you have a reason for that):
var city: City
Just like any other object oriented programming language, and object should be initialized before accessing it.
Like:
var city:City
This is just reference of the object. So, actual memory is not created here. You need to create actual object for City Class.
Fix it by adding following statement:
city = City()
You haven't initialised the city variable and when you trying to use it it crash.
initialise it first before you use it:
city = City()
city.name = "London"
You are getting error because you are not initializing your city variable instead you just implicitly unwrap it without initializing at any stage. To initialize it you must use the following code
var city:City = City()
You have to call the init method. So you would do it like this :
var city:City=City() //Calls init and creates an instance
city.name="foo"
If you don't define an init method(it's always a good practice that you do), the default init method is called.

Resources