Retrieve Values from Array with class - swift

I have a custom class defined as such:
public class Location {
var name = String()
var address = String()
var place = String()
}
I'm then populating an Array using that class as follows:
var chosenLocation = [Location]()
var locationResult = Location()
//Code to parse data here//
for result in results! {
locationResult.name = result["name"] as String
locationResult.address = "Bogus Address"
locationResult.place = result["place"] as String
chosenLocation.append(locationResult)
}
This all seems to be working fine, but when I try to get the individual "name" value in cellForRowAtIndexPath I just get the last record over and over. I guess I just don't understand how to reference each entry since it's a class wrapped in an array. The code I believe is in question, and which is returning the same row over and over is:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:UITableViewCell = UITableViewCell(style:UITableViewCellStyle.Default, reuseIdentifier:"cell")
var locationAnswer = Location()
locationAnswer = chosenLocation[indexPath.row
cell.textLabel?.text = locationAnswer.name
return cell
}
I believe it's getting appended to chosenLocation correctly, but since I don't know how to "unwrap" it , a println only shows me that I have the correct number of values and not what's in them.
Thanks a bunch for any help you can provide!

It looks like the bug is that just a single Location object is created and updated, so it contains the data from the very last update
Move the creation to be within the for loop...
// var locationResult = Location() <- Remove this
for result in results! {
var locationResult = Location() // <- Add it here
...

#Jawwad provided a solution to the problem.
Note that your code doesn't work because the item you are adding to the array is an instance of a reference type (class), so you are instantiating once, initializing at each iteration, then adding to the array - but what's added is just a copy of the reference to the instance, and not the instance itself.
Your code would work just fine if you turn the Location class into a struct. Being value types, structs are passed by value and not by reference, so the action of passing the same instance to the append method results in a copy of that instance to be created and passed.

Related

Find index of an object that contains an object with a specific value

Say I have an array of object 'YearGroup' called yeargroups. Each YearGroup has an array of object 'Student' called students.
In a function inside of a Student object, it is useful for me to know the index of the YearGroup it is in. How would I find this?
The below code is the example of this. I've made a similar function within YearGroup that finds it's own index in yeargroups, but how would I write a similar function within a Student?
class YearGroup {
var id = UUID()
var students = [Student]()
var myIndex = 0
func findMyIndex() {
myIndex = yeargroups.firstIndex(where: {$0.id == self.id})!
}
}
class Student {
let id = UUID()
var name:String
var age:Int
var myYearGroupIndex = 0
init(n:String, a:Int) {
name = n
age = a
}
func findMyYearGroupIndex() {
myYearGroupIndex = ????????
}
}
var yeargroups = [YearGroup]()
This code doesn't actually add any YearGroup or Student objects but I hope you get the idea.
Thanks!
As your code is right now a Student could be in any number of YearGroups. You could avoid potential confusion there by making the YearGroup a student is a member of a property of Student.
With that in place you can have the findMyYearGroupIndex() do a search like the one in YearGroup::findMyIndex().
If you really don’t want YearGroup to be a member of student you could pass it as an argument to findMyYearGroupIndex(groups: [YearGroups]).
Looking at this I’m not sure if YearGroups and the students make a 2 dimensional array. If so you need to store the index either as a pair (Int, Int) or an array with two elements [Int].
Sorry I don’t have code blocks in here. I can’t seem to make them on my phone.

Add array object not init

I have a method that init my class object from JSON. The method like this:
func getList (dic : Array<[String:AnyObject]>) -> Array<Dog> {
let dogs : NSMutableArray = NSMutableArray()
for myDic in dic {
let dog = Dog.init(dog: myDic)
dogs.addObject(dog)
}
return NSArray(dogs) as! Array<Dog>
}
And I can present it on tableview without issue. But right now I want to make pagination for the list. If I run the method getList again it will be init new object and replacing my old one. How can I to add a new object to exisiting. I don't want to create separate object with the same property.
You need to add a member variable which you will append to. So:
var dogs: [Dog] = []
And then call getList like this:
dogs += getList(dic: myDic)
By the way, you can make your getList method much simpler, like this:
func getList(dic: [[String: AnyObject]]) -> [Dog] {
return dic.map(Dog.init)
}

Wait for one property to initialize and use it as reference for pulling data from db into another property, best practice?

I have a class, it has two properties:
var fruitsPackId: Int
var fruitsPackContent: Array<Fruit>?
Once the class is being initialized, I want to append data into fruintsPackContent from a local db according the the initialized fruitsPackId. I am not sure what is the best practice on that type of case.
What I did for now, is creating fruitsPackContent as a computed property, that pulls out the data from the local db using the fruitsPackId as reference id.
However, I feel that this is just not the right way of doing it, any ideas?
My code:
class FruitsPack: NSObject {
var fruitsPackId: Int
init(fruitsPackId: Int) {
self.fruitsPackId = fruitsPackId
}
var fruitsPackContent: Array<Fruit>? {
// Pulling data from local db here...
// For this example I create a dummy array with one instance of Fruit
let fruit1 = Fruit(fruitsPackId: self.fruitsPackId, fruitName: "Banana")
var fruits = Array<Fruit>()
fruits.append(fruit1)
return fruits
}
}
class Fruit: FruitsPack {
var fruitName: String
init(fruitsPackId: Int, fruitName: String) {
self.fruitName = fruitName
super.init(fruitsPackId: fruitsPackId)
}
}
EDIT:
Using lazy variable type did the work for me:
Class initialization has nothing to do with that property
Memory is being utilized only once property is being called
The property is being filled up with data only once
An instance method is available to be used by others
New code:
class FruitsPack: NSObject {
var fruitsPackId: Int
lazy var fruitsPackContent: Array<Fruit>? = self.getFruitsPackContent(self.fruitsPackId)
init(fruitsPackId: Int) {
self.fruitsPackId = fruitsPackId
}
func getFruitsPackContent(fruitsPackId: Int) -> Array<Fruit>? {
// Pulling data from local db here...
// For this example I create a dummy array with one instance of Fruit
let fruit1 = Fruit(fruitsPackId: self.fruitsPackId, fruitName: "Banana")
var fruits = Array<Fruit>()
fruits.append(fruit1)
return fruits
}
}
class Fruit: FruitsPack {
var fruitName: String
init(fruitsPackId: Int, fruitName: String) {
self.fruitName = fruitName
super.init(fruitsPackId: fruitsPackId)
}
}
Retrieving data from a database is a relatively computationally expensive process, so I'm not personally a fan of building that into a computed property. What if you innocently had some code that did the following:
for index in 0 ..< fruitPack.fruitsPackContent.count {
print(fruitPack.fruitsPackContent[index])
}
If you had n items in the database, this code might be repeatedly retrieving the full list of all items from the database n + 1 times (once for count and again for each subscripted access). You could, theoretically, remedy this by making sure that the computed property cached the results (and you'd have to build some code that would identify when the database was updated and invalidate the cache).
It would be more prudent to make the retrieval from the database an explicit method so that the app developer knows when they're retrieving data from the database, and when they're accessing a cached array. You generally want to avoid some significant hidden performance impact resulting from innocently accessing some property.

How to trigger function by using check boxes in nstableview, in swift

I have nstableview in mac application. One column has names, second - numbers and third has checkboxes. When user make checkbox ON this should trigger function according to the name in first column in the same row. To have the name from first column (for triggering function) I need to know row index, and I use function rowForView(_ view: NSView). Question is what I should use as parameter (_ view: NSView) in this function. Below is the code. I am not sure if I am going in right direction to get what I have described above.
Related question is how I can get array of names from first column of table. I need this because if I will sort table according to the name then I cannot use my array, which used as data to fill the nstableview, I need array of sorted names to correctly trigger function.
#IBOutlet weak var myCheckbox: NSButton!
#IBAction func checkboxToShowRestrctaseSites(sender: AnyObject) {
if myCheckbox.state == NSOnState
{
let objectOfPresentRestrictase = RestrictasesSorting(restrictase: "", dna: inputDnaFromUser.string!)
var tableOfNumberedRestrictase = objectOfPresentRestrictase.makeListOfPresentRestrictase()
var listOfAllRestrictaseWithSites = objectOfPresentRestrictase.searchForRestrictionSiteInList().1
var row = myTable.rowForView(_ view: NSView)
var name = Array(tableOfNumberedRestrictase.keys)[row]
var site = listOfAllRestrictaseWithSites[name]
UPDATE
I have found that triggering function start to work if I remove if myCheckbox.state == NSOnState, and when I used sender as NSView as parameter for function rowForView. To control the ON and Off state I can also use sender in if condition. Code below.
#IBAction func checkboxToShowRestrctaseSites(sender: AnyObject){
if sender.state == NSOnState
{
let objectOfPresentRestrictase = RestrictasesSorting(restrictase: "", dna: inputDnaFromUser.string!)
var tableOfNumberedRestrictase = objectOfPresentRestrictase.makeListOfPresentRestrictase()
var listOfAllRestrictaseWithSites = objectOfPresentRestrictase.searchForRestrictionSiteInList().1
var row = myTable.rowForView(sender as! NSView)
var name = Array(tableOfNumberedRestrictase.keys)[row]
var site = listOfAllRestrictaseWithSites[name]
The documentation of rowForView:
Return Value
The index of the row corresponding to the view. Returns -1 if view is not an instance of NSTableRowView or a subview of an instance of NSTableRowView. In other words, if view is not in a table view, this method returns -1. (Note that this method may also return -1 when a row is being animated away, because view no longer has a valid row.).
Sounds like you can use the checkbox.
Your datasource does the sorting and has the data for the first column.

How can we transfer data between classes when using swift programming language

With Objective-C we could transfer data to an array at an UIView class from ViewController using a method.
In an UIView class we were using something like this
float values[10];
-(void) getValue:(float) value index:(int) index {
values[index] = value
}
But when we try doing a similar thing with Swift such as
var values : [CGFloat] = [10]
func getValue (value:CGFloat, index:Int) {
values [index] = value
}
We are getting " fatal error: Array index out of range error " or if we use
var values : [CGFloat] = [10]
var index = 0
func getValue (value:CGFloat) {
values.append = value
++index
}
We are not getting error message but whenever we use setNeedsDisplay() array values are being set to initial values which is 10 for this example.
Till now we are unable to convert Objective-C UIView classes like that to Swift one.
First:
var values : [CGFloat] = [10]
That line says that values is a variable array of CGFloat values that currently holds a single value of 10. Therefore, only index 0 actually exists.
Also:
func getValue(value:CGFloat, index:Int) {
values [index] = value
}
Never mind the fact that you have put this on the UIView class, never mind that you have a method named "getValue" that actually sets a value...
EDIT:
I found a better solution:
var values = Array<Float>(count:10, repeatedValue: 0)
func getValue(value: Float, index: Int) {
values[index] = value
}
The above is the direct Swift equivalent to the Objective-C code you posted in your question.
Frankly, I think you should take a step back and find a better way to solve the problem you think this code is solving. As it stands it doesn't make a whole lot of sense.
var values : [CGFloat] = [10]
In swift, this will create an array of CGFloat with one element: 10.0, not 10 elements.
so values.count is one
Swift Collections
Yes, after studying some more Swift I found the answer to my question.
Using static variable in the UIView class is the answer.
Define variable like that in UIView class
struct StructToChange {
static var varToChange = 1
}
Write a function to change variable value like this in UIView class.
func getIt (newValue:Int) {
StructToChange.varToChange = newValue
}
Call the function in a controller class this way
let valueChanger = AnyUIViewClass()
valueChanger.getIt ( 10 )
This is it you changed the value of variable and you can use it as parameter in your " override func drawRect(rect: CGRect) { } "