Sometimes Classes treat like structures in swift? - ios

for eg:-
In class DishPostedVC, I have an array of model:-
var finalDatesOfDish : [DishActivationDateTimeModel]?
If i pass this variable (finalDatesOfDish) to class DishActivationVC, and do some deletion operation in class DishActivationVC, and if i go back again to class DishPostedVC, then i am getting data after deletion not the actual data, but i am just coming back not passing any data.
I don't know why but this situation occurred one more time earlier with model array, its so strange, how's it possible?
can u tell me what shld i do?
class DishActivationDateTimeModel {
var dayDate: Date? = nil
var dayDateStr: String = ""
var servingsLeft: String = ""
var firstSlotPostedDishId: String = ""
var secondSlotPostedDishId: String = ""
var startTimeDateForFirstSlot: Date? = nil
var startTimeStringForFirstSlot: String = ""
var endTimeDateForFirstSlot: Date? = nil
var endTimeStringForFirstSlot: String = ""
var startTimeDateForSecondSlot: Date? = nil
var startTimeStringForSecondSlot: String = ""
var endTimeDateForSecondSlot: Date? = nil
var endTimeStringForSecondSlot: String = ""
}
It's my model

class DishActivationDateTimeModel {
var dayDate: Date? = nil
var dayDateStr: String = ""
var servingsLeft: String = ""
var firstSlotPostedDishId: String = ""
var secondSlotPostedDishId: String = ""
var startTimeDateForFirstSlot: Date? = nil
var startTimeStringForFirstSlot: String = ""
var endTimeDateForFirstSlot: Date? = nil
var endTimeStringForFirstSlot: String = ""
var startTimeDateForSecondSlot: Date? = nil
var startTimeStringForSecondSlot: String = ""
var endTimeDateForSecondSlot: Date? = nil
var endTimeStringForSecondSlot: String = ""
init(_ object : DishActivationDateTimeModel) {
self.dayDate = object.dayDate
self.dayDateStr = object.dayDateStr
// ....
// ....
self.endTimeStringForSecondSlot = object.endTimeStringForSecondSlot
}
}
extension Array where Element : DishActivationDateTimeModel {
func copyModelArray() -> [DishActivationDateTimeModel] {
var array : [DishActivationDateTimeModel] = []
for object in self {
array.append(DishActivationDateTimeModel(object))
}
return array
}
}
If you don't want to make changes in your main data then copy your
model array using copyModelArray method. And use your operational
model array. So, its not affect to your main model data array.

In Swift structs and classes give you both value and reference-based constructs for your objects. Structs are preferred for objects designed for data storage like Array. Structs also help remove memory issues when passing objects in a multithreaded environment. Classes, unlike structs, support inheritance and are used more for containing logic like UIViewController. Most standard library data objects in Swift, like String, Array, Dictionary, Int, Float, Boolean, are all structs, therefore value objects. The mutability of var versus let is why in Swift there are no mutable and non-mutable versions of collections like Objective C’s NSArray and NSMutableArray.

when you passed around your structs its copied but when you passed or assigned classes it gets reference to it.This means when you change class object in one location it changes everywhere, when you affect structs it changes individually. if you want more information differences between classes and structs you can check araound my article.
classes versus structs

Related

How to add optional values to an array in Swift

I'm having issues with appending an optional value to an array in Swift. The view I'm writing is for the creation of a routine for the gym. However my Routine object is not being instantiated as it should be.
I have experience with other programming languages but I am fairly new to Swift, and optionals.
My ViewController contains an optional variable:
var routine: Routine?
Where the Routine class contains:
name: String
exerciseList: [String]()
numOfSets: [Int]()
When I am preparing it to send the newly created routine to my other ViewController, I take the values from user input to edit the fields of the object.
let name = routineName.text ?? ""
let numberOne = Int(numOfSetsOne.text ?? "0") //numOfSetsOne is a text label
routine?.exerciseList.append(selectedExerciseOne!) //Haven't tested to see if this works yet
routine?.numOfSets[0] = numberOne! //This line is not working
routine = Routine(name: name)
To try a little debugging I put print statements on either side of the line like so:
print ("numberOne Value: \(numberOne!)")
routine?.numOfSets[0] = numberOne!
print ("numOfSets[0] Value: \(routine?.numOfSets[0])")
I expected the output from the second print statement to be identical to the first. However the terminal output:
numberOne Value: 3
numOfSets[0] Value: nil
Does anyone know what has gone wrong here?
Thanks
You have declared a property that may contain a Routine, but you have not assigned an instance of Routine to that property before trying to use it.
This means that, for example,
routine?.numSets[0] = numberOne!
doesn't do anything - routine is nil and so the statement is skipped.
You should create an appropriate init function for your Routine class and use that to create a new Routine and assign it to routine
For example:
class Routine {
var name: String
var exerciseList = [String]()
var numberOfSets = [Int]()
init(named: String) {
self.name = named
}
}
Then you can say
let name = routineName.text ?? ""
let numberOne = Int(numOfSetsOne.text ?? "0")
self.routine = Routine(named: name)
self.routine?.numberOfSets.append(numberOne!)
Coordinating related arrays can get a bit messy, so I would use a single array:
struct ExerciseSet {
let exerciseName: String
let sets: Int
}
class Routine {
var name: String
var exerciseList = [ExerciseSet]()
init(named: String) {
self.name = named
}
}
Your Routine is not initialised before its being assigned value
try
let name = routineName.text ?? ""
let numberOne = Int(numOfSetsOne.text ?? "0")
routine = Routine(name: name)
routine?.exerciseList.append(selectedExerciseOne!)
routine?.numOfSets[0] = numberOne!

