You don't have direct access to the index of the changed element, in part because setting a new value at a particular index is only one action that will trigger the didSet handler. Any mutating method will result in a call:
bar = ["Hello", "world"] // Set to [Hello, world]
bar[0] = "Howdy" // Set to [Howdy, world]
bar.insert("cruel", atIndex: 1) // Set to [Howdy, cruel, world]
bar.replaceRange(0..<1, with: ["So", "long"]) // Set to [So, long, cruel, world]
bar.removeRange(2..<3) // Set to [So, long, world]
bar.append("!") // Set to [So, long, world, !]
bar.removeAll() // Set to []
Inside the didSet handler, you do have access to a special variable named oldValue, which contains the previous value of the observed variable. If you need more than that you'd need to implement a struct or class that uses an Array for storage but provides its own true accessor methods.