I have an array of objects defined in swift, I load it in viewDidLoad(). The following code prints out the the values as expected:
for pair in pairs {
print("Pair - \(pair.word1): \(pair.word2)")
}
and the result looks like:
Pair - left: links
Pair - das Pferd: horse
Pair - good-bye: Auf Wiederzehn
Pair - right: rechts
Pair - horse: das Pferd
Pair - devil: der Teufeld
Pair - hello: Guten Tag
Pair - down: nach unten
Pair - nach unten: down
Pair - rechts: right
Pair - der Teufeld: devil
Pair - hoch: up
Pair - up: hoch
Pair - links: left
Pair - Auf Wiederzehn: good-bye
Pair - Guten Tag: hello
But the very next line causes an error of "Fatal error:Index out of range":
print("Test print - \(pairs[0].word1")
How can this code work fine in the for loop, but crashes with array index out of range if I try to access pairs[0].word1 immediately after this code?
Full code follow:
import UIKit
class ViewController: UIViewController {
var pairs = [Pair]()
var wordButtons = [UIButton]()
override func viewDidLoad() {
super.viewDidLoad()
guard let url = Bundle.main.url(forResource: "pairs", withExtension: "json") else {return}
do {
let jsonData = try Data(contentsOf: url)
let jsonDecoder = JSONDecoder()
pairs = try jsonDecoder.decode([Pair].self, from: jsonData)
pairs.shuffle()
} catch {
fatalError("Can't decode")
}
loadLevel()
for pair in pairs {
print("Pair - \(pair.word1): \(pair.word2)")
} // Prints fine
}
override func loadView() {
view = UIView()
view.backgroundColor = .darkGray
titleLabel = UILabel()
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.textAlignment = .center
titleLabel.text = "Concentration"
titleLabel.font = .systemFont(ofSize: 20, weight: .bold)
titleLabel.textColor = .white
view.addSubview(titleLabel)
let cardsView = UIView()
cardsView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(cardsView)
NSLayoutConstraint.activate([
scoreLabel.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
scoreLabel.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor),
cardsView.widthAnchor.constraint(equalToConstant: 400),
cardsView.heightAnchor.constraint(equalToConstant: 750),
cardsView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
cardsView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 50)
])
let width = 100
let height = 150
for row in 0..<4 {
for column in 0..<4 {
let wordButton = UIButton(type: .custom)
wordButton.titleLabel?.font = UIFont.systemFont(ofSize: 12)
wordButton.setTitle("WWW", for: .normal)
wordButton.setImage(UIImage(named: "cardBack"), for: .normal)
wordButton.addTarget(self, action: #selector(wordTapped), for: .touchUpInside)
let frame = CGRect(x: column * width, y: row * height, width: width, height: height)
wordButton.frame = frame
wordButton.layer.borderWidth = 5
wordButton.layer.borderColor = UIColor.lightGray.cgColor
cardsView.addSubview(wordButton)
wordButtons.append(wordButton)
}
}
for pair in pairs {
print("Pair - \(pair.word1): \(pair.word2)")
}
print("Crash \(pairs[0].word1)")
}
func loadLevel() {
// this is the new code that works fine
for i in 0..<pairs.count {
print("Setting title of wordButtons = \(i) title = \(pairs[i].word1)")
wordButtons[i].setTitle(pairs[i].word1, for: .normal)
}
}
//... rest of code removed due to relevance
}
You've very likely sliced this array, so the first index is not 0 (and
pairsis probably an ArraySlice instead of an Array). Try this instead:I expect that will work. This kind of problem occurs if you do something like:
In this case, the first index of
pairsis 2, not 0. As a rule, you should not subscript with numeric literals. Fetch indexes from the collection (firstIndex(of:)and the like), or use collection accessors likefirst.