Simple swift array append not working - arrays

I know this is going to be super elementary, but I have this piece of code:
var labels: [String]?
func initVC(image: Images){
self.image = image
let tempLabels = image.label?.allObjects as! [Labels]
for i in 0..<tempLabels.count{
labels?.append(tempLabels[i].label!)
}
}
labels is in the public scope, so the function should have access to it, but when the loop runs through, labels is still nil with no elements.
When I po during debugging, tempLabels is as I expect it to be with 2 string elements.
I'm pretty sure this is a very simple problem, but I guess I'm just out of it right now.

Labels has never been initialised. Change
var labels:[String]?
to
var labels:[String] = []

You are declaring the labels variable but never allowing it to store information. This means that it does not necessarily exist, since it is not initialized, and therefore cannot be used.
For it to be useable, you must initialize it
var labels:[String] = []

Yep, it was super simple.
Changed
var labels: [String]?
To
var labels = [String]()

Related

SWIFT: assigning all values of an item in a structure array to a variable

Sorry, I don't even have an idea of the keywords to search for answer.
I want to store all items within a global structure to a local variable.
struct HighScores: Codable {
var highscoreRecord: [HighscoreRecord]
}
struct HighscoreRecord: Codable {
var Rank:Int
var Date:Date
var avDuration:Float
var Score:Int
}
A global variable is based on this structure and populated within a UIViewController
var jsonResult: HighScores?
Now, in another UIViewController, I want to extract the values of Score for all Highscores and store it to a local variable. I thought it should look somewhat like this, however, I do not get it to work
#IBDesignable class ScoreTimeGraphView: UIView {
var graphScore = jsonResult!.highscoreRecord.Score
The declaration above throws "Value of type '[HighscoreRecord]' has no member 'Score'"
Any ideas how to do this?
Cheers!
highscoreRecord is an Array. You can't use .Score directly on it because the Array type doesn't have a property named Score.
However, because it's element type is HighScore (which does have the property you want), you can iterate over it and collect the Score property from each one.
I think this is what you are after:
var allGraphScores = jsonResult!.highscoreRecord.map { $0.Score }
.map(_:) takes a closure with one parameter, and passes in each element of a sequence in turn.
So, highscoreRecord.map { $0.Score } returns a new array, by finding the Score property of each HighScoreRecord in the array highscoreRecord.
PS it's probably a good idea to name your variables using lowercase camelCase, for readability and instant recognition by any Swift dev that Score is a variable and not an object.
You're trying to access the property from an array. You need to provide an index to remove the error. Update this line:
var graphScore = jsonResult!.highscoreRecord.Score
To this:
var graphScore = jsonResult!.highscoreRecord[0].Score

Swift: Changing value of global variable via function.

I'm trying to change the value of a global variable transferredData in the function didTransferData(_ data: Data?). My code looks like this:
var transferredData: [Double] = [0.0]
func didTransferData(_ data: Data?) {
var dataArray = [CUnsignedChar](repeating:0, count: (data?.count)!)
data?.copyBytes(to: &dataArray, count: dataArray.count)
let dataInt = dataArray.filter({ Int(String($0)) != nil }).map({ Int(String($0))! })
let dataDouble = dataInt.map { Double($0) }
print("Data double: \(dataDouble)")
transferredData = dataDouble
}
Printing transferredData inside of didTransferData returns the correct array, containing all values of dataDouble. But when I try to access it in this function later on
func returnData() -> [Double]{
print("return Data: \(transferredData)")
return transferredData
}
it returns [0.0]. I'm not sure if I'm missing something crucial here, but I thought that even if I change the value of a global variable in a function, the new value should be accessible for every other functions, too.
Thanks for any advice!!
You said that this code is inside a class. The variable transferredData is not a global, it is a member variable.
There is one of these per object of the class. I suspect that you are not using the same object to make these two calls.
You could make the member shared between all objects by declaring it static, but I think it would be better to leave as is and arrange to use the same object.
EDIT: based on comment
If you write the code
let centralInstance = CentralViewController()
You will get a new object with its own transferredData member initially set to [0.0]. You need to either
Get a reference to the same VC object that has the data
Store the data some where else in an object that you can get back (since the VC might be gone)
To hack something that works (not recommended, but to help understand)
You could move transferredData out of the class and make it an actual global variable. I would do this only to help you get past this issue and understand what's going on.
You could also make it static, which makes all of the VC's share the same instance of the transferredData
When you understand what is going on, you can try to do something a little better (at least move it to its own model object, and not in the VC at all). The next simplest thing is some kind of singleton to manage it -- this is just a glorified global variable but puts you more on the road to a more typical solution.
Where is the problem:
$ swift
Welcome to Apple Swift version 3.0.2 (swiftlang-800.0.63 clang-800.0.42.1). Type :help for assistance.
1> var data:[Double] = [0.0]
2.
3. class MuckWithData {
4.
5. func showData () -> [Double] {
6. print ("Data: \(data)")
7. return data
8. }
9.
10. func modifyData () {
11. data = [1.0]
12. }
13. }
data: [Double] = 1 value {
[0] = 0
}
14>
15> var mucked = MuckWithData()
mucked: MuckWithData = {}
16> mucked.showData()
Data: [0.0]
$R0: [Double] = 1 value {
[0] = 0
}
17> mucked.modifyData()
18> mucked.showData()
Data: [1.0]
$R1: [Double] = 1 value {
[0] = 1
}
Probably your computation for dataDouble is actually [0.0] and thus it appears that transferredData didn't change, but it did.

