App Crash on Table Row load - ios

I'm pulling data in from CloudKit, and there is only one item in the data.
Once the matchup loads, and before the background color is seen in the UI, the app crashes. I can't figure out why, any ideas?
InterfaceController on Watch:
func loadTable() {
self.rowTable.setNumberOfRows(self.matchupArray.count, withRowType: "rows")
let rowCount = self.rowTable.numberOfRows
for i in 0...rowCount {
let row = self.rowTable.rowController(at: i) as! Rows!
row?.matchup.setText(self.matchupArray[i])
let colorBackground = UIColor.init(hex: self.teamColorArray[i])
row?.groupColor.setBackgroundColor(colorBackground)
}
}
func getData() {
cloud.getCloudKit { (game: [GameWatch]) in
var teamColorArray = [String]()
var matchupArray = [String]()
for item in game {
teamColorArray.append(item.teamColor)
matchupArray.append(item.matchup)
}
self.teamColorArray = teamColorArray
self.matchupArray = matchupArray
self.loadTable()
}
}
UPDATE:
Got a crash, with the error "fatal error: Index out of range".
I'm not sure why this is, because the matchupArray.count is 1, the rowCount is 1. It started iterating through the for-loop with i as 0, and finished the first iteration where it should have stopped since there was only 1 item. But I got the crash because it started to iterate through the loop again, with i as 1, and then obviously found nothing so it crashed.
The crash comes after row?.matchup.setText(self.matchupArray[i]) is run.

The error is in the line:
for i in 0...rowCount
This ... operator creates a range of indexes that includes both values when, because Swift uses 0-based arrays, you need the ..< operator to create a range that excludes the upper value.
This line should therefore be:
for i in 0..<rowCount

Related

iOS app crashing on high numbers of table cells with sliders

Noob here.
I am programmatically creating a number (globalCount) of UISliders in iOS with Swift 3. A grandTotal float value is divided between these sliders' values and when one is changed they all adjust to sum to 100% of the grandTotal. No slider goes below their minimum of 0 or above their maximum of 100.
All is working well for numbers of sliders less than 7. As soon as I add 7 or more. I get a crash when trying to update the cell's slider values with the underlying struct values.
I've moved the cell update functionality to its own function to try isolate the issue. I still get a crash. The crash is
EXC_BAD_INSTRUCTION (code=EXC_1386_INVOP, subcode=0x0)
and appears on the let loopCell line.
Does anyone have any suggestions as to what this is about? Why is it working for low numbers and not higher numbers of cells? Is this a conditionals thing?
This is the cell update function
func updateCells() {
for i in 0...globalCount - 1 {
let loopCell: MyTableCell = ((myTableView.cellForRow(at: IndexPath(row:i, section: 0))) as? MyTableCell)!
loopCell.valueLabel.text = String(structData[i].value)
loopCell.cellSlider.value = structData[i].value
}
}
My UI:
cellForRow(at:) returns nil if a table view cell is not visible (currently offscreen). You are crashing because you are force unwrapping nil. Instead, use optional binding (if let) to safely unwrap the value:
func updateCells() {
for i in 0..<globalCount {
if let loopCell = myTableView.cellForRow(at: IndexPath(row: i, section: 0)) as? MyTableCell {
loopCell.valueLabel.text = String(structData[i].value)
loopCell.cellSlider.value = structData[i].value
}
}
}
It's due to use of force unwrapping it is crashing .
Best handy way is to use if let statement to check data is there or not , since if let does it (unwrapping).

Swift 3 - 'EXC_BAD_INSTRUCTION(code=EXC_1386_INVOP,subcode=0x0)' Error

I am working on a maze game in swift 3 and it compiles but when I run it I get the EXC_BAD_INSTRUCTION error. I'm relatively new to swift so I don't know how to properly debug it. Here is the piece of code that is getting the error:
init(window:SKScene, width:Int, height:Int){
widthOfBoard = width
heightOfBoard = height
for i in 0..<widthOfBoard {
var temp:[cell]!
for j in 0..<heightOfBoard {
let tempCell = cell(x: i, y: j, boardWidth: widthOfBoard, boardHeight: heightOfBoard, screenSize: window.frame)
temp.append(tempCell)
}
cells.append(temp)
}
stackOfCells.append(cells[0][0])
recursiveMaze(currentX: 0, currentY: 0, window: window)
}
I am getting the error with cells.append(temp).
I am also getting the error in each of
these locations.
temp is nil that causes the crash. Any carelessly written exclamation mark can cause a crash.
You declared the variable temp but you have to initialize the array if you want to append items to it:
var temp = [cell]()
By the way: Class names are supposed to start with a capital letter.

