Search code examples
swiftswift5

Swift 5 Thread 1: Fatal error: Index out of range


I'm looping through an array using the array count. The code will run once and then after that I get an index out of range error. My code is below. I can't figure out why I'm getting this error. Can someone please let me know what I'm missing?

for stockItem in stride(from: 0, through: self.posts.count, by: 1) {


            guard let url = URL(string: "https://api.tdameritrade.com/v1/marketdata/\(self.posts[stockItem].symbol)/quotes") else {
                print("URL does not work")
                fatalError("URL does not work!")

            }}

Solution

  • The problem with stride(from:through:by:) is that it includes that final value supplied to through. Consider:

    let strings = ["foo", "bar", "baz"]
    for index in stride(from: 0, through: strings.count, by: 1) {
        print(index)
    }
    

    That will print four values (!):

    0
    1
    2
    3 
    

    If you tried to use that index as a subscript in the array ...

    for index in stride(from: 0, through: strings.count, by: 1) {
        print(index, strings[index])
    }
    

    ... it would work for the first three indexes, but that fourth one would fail because there are only three items in the array:

    0 foo
    1 bar
    2 baz
    Fatal error: Index out of range 
    

    You could solve this by using to, instead, striding up to, but not including, that final value:

    for index in stride(from: 0, to: strings.count, by: 1) {
        print(index, strings[index])
    }
    

    That would stop at the third entry, and everything would be good:

    0 foo
    1 bar
    2 baz
    

    All of that having been said, we wouldn’t generally use stride at all with a by value of 1. We’d just use a half-open range operator, ..<:

    for index in 0 ..< strings.count {
        print(strings[index])
    }
    

    Or, better, you might instead use:

    for index in strings.startIndex ..< strings.endIndex {
        print(strings[index])
    }
    

    Or, better, use indices:

    for index in strings.indices {
        print(strings[index])
    }
    

    The use of indices becomes essential if you happen to be working with slices of arrays, where one cannot assume the appropriate values, or if you happen to be dealing with some random access collection that does not happen to use numeric indices.

    Or, since you don’t really need that index, you would just do:

    for string in strings {
        print(string)
    }
    

    Or, in your case:

    for post in posts {
        let url = URL(string: "https://api.tdameritrade.com/v1/marketdata/\(post.symbol)/quotes")!
        ...
    }