Swift addObject

So, I'm learning how to get data from DB with JSON and then put the data on some array. Problem accours on last line, citiesArray.addObject(City()), when I need to put all data from object city (id, name, state,...) into array.
I was looking line by line with compiler, and basically everything is fine except that at the end, my array is still empty (its value is nil)?
for (var i=0;i<jsonArray.count;i++){
//Create city objec
var cID: AnyObject? = jsonArray.objectAtIndex(i).objectForKey("id") as NSString
var cName: AnyObject? = jsonArray.objectAtIndex(i).objectForKey("cityName") as NSString
var cState: AnyObject? = jsonArray.objectAtIndex(i).objectForKey("cityState") as NSString
var cPopulation: AnyObject? = jsonArray.objectAtIndex(i).objectForKey("cityPopulation") as NSString
var cCountry: AnyObject? = jsonArray.objectAtIndex(i).objectForKey("country") as NSString
//add city obj (i have City class) to city array
var city = City()
city.cityID = cID as NSString
city.cityName = cName as NSString
city.cityState = cState as NSString
city.cityPopulation = cPopulation as NSString
city.cityCountry = cCountry as NSString
citiesArray.addObject(City())
}
A couple of issues:
You suggested that you were trying to add the city with the following line of code:
citiesArray.addObject(City())
The City() construct will instantiate a new, blank City object. So that line of code would, best case scenario, add a blank City object to your array, which is not what you intended.
When you add the city to your citiesArray, you should simply:
citiesArray.addObject(city)
You say you've defined your citiesArray like so:
var citiesArray: NSMutableArray!
You also need to instantiate an object for this variable (i.e. create an object to which this variable will now point), e.g.:
citiesArray = NSMutableArray()
You are reporting, though, that at the end of this loop, that citiesArray is nil. Really?!? But if you tried to call the addObject method and citiesArray was nil, you could have received a fatal error: "unexpectedly found nil while unwrapping an Optional value".
So, if citiesArray was nil, then jsonArray must have been empty, too. Or for some reason you didn't even get to this loop. I would suggest (a) logging jsonArray; and (b) log or put breakpoint inside this loop and confirm you're even getting in here like you think you are.
Also, check the timing of this (i.e. make sure your statement logging citiesArray is actually taking place after this routine that populates it). I know that sounds crazy, but if you are retrieving the data from some network resource asynchronously, you could have some timing related issues.
Since you're writing Swift code, you might consider using Swift arrays. For example, define your array variable as
var citiesArray: [City]!
And instantiate it with:
citiesArray = [City]()
And add objects to it with:
citiesArray.append(city)
I am pretty sure you need to use the append function:
citiesArray.append(city)
or
if you want to append at the start of the array
citiesArray.insert(city, atIndex: 0)
instead of
citiesArray.addObject(City())
here is a little example: Syntax might not be 100% not on comp with xcode right now.
var strA:String = "apple"
var strB:String = "pineapple"
var strArr = ["kiwi", "mango", "lime"]
strArr.append(strA)
println(strArr.count) //4 ["kiwi", "mango", "lime", "apple"]
citiesArray.insert(strB, atIndex: 0)
println(strArr.count) //5 ["pineapple", "kiwi", "mango", "lime", "apple"]

Modifying an array of dictionaries in Swift