Realm list property not saving data

I have two models, lets call them Schools, and Teachers. Models are as under
#objcMembers public class Schools : Object {
dynamic var Id : String = ""
dynamic var UserId : Int64 = 0
dynamic var Name : String? = ""
dynamic var listTeachers : List<Teachers>? = nil
dynamic var teachersList : [Teachers]? = []
}
#objcMembers public class Teachers : Object {
dynamic var Id : String = ""
dynamic var UserId : Int64 = 0
dynamic var Name : String? = ""
}
now before saving data I m putting Teachers objects (list) in School object then I save that School object in realm write closure.
after that I just get the School realm object and when I get the Teachers list, it always gets Nil. What is the case?
Am i missing something or missing something to understand the real LIST property??
please help
Update: This is how I am getting object
let mSavedItems = mDbHelper.realmObj.objects(Schools.self)
if let teachers = mSavedItems[0].teachersList{// here teacher list is nil
}
Your Schools declaration is flawed. You shouldn't declare a List as dynamic or mutable, nor should you make it Optional. As the docs clearly state, let listTeachers = List<Teachers>() is the correct way to declare a many-to-many relationship.
Storing a property of type Array is also not supported by Realm, so you should delete the teachersList : [Teachers]? property.
#objcMembers public class Schools : Object {
dynamic var Id : String = ""
dynamic var UserId : Int64 = 0
dynamic var Name : String? = ""
let listTeachers = List<Teachers>()
}

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

swift access property of an object in Array, returns nil

I have been learning the swift language. I setup this vocabulary class and using this class to generate a new object "newWord". I put this object into a new Array "vocabularyListb". When I try to get the newWord.name property from the array, it returns "nil". So the question is how can I access the property of an Object that resides in an Array?
class vocabulary{
let name:String
init(name: String){
self.name = name
}
}
let vocabularyList1a = ["instigate", "constitution", "bellow", "jargon", "term"]
var vocabularyList1b = [AnyObject]()
var newWord = vocabulary(name: vocabularyList1a[0])
newWord.name
vocabularyList1b.append(newWord)
vocabularyList1b[0].name
At the moment you instantiate your vocabularyList1b as [AnyObject]. But you actually want to have an array of vocabulary objects.
So you will have to change :
var vocabularyList1b = [AnyObject]()
To:
var vocabularyList1b = [vocabulary]()
After that you can access the name variable.

swift: can I have a property thats actually a reference?

for example.. I want to pass in some dictionaries into a class in its initializer, and i want to reference those dictionaries across my class.. the problem is when I set them as properties, they are actually being copied, not referenced.
example:
var activeDict: [Int: Projectile]
var inactiveDict: [Int: Projectile]
init(inout activeDict: [Int: Projectile], inout inactiveDict: [Int: Projectile]) {
self.activeDict = activeDict
self.inactiveDict = inactiveDict
I want to use activeDict, and inactiveDict across my class. I want them to be references of the originals that are being passed in.
You can store an UnsafeMutablePointer to the dictionary, here is an example, tested in playground:
var dict = ["hejj": 1]
class A<T> {
var myDictRef: UnsafeMutablePointer<T>
init(ref: UnsafeMutablePointer<T>) {
self.myDictRef = ref
}
}
let a = A(ref: &dict)
a.myDictRef.memory["asd"] = 3
a.myDictRef.memory
dict
Updated
In your case use it like that:
var activeDict: UnsafeMutablePointer<[Int: Projectile]>
var inactiveDict: UnsafeMutablePointer<[Int: Projectile]>
init(activeDict: UnsafeMutablePointer<[Int: Projectile]>, inactiveDict: UnsafeMutablePointer<[Int: Projectile]>) {
self.activeDict = activeDict
self.inactiveDict = inactiveDict
}
whenever you want to use activeDict or inactiveDict, you can call the dictionary behind the pointer with their memory property like:
activeDict.memory[YourKey] = YourValue

Resources