4

I want to achieve the following; being able to remove any given instance from an array. However the following is not valid Swift 3 syntax:

extension Array where Element: class { // error: Expected identifier for type name
    mutating func remove(_ object: AnyObject) {
        if let index = index(where: { $0 === object }) {
            remove(at: index)
        }
    }
}

protocol MyProtocol: class { }
class MyClass: MyProtocol { }
var myInstance = MyClass()
var myArray: [MyProtocol] = [myInstance]
myArray.remove(myInstance)

How would a generic approach work? I don't want to special-case the generic extension to MyProtocol or Equatable.

Bouke
  • 11,768
  • 7
  • 68
  • 102

2 Answers2

5

Convert your restriction to AnyObject instead of class:

extension Array where Element: AnyObject {
    mutating func remove(_ object: Element) {
        if let index = firstIndex(where: { $0 === object }) {
            remove(at: index)
        }
    }
}

The below notes are no longer true, keeping them for historical purposes.

The only problem now is that the compiler won't let you declare var myArray: [MyProtocol], as it will throw the following:

error: using 'MyProtocol' as a concrete type conforming to protocol 'AnyObject' is not supported

So you'll need to var myArray: [AnyObject]. Here is a good explanation of why this restriction currently exists in Swift.

Cristik
  • 30,989
  • 25
  • 91
  • 127
  • Thanks for your reply. So from what I understand from your response is that there currently is no way to make this happen with typed objects and collections. The compiler does allow me to write the array that way, however it can't see the remove method unless declared as [AnyObject]. – Bouke Sep 19 '16 at 20:39
  • Yes. It's odd that you are allowed to declare an array of `AnyObject`, but you are not allowed to declare an array of a protocol derived from `AnyObject`. Perhaps that's due to the fact that compiler gives preferential treatment to `AnyObject`. – Cristik Sep 19 '16 at 20:42
3

Replace the extension by this:

 extension Array { // error: Expected identifier for type name
    mutating func remove(_ object: AnyObject) {
        if let index = index(where: { $0 as AnyObject === object }) {
            remove(at: index)
        }
    }
 }
Jans
  • 11,064
  • 3
  • 37
  • 45
  • 1
    This will add the `remove` method to all `Array` instances, and I think the asker wants to restrict this method only to arrays that contain classes. – Cristik Sep 19 '16 at 18:18