array.append(value) to add one element to the end, array.append(contentsOf: other) or array += other to add many, and array.insert(value, at: 0) to add at the front. There is no constant-time prepend in Swift: inserting at index 0 shifts every existing element, so it costs O(n).Why this happens
Swift arrays have no push() or unshift() method. Those names come from JavaScript and Ruby, and neither exists on Array. What Swift gives you instead is a small set of mutating methods whose names describe exactly where the element lands: append for the end, insert(_:at:) for a specific index.
Two other things trip people up. First, the method signature changed in Swift 3 — older tutorials and Stack Overflow answers still show insert(str, atIndex: 0), which no longer compiles. Second, += only works between two arrays, not between an array and a single element, so myArray += "Tim" fails even though myArray += ["Tim"] is fine.
Finally, the question of a constant-time prepend has a real answer: there isn’t one. Array stores its elements contiguously, so inserting at the front moves everything else.
How to fix it
1. Add one element to the end with append(_:)
This is the direct replacement for push. The array must be declared with var, because append is a mutating method.
var myArray = ["Steve", "Bill", "Linus", "Bret"]
myArray.append("Tim")
// ["Steve", "Bill", "Linus", "Bret", "Tim"]
You can pass a variable or any expression, not just a literal:
let name = "Tim"
myArray.append(name)
If you write let myArray = [...] and then call append, the compiler stops you with cannot use mutating member on immutable value: 'myArray' is a 'let' constant. Change it to var.
2. Add several elements with append(contentsOf:) or +=
Both of these concatenate arrays. append(contentsOf:) is the more explicit form and is what you should reach for when the argument is a computed array.
var myArray = ["Steve", "Bill"]
myArray.append(contentsOf: ["Linus", "Bret"])
myArray += ["Tim", "Jony"]
+= is fine for a literal or another array, but it does not accept a bare element:
myArray += "Tim" // error: binary operator '+=' cannot be applied to operands of type '[String]' and 'String'
myArray += ["Tim"] // OK
That error is the single most common reason people think += is broken. It isn’t — the right-hand side has to be a sequence of the same element type.
3. Add an element to the front with insert(_:at:)
There is no unshift. Use insert with index 0, or startIndex if you prefer not to hard-code it:
var myArray = ["Steve", "Bill", "Linus", "Bret"]
myArray.insert("Tim", at: 0)
// ["Tim", "Steve", "Bill", "Linus", "Bret"]
myArray.insert("Jony", at: myArray.startIndex)
Note the label is at:, not atIndex:. The old Swift 2 spelling insert(str, atIndex: 0) produces extraneous argument label 'atIndex:' in call or incorrect argument label in call (have 'atIndex:', expected 'at:') on modern toolchains.
insert also works at the end — myArray.insert("Tim", at: myArray.endIndex) is equivalent to append — but append communicates intent better and skips the index arithmetic.
4. Insert a whole array at a position
insert(contentsOf:at:) splices another array in at a given index, keeping order:
var myArray = ["Steve", "Bret"]
myArray.insert(contentsOf: ["Bill", "Linus"], at: 1)
// ["Steve", "Bill", "Linus", "Bret"]
The index must be within 0...array.count. Passing array.count appends; anything larger traps at runtime with Fatal error: Array index is out of range.
5. Understand the cost before you prepend in a loop
append is amortized O(1) — the array occasionally reallocates and doubles its capacity, but the average cost per call stays constant. insert(_:at: 0) is O(n), because every existing element has to shift one slot to the right.
That means this is quadratic:
var reversed: [Int] = []
for value in 1...100_000 {
reversed.insert(value, at: 0) // O(n) each time
}
If you only need the elements in reverse order, append and reverse once at the end:
var values: [Int] = []
values.reserveCapacity(100_000)
for value in 1...100_000 {
values.append(value)
}
values.reverse()
reserveCapacity(_:) pre-allocates storage so the array doesn’t reallocate repeatedly while growing.
6. When you genuinely need fast insertion at both ends
If your algorithm really does push and pop from the front, Array is the wrong container. The swift-collections package ships Deque, which supports O(1) insertion and removal at both ends:
import Collections
var queue: Deque<String> = ["Steve", "Bill"]
queue.prepend("Tim")
queue.append("Linus")
Add the package via File > Add Package Dependencies in Xcode and import Collections. For anything that fits in memory and is read far more than it is prepended to, plain Array with insert(at: 0) is perfectly fine.
How to prevent it
- Declare the array with
varbefore callingappendorinsert;letblocks both. - Use
append(_:)for one element andappend(contentsOf:)for many — don’t mix them up with+=. - Remember the Swift 3 label change: it’s
at:, neveratIndex:. - Treat
insert(_:at: 0)as an O(n) operation and keep it out of loops over large collections. - Call
reserveCapacity(_:)when you know the final size and are appending in a loop. - Reach for
Dequefromswift-collectionswhen you need a real double-ended queue.
FAQ
Is there a push method on Swift arrays?
No. append(_:) is the equivalent. push exists in JavaScript and Ruby but not in Swift’s standard library, so myArray.push("Tim") fails with value of type '[String]' has no member 'push'.
Why does myArray += "Tim" not compile?
+= for arrays is defined between two sequences of the same element type. A bare String is not an array of strings, so the operator doesn’t match. Write myArray.append("Tim") or myArray += ["Tim"].
Can I add an element at the front in constant time?
Not with Array. Elements are stored contiguously, so inserting at index 0 shifts the whole buffer — O(n). Use Deque from swift-collections, or append and reverse at the end if order is all you need.
Parts of this guide are adapted from content licensed under CC BY-SA 4.0; this article is shared under the same license.