App crashes if playlist count = 0 (Do empty playlists have a persistentid?)

My app is crashing if a playlist is empty (no songs). My app works for all non-empty playlists. It seems like there isn't a persistentid for an empty playlist, but I think I am wrong on that.
let qryPlaylists = MPMediaQuery.playlistsQuery()
var selectedPlaylistTitle: String!
var selectedPlaylistPersistentID: UInt64!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let indexPath: NSIndexPath = playlistsTableView.indexPathForSelectedRow!
let rowItem = qryPlaylists.collections![indexPath.row]
let playlistSize = rowItem.count
print("playlistSize = ", playlistSize)
selectedPlaylistTitle = rowItem.valueForProperty(MPMediaPlaylistPropertyName) as! String
print("selectedPlaylistTitle = ", selectedPlaylistTitle)
// Will crash on this next line if the playlistSize = 0
selectedPlaylistPersistentID = rowItem.items[0].persistentID
// If I use following line instead, it will crash on any playlist
// selectedPlaylistPersistentID = rowItem.valueForProperty(MPMediaPlaylistPropertyPersistentID) as! UInt64
// This line will never be printed
print("selectedPlaylistPersistentID = ", selectedPlaylistPersistentID)
}
Thanks in advance for any help!
If an array such as items is empty, it has no index 0 and will crash if you refer to it, with an out-of-bounds error. So if you don't want to crash, don't do that. You already know that items is empty, because rowItem.count told you so; as you said yourself, playlistSize is 0 in that case.
A simple-minded way to look at it is this: the largest legal index in an array is one less than its count.
Another issue you asked about is that this line always crashes:
selectedPlaylistPersistentID = rowItem.valueForProperty(MPMediaPlaylistPropertyPersistentID) as! UInt64
The problem here is that you are apparently using Swift 2.x. (You should have said that in your question; I deduce it, though, from the fact that valueForProperty has not changed to value(forProperty:), which is what it is called in Swift 3.)
In Swift 2, you cannot cast directly down to UInt64. (I am surprised that the compiler does not draw your attention to this fact with a warning.) Thus, the cast fails and you crash. You need to cast down to NSNumber and then take that NSNumber's unsignedLongLongValue.
And while you are doing this, you really should stop using exclamation marks in your code. When I say "cast down", I mean "cast down safely". My own Swift 2 code for doing this sort of thing looks like this:
if let id = (rowItem.valueForProperty(MPMediaItemPropertyAlbumPersistentID) as? NSNumber)?.unsignedLongLongValue {
// ...
}

Swift: Getting EXC_BAD_INSTRUCTION when trying to append random array elements

