Newbie: referencing and initializing structure elements within structures - arrays

OK, super-newbie Swift learner here with what I am sure is a simple question, that for some reason I cannot wrap my head around.
In the below code, I am creating a (very) simple address book structure just to understand how to reference and initialize the various elements, especially structures within structures. What am I doing wrong and is there a better way to write this? I am getting errors (using Swift Playground).
Errors on the last three lines:
Instance member 'page' cannot be used on the type 'AddressBookStruct'
Instance member 'subscript' cannot be used on type '[AddressPageStruct]'.
Plus, when I set var page = [AddressBookStrcut] I get the error:
Expected member name or constructor call after type name
Thanks in advance for the understanding nudge. :)
struct AddressPageStruct {
let firstName: String
let lastName: String
let cellPhone: Int
init(f: String, l:String, c:Int) {
firstName = f
lastName = l
cellPhone = c
}
}
struct AddressBookStruct {
let color: String
let size: String
var page = [AddressPageStruct]
}
var littleBlackBook = AddressBookStruct.self
littleBlackBook.init(color: "Black", size: "Little")
littleBlackBook.page[0].cellPhone = 6191234567
littleBlackBook.page[0].firstName = "Bob"
littleBlackBook.page[0].lastName = "Smith"

The struct is value type, please check this.
structure vs class in swift language,
you can't make this littleBlackBook.page[0].cellPhone, because cellPhone is constant you use let, instead use the constructor, also I change the page for empty array of AddressPageStruct
import Cocoa
struct AddressPageStruct {
let firstName: String
let lastName: String
let cellPhone: Int
init(f: String, l:String, c:Int) {
firstName = f
lastName = l
cellPhone = c
}
}
struct AddressBookStruct {
let color: String
let size: String
var page = [AddressPageStruct]()
}
var littleBlackBook = AddressBookStruct(color: "Black", size: "Little")
littleBlackBook.page.append(AddressPageStruct(f: "Bob", l: "Smith", c: 6191234567))
print(littleBlackBook.page)

enter code here
var littleBlackBook = AddressBookStruct(color: "Black", size: "Little")
let item = AddressPageStruct(f: "Bob", l: "Smith", c:6191234567 )
littleBlackBook.page.append(item)
littleBlackBook.page.append(AddressPageStruct(f: "Joe", l: "Blog", c: 3123467))
print(littleBlackBook.page[0])
print(littleBlackBook.page[1])
print(littleBlackBook)

Related

Comparing two arrays of objects in Swift