I’m new to Swift and have been having some troubles figuring out some aspects of Arrays and Dictionaries.
I have an array of dictionaries, for which I have used Type Aliases - e.g.
typealias myDicts = Dictionary<String, Double>
var myArray : [myDicts] = [
["id":0,
"lat”:55.555555,
"lng”:-55.555555,
"distance":0],
["id":1,
"lat": 44.444444,
"lng”:-44.444444,
"distance":0]
]
I then want to iterate through the dictionaries in the array and change the “distance” key value. I did it like this:
for dict:myDicts in myArray {
dict["distance"] = 5
}
Or even specifically making sure 5 is a double with many different approaches including e.g.
for dict:myDicts in myArray {
let numberFive : Double = 5
dict["distance"] = numberFive
}
All my attempts cause an error:
#lvalue $T5' is not identical to '(String, Double)
It seems to be acting as if the Dictionaries inside were immutable “let” rather than “var”. So I randomly tried this:
for (var dict:myDicts) in myArray {
dict["distance"] = 5
}
This removes the error and the key is indeed assigned 5 within the for loop, but this doesn't seem to actually modify the array itself in the long run. What am I doing wrong?
The implicitly declared variable in a for-in loop in Swift is constant by default (let), that's why you can't modify it directly in the loop.
The for-in documentation has this:
for index in 1...5 {
println("\(index) times 5 is \(index * 5)")
}
In the example above, index is a constant whose value is automatically
set at the start of each iteration of the loop. As such, it does not
have to be declared before it is used. It is implicitly declared
simply by its inclusion in the loop declaration, without the need for
a let declaration keyword.
As you've discovered, you can make it a variable by explicitly declaring it with var. However, in this case, you're trying to modify a dictionary which is a struct and, therefore, a value type and it is copied on assignment. When you do dict["distance"] = 5 you're actually modifying a copy of the dictionary and not the original stored in the array.
You can still modify the dictionary in the array, you just have to do it directly by looping over the array by index:
for index in 0..<myArray.count {
myArray[index]["distance"] = 5
}
This way, you're sure to by modifying the original dictionary instead of a copy of it.
That being said, #matt's suggestion to use a custom class is usually the best route to take.
You're not doing anything wrong. That's how Swift works. You have two options:
Use NSMutableDictionary rather than a Swift dictionary.
Use a custom class instead of a dictionary. In a way this is a better solution anyway because it's what you should have been doing all along in a situation where all the dictionaries have the same structure.
The "custom class" I'm talking about would be a mere "value class", a bundle of properties. This was kind of a pain to make in Objective-C, but in Swift it's trivial, so I now do this a lot. The thing is that you can stick the class definition for your custom class anywhere; it doesn't need a file of its own, and of course in Swift you don't have the interface/implementation foo to grapple with, let alone memory management and other stuff. So this is just a few lines of code that you can stick right in with the code you've already got.
Here's an example from my own code:
class Model {
var task : NSURLSessionTask!
var im : UIImage!
var text : String!
var picurl : String!
}
We then have an array of Model and away we go.
So, in your example:
class MyDict : NSObject {
var id = 0.0
var lat = 0.0
var lng = 0.0
var distance = 0.0
}
var myArray = [MyDict]()
let d1 = MyDict()
d1.id = 0
d1.lat = 55.55
d1.lng = -55.55
d1.distance = 0
let d2 = MyDict()
d2.id = 0
d2.lat = 44.44
d2.lng = -44.44
d2.distance = 0
myArray = [d1,d2]
// now we come to the actual heart of the matter
for d in myArray {
d.distance = 5
}
println(myArray[0].distance) // it worked
println(myArray[1].distance) // it worked
Yes, the dictionary retrieved in the loop is immutable, hence you cannot change.
I'm afraid your last attempt just creates a mutable copy of it.
One possible workaround is to use NSMutableDictionary:
typealias myDicts = NSMutableDictionary
Have a class wrapper for the Swift dictionary or array.
class MyDictionary: NSObject {
var data : Dictionary<String,Any>!
init(_ data: Dictionary<String,Any>) {
self.data = data
}}
MyDictionary.data

Found nil while unwrapping an Optional value (Music Library)

var songList = NSMutableArray()
var player = AVPlayer()
var isSelected = false
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var songs = MPMediaQuery() // also tried var songs = MPMediaQuery.songsQuery()
var localSongs: NSArray = songs.items
songList = NSMutableArray(array: localSongs)
tableView.reloadData()
var song: MPMediaItem = songList[0] as MPMediaItem // <<- Error here
var currentItem = AVPlayerItem(URL: song.valueForProperty(MPMediaItemPropertyAssetURL) as NSURL)
player.replaceCurrentItemWithPlayerItem(currentItem)
player.play()
var songTitle: NSString = song.valueForProperty(MPMediaItemPropertyTitle) as NSString
songName.text = songTitle
var time = Float(CMTimeGetSeconds(player.currentItem.duration))
sliderOutlet.value = time
}
I'm building a music player, but the app crasher when i run in the simulator and gives this error: index 0 beyond bounds for empty array, and says this when i try run on iPhone : {
MediaLibrary} Database validation succeeded
fatal error: unexpectedly found nil while unwrapping an Optional value
EDIT: Seems like it's not accessing the local music library on my iPhone and adding songs to the array.
You're accessing an element without testing its size. So songList[0] is out of bounds, hence the crash.
If your media query returns no items, then localSongs and songlist will both be arrays of size 0. When you then try to reference a song with songList[0] you will be accessing index 0 in an empty array. Hence the crash.
Seems more like a problem with expecting the simulator to have music in it. Since it doesn't, songList.count == 0, which means songList[0] should generate an exception. Seems like the expected and desired behavior to me.
Put a check on count using if condition. You are accessing index which can be nil. If accessed it will through runtime error.
Preferred approach would it be wrapped explicitly with ?

Resources