Why is this crashing?

Whenever I run this code, after I have purchased something and it shifts up after leaving and reopening the page it crashes with the error "Index out of range".

Ideas? Thanks in advance!

Ps: I can provide full project if needed.
Answered by Mcrich23 in 674573022
I fixed it again by instead of using num unlocked using unlocked.count and locked.count.
I got it working, but now if IconChange.setIconPurchased == true isn't getting triggered because it is running before it's true. Ideas?
There are many test
Code Block
if IconChange.setIconPurchased == true {

Which one is not triggered ?

Note: you could simply write:
Code Block
if IconChange.setIconPurchased {

Could you please give me an example? This is what is not running:
Code Block
                        if IconChange.setIconPurchased == true {
                            print("purchased = true")
                            self.iconSelected = self.names[indexPath.row]
                            UserDefaults.standard.setValue(self.names[indexPath.row], forKey: "iconSelected")
                            self.IconSelector.reloadData()
                            tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
                            IconChange.pushBack = true
                            self.navigationController?.popViewController(animated: true)

Also, I am mysteriously getting the index out of range error and crashing again.
A general comment first. You could streamline your code by replacing statement like
Code Block
if unlocked.contains("Summer") {
} else {
unlocked.append("Summer")
}

by
Code Block
if !unlocked.contains("Summer") {
unlocked.append("Summer")
}


Which line do you get the crash exactly ? (code is slightly different from the snippet you present, so that makes it difficult to localise where this occurs); is it in didSelectRowAt ? Line 7, 10 or 16, 19 ?

If that's here:
  • i don't understand how you use names: rows in sections will be 0, 1, 2, restarting at zero in each section

  • you call self.unlocked[indexPath.row] ; Could you test with a print the size of self.unlocked and the number of rows in section ?

Code Block
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.row)
tableView.deselectRow(at: indexPath, animated: true)
if indexPath.section == 0 {
let icon = self.unlocked[indexPath.row]
setIcon(name: icon)
if IconChange.setIconPurchased == true {
iconSelected = names[indexPath.row]
UserDefaults.standard.setValue(names[indexPath.row], forKey: "iconSelected")
IconSelector.reloadData()
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
}
} else if indexPath.section == 1 {
let icon = self.locked[indexPath.row]
setIcon(name: icon)
if IconChange.setIconPurchased == true {
iconSelected = names[indexPath.row]
UserDefaults.standard.setValue(names[indexPath.row], forKey: "iconSelected")
IconSelector.reloadData()
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
IconChange.pushBack = true
self.navigationController?.popViewController(animated: true)
}
}
}

Yet another comment.
Maintaining several arrays to keep track of what is in tableView is really a risky design (the crash is just an illustration).
You'd better have a single dataSource, with attributes as locked: Bool instead of locked and unlocked arrays.
Thank you for the help with streamlining it. I couldn't figure that out earlier. As for where it crashes, it prints it fine and then crashes at cell.textLabel?.text = unlocked[indexPath.row], if I comment that out it crashes when setting the image.
Accepted Answer
I fixed it again by instead of using num unlocked using unlocked.count and locked.count.
So, add the print and tell what you get before crash:

Code Block
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.row)
print("unlocked:", self.unlocked.count, "locked:", self.locked.count) // <-- add this
tableView.deselectRow(at: indexPath, animated: true)
if indexPath.section == 0 {
let icon = self.unlocked[indexPath.row]
setIcon(name: icon)
if IconChange.setIconPurchased == true {
iconSelected = names[indexPath.row]
UserDefaults.standard.setValue(names[indexPath.row], forKey: "iconSelected")
IconSelector.reloadData()
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
}
} else if indexPath.section == 1 {
let icon = self.locked[indexPath.row]
setIcon(name: icon)
if IconChange.setIconPurchased == true {
iconSelected = names[indexPath.row]
UserDefaults.standard.setValue(names[indexPath.row], forKey: "iconSelected")
IconSelector.reloadData()
tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark
IconChange.pushBack = true
self.navigationController?.popViewController(animated: true)
}
}
}

My guess is that indexPath.row gets larger than the array size.
Good you found a solution.

I fixed it again by instead of using num unlocked using unlocked.count and locked.count.

But I could not see num unlocked anywhere in your code.

Anyway, don't forget to close the thread.
Ok, but I am still wondering about how to get the things to trigger if setIconPurchased = true

Ok, but I am still wondering about how to get the things to trigger if setIconPurchased = true

Maybe you need to clarify when you expect setIconPurchased gets true and when if setIconPurchased is executed.
Ok, thanks. Here is my code designed to check if purchased/purchase and then equip the icon, and refresh. However, the part with the if runs before it can be purchased, if it is already purchased then it works fine.
Code Block
func setIcon(name: String) {
            IconChange.setIconPurchased = false
            print(name)
            let app = UIApplication.shared
            if #available(iOS 10.3, *) {
                if app.supportsAlternateIcons {
                    checkForUnlocked(name: name)
                    if IconChange.setIconPurchased == true {
                        app.setAlternateIconName(name, completionHandler: { (error) in
                            if error != nil {
                                print("error => \(String(describing: error?.localizedDescription))")
                                // Create new Alert
                                let dialogMessage = UIAlertController(title: "Failed to change icon", message: "There was an error changing the icon: \(error!)", preferredStyle: .alert)
                                
                                // Create OK button with action handler
                                let ok = UIAlertAction(title: "OK", style: .default, handler: { (action) -> Void in
                                    print("\"Failed to change icon\" alert was dismised")
                                 })
                                
                                //Add OK button to a dialog message
                                dialogMessage.addAction(ok)
                                // Present Alert
                                self.present(dialogMessage, animated: true, completion: nil)
                            }else {
                                print("Changed Icon Sucessfully.")
                                self.iconSelected = name
                                UserDefaults.standard.setValue(name, forKey: "iconSelected")
                                IconChange.pushBack = true
                                self.navigationController?.popViewController(animated: true)
                                }
                            })
                        }
                    }
                }
            }


However, the part with the if runs before it can be purchased

Please clarify:
  • Where is the part with the if?

  • When is the time it can be purchased?

In my opinion, your code is too complex than it should be so just showing the code does not explain enough.
Would you like me to give you my project so you can run it? The if I am talking about is on line 15 and it is supposed to prompt to get purchased if not purchased already, and then refresh and select it. However, the if on line 15 is running before you purchase it.

Thank you for all the help you two!

Would you like me to give you my project so you can run it?

Thank you for your proposal, but as far as I see the code you have already shown, the whole project would not clarify what I want to know.

The if I am talking about is on line 15 and it is supposed to prompt to get purchased if not purchased already

If you say the if on line 15 is not working as you expect, why are you setting setIconPurchased to true before it is ready?
Once an icon gets purchased, setIconPurchased gets set to true, and then if it equals true it does more actions.
Why is this crashing?
 
 
Q