Why I can't change value in nested Models? - ios

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

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

Populate Class Properties via 1 line within a for loop?

Let's say I have a class that has 10 properties. I have an XML response (AEXMLDocument in this case) where the element tags match the property names exactly. Is there a way I could populate the values in a for loop rather than writing out 10 lines of code? I used ***property.name**** to show where I would like to put these variables. That part is not actually in the code.
class User(){
var firstName = String()
var lastName = String()
var middleName = String()
...
var property10 = String()
}
func populateUser (xml: AEXMLDocument) -> User{
var returnUser = User()
for property in xml.root["SOAP-ENV:Body"]["ns1:getUserResponse"]["return"].children{
returnUser.***property.name*** = property.value
}
return returnUser
You can use setValue(_, forKey:) method as long as you subclass from NSObject.
If the class inherits from NSObject, you can use key-value coding:
returnUser.setValue(property.value, forKey: property.name)
Be careful with this though. Anyone who can modify the XML sent to your app can modify any property on the User object.

Classes and calling the properties Dynamically, Swift

I have two swift classes with two fairly similar properties (I have made the ones below the same for the purpose of this question) I want to use. I want to call the properties dynamically in the ViewDidLoad and I have used an array as shown below so I can change the index and choose which class to call. Unfortunately I get the error
Anyobject does not have a member named speciesType
What could I have missed or am I approaching the whole problem in the wrong way?
class Lion {
var age = 45
var speciesType = "Felidae"
}
class Lioness {
var age = 10
var speciesType = "Panthera"
}
var animal1 = Lion()
var animal2 = Lioness()
var animalKingDom = [animal1, animal2]
var colourChanging = animalKingDom[0].speciesType
Why dont you create a protocol that extracts out the common properties from both the classes.
class Lion {
var age = 45
var speciesType = "Felidae"
}
class Lioness {
var age = 10
var speciesType = "Panthera"
}
protocol LionType {
var speciesType: String { get set }
var age: Int { get set }
}
extension Lion: LionType { }
extension Lioness: LionType { }
var animal1 = Lion()
var animal2 = Lioness()
var animalKingDom: [LionType] = [animal1, animal2]
var colourChanging = animalKingDom[0].speciesType
I created a protocol LionType and added extension to both the classes to conform to the protocol. Since both the classes already have age and speciesType, the classes will remain unchanged. And finally, a small modification to your animalKingDom array could make it work easily.
By the way, I added a protocol LionType, I think it makes more sense to name it AnimalType which fits for all animals not just lions :)
I prefer the syntax where you declare the protocol before the classes, and let the classes explicit implement the protocol upon creation.
import Cocoa
protocol Animal {
var age: Int { get set }
var speciesType: String { get set }
}
class Lion: Animal {
var age: Int
var speciesType: String
init(age: Int, speciesType: String) {
self.age = age
self.speciesType = speciesType
}
}
class Lioness: Animal {
var age: Int
var speciesType: String
init(age: Int, speciesType: String) {
self.age = age
self.speciesType = speciesType
}
}
var l1 = Lion(age: 5, speciesType: "Cat")
var l2 = Lioness(age: 6, speciesType: "Doge")
var animalArray: [Animal] = [l1, l2]
for animal in animalArray {
print(animal.age)
print(animal.speciesType)
}
Also I'm not sure I would give a class for both male and female of an animal. But it totally depends on your business domain.
The compiler infer that the generic type of your array is 'AnyObject'.
Try removing one element from your array, and the compiler will find the good type of your array.
The compiler is not able to know that Lion and Lioness has a property in common.
You can either make a superclass or a protocol like GeneratorOne suggests.
I think that what you really want to do is have two instances of a single class (or better yet, if your real example is anything like this one, a struct). Let's keep using your Lion class. Two instances would then be
var lion = Lion()
lion.age = 45
lion.family = "Felidae"
var lioness = Lion()
lioness.age = 10
lioness.family = "Panthera"
(I'm ignoring the fact that your male and female are in different families. :))

Trying to create a Swift Realm Data Model

I have looked at the Realm.io docs. I am working on an application to track my vehicle expenses. I have put together what I think might work for a data model in Realm, but I am new to it and not sure if this is something that will work or if there is a better way to do it. Here is what I have, and I have not put this in a project and tried to compile yet. The realm.io docs are a little vague to me, so maybe someone can tell me what you think. I have included some comments in places that I am just not sure how to achieve what I'm going for...
// Vehicle model
class Vehicle : RLMObject {
dynamic var name = “”
dynamic var number = “”
dynamic var currentMiles = 0
dynamic var entries = RLMArray(objectClassName: Entry.className())
}
// Entry model
class Entry: RLMObject {
dynamic var vehicle: Vehicle //??
dynamic var date = NSDate()
dynamic var expense = 0.0
dynamic var mileage : Vehicle.currentMiles // want to update the Vehicle mileage with each entry
}
// Gas model
class Gas: Entry {
dynamic var gallons = 0
dynamic var pricePerGallon = 0.0
}
// OilChange model
class OilChange : Entry {
dynamic var milesBetweenChanges = 0
}
// Other Service model
class OtherService: Entry {
dynamic var notes = “”
}
You're on the right track! The only model that needs work is Entry, I think. First, here's your model with my annotations:
// Entry model
class Entry: RLMObject {
dynamic var vehicle: Vehicle // This is valid Swift, but you'll need to set the value in the designated initializer (`init()`).
dynamic var date = NSDate()
dynamic var expense = 0.0
dynamic var mileage : Vehicle.currentMiles // This isn't valid Swift, since `Vehicle` is a class, and doesn't have a `currentMiles` member
}
What you want is something like this:
// Entry model
class Entry: RLMObject {
dynamic var vehicle = Vehicle() // Use a default value so that `init()` succeeds, but you can still use `init(vehicle: Vehicle)` in your code
dynamic var date = NSDate()
dynamic var expense = 0.0
dynamic var mileage = 0
init() {
// Must override init() when adding a convenience initializer
super.init()
}
convenience init(vehicle: Vehicle) {
super.init()
self.vehicle = vehicle
mileage = vehicle.currentMiles
}
}
It's unfortunate that you find Realm's docs vague. Please let us know if there's anything in particular you'd like us to clarify. We're a pretty approachable bunch!

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