Can I make an Array extension that applies to, for instance, just Strings?
-
1Not sure what you mean - can you say more about your intent? Maybe an example? – dpassage Dec 08 '14 at 03:28
-
@dpassage You can extend things with the requirement that the extending classes adhere to a protocol (https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Extensions.html), I'm curious if you can further restrict it to only a class, so that my extension in Swift will only apply to String arrays. – Doug Smith Dec 08 '14 at 03:32
-
But describe what you want such an extension to _do_. Is it just a matter of a function? Then define a global generic function. Give an actual use case for what you are hoping to accomplish. – matt Dec 08 '14 at 03:33
-
Yes, it's just a function. Could you describe what that would look like? If I'm understanding correctly I'd have to pass the array in, which wouldn't be optimal. – Doug Smith Dec 08 '14 at 03:34
-
It would look like the built-in functions. Look at how `find` works on Arrays whose elements are Equatable only. – matt Dec 08 '14 at 03:35
-
Appreciate the answer, but I'd prefer a cleaner solution that operates like other extensions. – Doug Smith Dec 08 '14 at 03:36
-
You're not going to get one. That's not how extensions work. Why not wrap up an `Array
` as a property of a struct or class? Now you're in total charge and you've encapsulated. – matt Dec 08 '14 at 03:37 -
Ah okay, I like that idea, I'll play around with that. – Doug Smith Dec 08 '14 at 03:42
4 Answers
As of Swift 2, this can now be achieved with protocol extensions, which provide method and property implementations to conforming types (optionally restricted by additional constraints).
A simple example: Define a method for all types conforming
to SequenceType
(such as Array
) where the sequence element is a String
:
extension SequenceType where Generator.Element == String {
func joined() -> String {
return "".join(self)
}
}
let a = ["foo", "bar"].joined()
print(a) // foobar
The extension method cannot be defined for struct Array
directly, but only for all types
conforming to some protocol (with optional constraints). So one
has to find a protocol to which Array
conforms and which provides all the necessary methods. In the above example, that is SequenceType
.
Another example (a variation of How do I insert an element at the correct position into a sorted array in Swift?):
extension CollectionType where Generator.Element : Comparable, Index : RandomAccessIndexType {
typealias T = Generator.Element
func insertionIndexOf(elem: T) -> Index {
var lo = self.startIndex
var hi = self.endIndex
while lo != hi {
// mid = lo + (hi - 1 - lo)/2
let mid = lo.advancedBy(lo.distanceTo(hi.predecessor())/2)
if self[mid] < elem {
lo = mid + 1
} else if elem < self[mid] {
hi = mid
} else {
return mid // found at position `mid`
}
}
return lo // not found, would be inserted at position `lo`
}
}
let ar = [1, 3, 5, 7]
let pos = ar.insertionIndexOf(6)
print(pos) // 3
Here the method is defined as an extension to CollectionType
because
subscript access to the elements is needed, and the elements are
required to be Comparable
.
-
You could switch `Index == Int` to `Index: RandomAccessIndexType` with some minor changes e.g. `startIndex` instead of `0`, `endIndex.predecessor()`, `let mid = lo.advancedBy(lo.distanceTo(hi)/2)` plus an early bail on `isEmpty`. Would mean for for example it would work with `String.UTF16View` which is random-access but not integer indexed. – Airspeed Velocity Jun 12 '15 at 04:57
-
UPDATE: Please See Martin's answer below for Swift 2.0 updates. (I can't delete this answer since it is accepted; if Doug can accept Martin's answer, I'll delete this one to avoid future confusion.)
This has come up several times in the forums, and the answer is no, you can't do this today, but they get that it's a problem and they hope to improve this in the future. There are things they would like to add to stdlib that also need this. That's why there are so many free functions is stdlib. Most of them are work-arounds for either this problem or the "no default implementation" problem (i.e. "traits" or "mixins").

- 286,113
- 34
- 456
- 610
-
2Thanks, it did seem like an odd omission. Will keep my fingers crossed. – Doug Smith Dec 08 '14 at 03:42
-
Might want to edit this one for 2.0, esp. as dupes refer to it. – Airspeed Velocity Jun 12 '15 at 04:58
This has already been answered by the three wise-men above ;-) , but I humbly offer a generalization of @Martin's answer. We can target an arbitrary class by using "marker" protocol that is only implemented on the class that we wish to target. Ie. one does not have to find a protocol per-se, but can create a trivial one for using in targeting the desired class.
protocol TargetType {}
extension Array:TargetType {}
struct Foo {
var name:String
}
extension CollectionType where Self:TargetType, Generator.Element == Foo {
func byName() -> [Foo] { return sort { l, r in l.name < r.name } }
}
let foos:[Foo] = ["c", "b", "a"].map { s in Foo(name: s) }
print(foos.byName())

- 8,889
- 5
- 52
- 68
You still haven't given a use case, despite many requests in comments, so it's hard to know what you're after. But, as I've already said in a comment (and Rob has said in an answer), you won't get it literally; extensions don't work that way (at the moment).
As I said in a comment, what I would do is wrap the array in a struct. Now the struct guards and guarantees the string's type, and we have encapsulation. Here's an example, though of course you must keep in mind that you've given no indication of the kind of thing you'd really like to do, so this might not be directly satisfying:
struct StringArrayWrapper : Printable {
private var arr : [String]
var description : String { return self.arr.description }
init(_ arr:[String]) {
self.arr = arr
}
mutating func upcase() {
self.arr = self.arr.map {$0.uppercaseString}
}
}
And here's how to call it:
let pepboys = ["Manny", "Moe", "Jack"]
var saw = StringArrayWrapper(pepboys)
saw.upcase()
println(saw)
Thus we have effectively insulated our string array into a world where we can arm it with functions that apply only to string arrays. If pepboys
were not a string array, we couldn't have wrapped it in a StringArrayWrapper to begin with.

- 515,959
- 87
- 875
- 1,141
-
If the array is likely to be big, a class might be better than a struct, so that you are guaranteed of pass-by-reference. I often wrap big arrays in classes for just this reason - so that passing them around involves no copying. – matt Dec 08 '14 at 15:25
-
Swift uses copy-on-write for pass-by-value, so in most cases there shouldn't be a major cost in passing large arrays. If you find a major cost, you should open a radar. Apple has noted specifically that you shouldn't try to dodge copying for performance reasons. See the note at the bottom of https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html – Rob Napier Dec 08 '14 at 18:33
-
@RobNapier The property here is a `var` and the example assumes that it will be written to. – matt Dec 08 '14 at 18:42