Search code examples
iosswiftdelegates

Find delegate in a swift Array of delegates


I want to check if I already have a delegate in my removeDelegate method before removing. How do I do that?

Here's what I've got so far:

protocol LocationManagerDelegate {
    func locationManagerDidUpdateLocation(
        oldLocation: CLLocationCoordinate2D,
        currentLocation: CLLocationCoordinate2D
    )
}

class LocationManager: NSObject {
    private var _delegates = [LocationManagerDelegate]()

    func removeDelegate(delegate:LocationManagerDelegate) {
        if contains(_delegates, delegate) {
            // Remove delegate
        }
    }
}

However, this gives me the following error on the 'if contains' line:

cannot invoke 'contains' with an argument list of type '(@lvalue Array< LocationManagerDelegate >!, LocationManagerDelegate)'


Solution

  • Update for Swift 4.2:

    Assuming that the delegates are actually instances of a class, you could require that in the protocol by "inheriting" from "class":

    protocol LocationManagerDelegate: class {
        // ...
    }
    

    and then use the firstIndex(where:) method, using the "identity operator ===:

    class LocationManager: NSObject {
        private var _delegates = [LocationManagerDelegate]()
        
        func removeDelegate(delegate:LocationManagerDelegate) {
            if let index = _delegates.firstIndex(where: { $0 === delegate }) {
                _delegates.remove(at: index)
            }
        }
    }
    

    Old answer (Swift 1):

    There are two slightly different contains() functions:

    func contains<S : SequenceType where S.Generator.Element : Equatable>(seq: S, x: S.Generator.Element) -> Bool
    
    func contains<S : SequenceType, L : BooleanType>(seq: S, predicate: (S.Generator.Element) -> L) -> Bool
    

    You are using the first one, which requires that the sequence elements conform to the Equatable protocol, i.e. they can be compared with ==.

    Assuming that the delegates are actually instances of a class, you could require that in the protocol by "inheriting" from "class":

    protocol LocationManagerDelegate : class {
        // ...
    }
    

    and then use the second, predicate-based version of contains() with the identity operator ===:

    func removeDelegate(delegate:LocationManagerDelegate) {
        if contains(_delegates, { $0 === delegate }) {
            // Remove delegate
        }
    }
    

    To remove the object from the array you'll have to get its index, so you might use the findIdenticalObject() function from https://stackoverflow.com/a/25543084/1187415:

    func findIdenticalObject<T : AnyObject>(array: [T], value: T) -> Int? {
        for (index, elem) in enumerate(array) {
            if elem === value {
                return index
            }
        }
        return nil
    }
    

    and then find and remove from the array with

    func removeDelegate(delegate:LocationManagerDelegate) {
        if let index = findIdenticalObject(_delegates, delegate) {
            _delegates.removeAtIndex(index)
        }
    }