I am getting the error, "Thread 1: EXC_BAD_INSTRUCTION(code=EXC_1386_INVOP, subcode=0x0)" when trying to append random array elements into a new array.
The debug log says "fatal error: Index out of range"
//If there are more than 6 players prioritizing the event, make a random choice. garudaLocations is an array containing the players who prioritized the event "Garuda".
if garudaLocations.count > 6 {
var finalGarudaPlayers : [Int] = []
let getRandom = randomSequenceGenerator(1, max: garudaLocations.count) //Tell RNG how many numbers it has to pick from.
var randomGarudaPrioritiesIndex = Int()
for _ in 1...6 {
randomGarudaPrioritiesIndex = getRandom() //Generate a random number.
finalGarudaPlayers.append(garudaLocations[randomGarudaPrioritiesIndex]) //ERROR: Thread 1: EXC_BAD_INSTRUCTION(code=EXC_1386_INVOP, subcode=0x0)
}
debugPrint(finalGarudaPlayers) //Print array with the final priority Garuda members.
randomSequenceGenerator is a function I got from here, which does work to generate the random numbers.
func randomSequenceGenerator(min: Int, max: Int) -> () -> Int {
var numbers: [Int] = []
return {
if numbers.count == 0 {
numbers = Array(min ... max)
}
let index = Int(arc4random_uniform(UInt32(numbers.count)))
return numbers.removeAtIndex(index)
}
}
To get a better understanding, I am trying to write a piece of a "team making" program where players are automatically sorted into events, but they can choose which events they would like to prioritize.
I can only have 6 people per event, however, so the goal is to take the existing garudaLocations array, choose a random 6 index locations, and get rid of the rest of the players.
I get the error only after I submit more than 6 players to the same event.
Any help is very much appreciated!
You can never speak of a non-existent index. If you do, you will crash just as you are crashing now.
So, you are saying:
garudaLocations[randomGarudaPrioritiesIndex]
Now, I do not know what garudaLocations is. But I can tell you for certain that if randomGarudaPrioritiesIndex is not an existing index within garudaLocations, you will absolutely crash.
Thus, you can easily debug this by logging (print) randomGarudaPrioritiesIndex.
Bear in mind that the largest existing index is not garudaLocations[garudaLocations.count]. It is garudaLocations[garudaLocations.count-1]. So compare randomGarudaPrioritiesIndex to garudaLocations.count-1. If it is greater, you crash when you use it as an index on garudaLocations.

How Can I Remove Only Selected Index Paths From An Array In Swift?

I have a mutable array:
var responseArray = ["yes", "no", "no way", "of course", "for sure", "not a chance", "positively"]
The responseArray is the data source for my table view which allows for multiple selections during editing.
I am capturing the selected index paths:
let paths = tableView.indexPathsForSelectedRows()
I can return and verify each selected indexPath of my tableView by running println(paths).
I have read the documentation for the indexPathsForSelectedRows method and understand that it returns an array of index paths which I have sorted by row.
What I cannot understand is how I can use the returned array of index paths to remove the data from the responseArray for each row that is selected for deletion in the table view.
After reading through some documents, is it correct of me to believe that I cannot remove any data from the `responseArray' as I am enumerating over it? For example:
#IBAction func deleteButtonPressed(sender: AnyObject) {
if responseArray.count > 0 {
if let paths = self.tableView.indexPathsForSelectedRows() {
var sortedArray = paths.sorted({$0.row < $1.row})
// Remove index paths from responseArray
for i in sortedArray {
responseArray.removeAtIndex(i.row)
}
self.tableView.reloadData()
}
}
}
I am able to remove each row from the table view one by one, but when I select the first and last rows, all of the rows, or any other combination of rows for deletion, I get fatal error: Array index out of range. However, if I select two adjacent rows for deletion, the desired result is achieved and those two rows are removed from the table view.
I know that there is something that I am missing, but as a new programmer I have been unable to resolve this issue for three days now. What is it that I am not doing correctly?
Here's your array: [ A, B, C, D ]
Let's say you want to delete A and D at index 0 and 3 respectively, one at a time:
deleteAtIndex(0) gives: [ B, C, D ]
deleteAtIndex(3) gives: Out of bounds exception
Edit:
Ok, to avoid complicating things, why not just always delete the highest index first by reversing your sort: {$1.row < $0.row}
For future reference, in addition to the answers given already you could simplify it further by reversing the selected indices:
#IBAction func deleteButtonPressed(sender: AnyObject) {
self.tableView.selectedRowIndexes.reverse().forEach { x in
responseArray.removeAtIndex(x)
}
self.tableView.reloadData()
}
Since your paths array is sorted, you know that every time you delete an element from the array, higher indices will now be one less than they were. You can simply keep an incrementing offset to apply to your deletions -
#IBAction func deleteButtonPressed(sender: AnyObject) {
if responseArray.count > 0 {
if let paths = self.tableView.indexPathsForSelectedRows() {
var sortedArray = paths.sorted({$0.row < $1.row})
var offset=0;
// Remove index paths from responseArray
for i in sortedArray {
responseArray.removeAtIndex(i.row-offset)
offset++
}
self.tableView.reloadData()
}
}
}

Resources