I have two arrays of objects with different sizes.
First one with old data, second one with updated data from server (included old data with new), data can be mixed. I want to get difference between these arrays.
My class
class Station {
var dateOfIssue: Date
var region: String
var locality: String
var bsName: String
var freqIn: String
var freqOut: String
var networkType: String
var provider: String
var bsUsableName: String
...
}
Arrays I want to compare (example)
var a = [Station]()
var b = [Station]()
for _ in 0...5 {
a.append(Station(someRandomStationValue...)
}
b = a
for _ in 0...7{
b.append(Station(someRandomStationValue...) //array "b" will contain all that array "a" contains and some new values
}
How to compare these arrays comparing all fields between and get a new array with differences (like in java: b.removeAll(a))?
You can make use of Set which provides in-built .subtract() and .subtracting() methods which removes the common entries inside both the Sets
struct Station: Hashable,CustomStringConvertible {
var id: Int
var region: String
var locality: String
var bsName: String
// Just to provide a pretty print statement
var description: String {
return "ID: \(id) | region: \(region) | locality: \(locality) | bsName: \(bsName)"
}
}
var stations1 = Set<Station>()
var stations2 = Set<Station>()
for currentNumber in 0...3 {
stations1.insert(Station(id: currentNumber, region: "abc", locality: "abc", bsName: "abc"))
}
for currentNumber in 0...5 {
stations2.insert(Station(id: currentNumber, region: "abc", locality: "abc", bsName: "abc"))
}
// Caluculating the difference here
print(stations2.subtracting(stations1))
As pointed out by #koropok, a good solution is using Set. The first step is to conform your type to Hashable. For classes, you'd have to implement == and hash(into:) functions, but if you use struct you don't have to do anything else other than declaring the conformance. So:
struct Station: Hashable {
var dateOfIssue: Date
var region: String
...
}
Now you should be able to add Station into a Set. Thus:
var a = Set<Station>()
for _ in 0...5 {
a.insert(Station(...))
}
var b = Set<Station>()
a.forEach { b.insert($0) }
for _ in 0...7 {
b.insert(Station(...))
}
let c = b.subtracting(a)
Set also provides a handy initializer that you can use to turn your Station array into a set:
let s = Set(arrayLiteral: [your, station, objects])
As mentioned in comments by koropok you may use subtract method:
// Added to make original code functional
// Station must conform to Hashable protocol in order to be stored in the Set
struct Station: Hashable {
let id: Int
}
var a = [Station]()
for i in 0...5 {
a.append(Station(id:i))
}
var b = [Station]()
for i in 0...7{
//array "b" will contain all that array "a" contains and some new values
b.append(Station(id:i))
}
var c = Set(b)
// c will contain 6 and 7
c.subtract(a)

Type 'SNstorelocation' has no subscript members - I just don't understand

I have been happily programming in Swift for a few weeks - really enjoying it, but I have hit a block. I have googled and I cannot find an explanation. I set up a much simpler test case, which didn't have the same problem.
I am getting the error "Type 'SNstorelocation' has no subscript members", and also "Value of type '[SNstorelocation]?' has no member 'append'".
I have read plenty about subscripting, but I don't think that is the problem. I want to have an array of structs, one of the elements is also an array of structs. I have seen it everywhere and my little test case it worked no problem.
So I have concluded (maybe incorrectly!) that somehow I haven't created and array. If I had, it should have an index to access the data and the code should work, but it doesn't.
Here is the little example I created, which I see as the same as my example, but simpler, which works.
struct test1 {
var t1Item1: Int?
var t1Item2: String?
var myArray = test2
}
struct test2 {
var t2Item1: Int?
var t2Item2: Int?
init(v1: Int, v2: Int) {
self.t2Item1 = v1
self.t2Item2 = v2
}
}
and I can do all the normal array things:
var myVar = [test1]()
var newItem = test1()
newItem.t1Item1 = 1
newItem.t1Item2 = "Hi"
myVar.append(newItem)
let myCount = myVar[0].myArray.count
Where as my example (just a few elements from the struct removed to keep it simple and short)
struct SNStoreItemGroups: Codable {
var Welland: [SNStoreItem]
var Other: [SNStoreItem]
init() {
self.Welland = []
self.Other = []
}
}
struct SNStoreItem: Codable {
var locations = [SNstorelocation]()
var customerName: String?
var customerPhone: String?
var customerEmail: String?
var notes: String?
}
struct SNstorelocation: Codable {
let longitude: Double
let latitude: Double
let country: String
let user: Int
let timestamp: Double = Date().timeIntervalSinceReferenceDate
init(_ lon: Double, _ lat: Double, _ ISOcountry: String, _ UserID: Int) {
self.longitude = lon
self.latitude = lat
self.country = ISOcountry
self.user = UserID
}
}
var mySNData: SNStoreItem?
var SNStore: SNStoreItemGroups = SNStoreItemGroups()
// Some code to populate the SNStore
if let locIndex = SNStore.Welland[index].locations.index(where: { $0.longitude == MyLongitude }) {
// This then causes the error
// "Type 'SNstorelocation' has no subscript members"
if SNStore.Welland[index].locations[locIndex].timestamp {
}
}
Could someone please explain why this second example has no subscripts and the first one works OK? I just don't understand - especially because of the first let which seems to be OK in finding the index - I am sure I have just done something stupid!
TIA.
Ambiguous error reporting by the Swift compiler…
A timestamp is not a Boolean so you need to compare the timestamp with something.
if let locIndex = mySNStore.welland[index].locations.index(where: { $0.longitude == 0.1234 }) {
if mySNStore.welland[index].locations[locIndex].timestamp == Double(42) {
}
}
You can see the correct error and the problem with:
let location = mySNStore.welland[index].locations[locIndex]
if location.timestamp {
}
You could raise a bug with the Swift team on this.
And as a style point Capitalised variables are horrible to read because they look like class names.

Make a copy of object with same address in swift

var empObj : EmployeeModel!
var tempObj : EmployeeModel!
tempObj = empObj.copy() as! EmployeeModel
whenevr I am copy the empObj to tempObj it's memory address is changed.I want to prevent this any idea?
when you copy its actually making a new reference. instead of copying just assign the value . then both will share the same reference
var empObj : EmployeeModel!
var tempObj = empObj
When you want the same address for different values
USE =
for eg.
var ogArray : NSMutableArray()
var tempArray = ogArray
If you are using objects two variables can point at the same object so that changing one changes them both, whereas if you tried that with structs you'd find that Swift creates a full copy so that changing the copy does not affect the original.
If you want to modify copies of a class object so that modifying one object doesn't have an effect on anything else, you can use NSCopying.
Make your class conform to NSCopying. This isn't strictly required
Implement the method copy(with:), where the actual copying happens.
Call copy() on your object.
Example Code for NSCopying
class Person: NSObject, NSCopying {
var firstName: String
var lastName: String
var age: Int
init(firstName: String, lastName: String, age: Int) {
self.firstName = firstName
self.lastName = lastName
self.age = age
}
func copy(with zone: NSZone? = nil) -> Any {
let copy = Person(firstName: firstName, lastName: lastName, age: age)
return copy
}
}
Check the output of this code in Playground
let paul = Person(firstName: "Paul", lastName: "Hudson", age: 36)
let sophie = paul.copy() as! Person
sophie.firstName = "Sophie"
sophie.age = 6
print("\(paul.firstName) \(paul.lastName) is \(paul.age)")
print("\(sophie.firstName) \(sophie.lastName) is \(sophie.age)")
Reference

Nil while appending data

I have a type struct Course
struct Course {
let title: String
let subtitle: String
let instructor: String
let color: String
init(title: String, subtitle: String, instructor: String, color: String) {
self.title = title
self.subtitle = subtitle
self.instructor = instructor
self.color = color
}
}
This is my code trying to append the data of type struct Course into courses but end up getting nil error. What should I do?
var courses: [Course]!
courses?.append(Course(title: "Team-Based Mobile Device Application Development", subtitle: "2018SP INFOTC 4500/JOURN 4444 Moore & Musser", instructor: "", color: ""))
You need to initialize your array. All you've do so far is declare it.
Change:
var courses: [Course]!
to:
var courses = [Course]()
This will declare and initialize courses as an empty array of Course.
Then:
courses?.append(Course...
becomes:
courses.append(Course...
You need to initialize a variable before it used.
so need to change
var courses: [Course]!
courses?.append(Course(title: ...
to
var courses: [Course] = []
courses.append(Course(title: ...
The best way for this to work is
var courses: [Course]! = []
with append function
courses.append(Course(title: "Team-Based Mobile Device Application Development", subtitle: "2018SP INFOTC 4500/JOURN 4444 Moore & Musser", instructor: "", color: ""))

Swift generics subclass behaviour in Array

So I walk against this problem.
I have a BaseModel with some variables inside and a few normal "final" Models that inherit from the BaseModel.
If I have an array with class BaseModel with inside a few final models, and then loop trough those models and sent them to a function that uses generics. And check the class of the generics I get back they are from class BaseModel instead of the final class model that I need. How can I retrieve in a dynamic way the class of the final model. And of course I could cast them, but I would love to find a better solution.
See example in the playground I made:
import UIKit
class BaseModel {
var id: Int
init(id: Int) {
self.id = id
}
}
final class PersonModel: BaseModel {
var firstName: String
var surname: String
init(id: Int, firstName: String, surname:String) {
self.firstName = firstName
self.surname = surname
super.init(id: id)
}
}
func genericsTest<C: BaseModel>(with object: C) {
print("Class type: \(C.self)")
}
var person0 = PersonModel(id: 0, firstName: "John", surname: "Doe")
genericsTest(with: person0)
// This prints: "Class type: PersonModel"
var person1 = PersonModel(id: 1, firstName: "John1", surname: "Doe")
var person2 = PersonModel(id: 2, firstName: "John2", surname: "Doe")
var person3 = PersonModel(id: 3, firstName: "John3", surname: "Doe")
var personArray: [BaseModel] = [person0, person1, person2, person3]
for person in personArray {
print("Class type in for loop: \(person.self)")
// This prints: "Class type in for loop: __lldb_expr_195.PersonModel"
genericsTest(with: person)
// This prints: "Class type: BaseModel"
}
So in the for loop if I print the object.self I get the class I expect but in the function with generics do the same call I get the base class.
Edit 1:
So if it would be possible to make Generic C the type of the final model I would be able to create thing like:
func genericsTest<C: BaseModel>(with type: C.Type, and object: C) {
let test = KeyPath<C, Int>
}
Just as example
Have a look at this
Question
You define a array of type 'BaseModel' so in your for loop it basically is saying
for let person: BaseModel in personArray
Making person of type BaseModel
In the function:
func genericsTest<C: BaseModel>(with object: C) {
print("Class type: \(C.self)")
}
You are using the type, in contrary in this code:
print("Class type in for loop: \(model.self)")
You are using the object. If you change the C to object you will get the same result.
Do remember if you going to use this in you project to determine what type of object this is. You need to seriously go back to your drawing board. Because this is not OOP!

Resources