Find delegate in a swift Array of delegates
Asked Answered
M

3

6

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)'

Mackinnon answered 17/12, 2014 at 19:1 Comment(0)
H
16

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://mcmap.net/q/1631944/-swift-array-find-by-identity:

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)
    }
}
Herr answered 17/12, 2014 at 19:39 Comment(4)
It seems weird to require the predicate-based syntax for this. Is there a good reason it doesn't fall back to the identity operator by default? (Like [NSObject -isEqual:] does)?Lifework
@AaronBrager: I tried that first by requiring LocationManagerDelegate : NSObjectProtocol, but it did not work, NSObjectProtocol does not conform to Equatable. But there may be a better solution.Herr
In swift 5.1, index(where:) is deprecated, so use firstIndex(where:) instead.Wolsky
@Vijayendra: Thanks for the notice, I have updated the code accordingly.Herr
A
2

The arguments to contains must implement the Equatable protocol since it is defined as:

public func contains<T:Equatable>(left:[T], right:T) -> Bool

Since there's no way to indicate that LocationManagerDelegate implements Equatable, I don't think you can use it. The obvious attempt would be:

protocol LocationManagerDelegate : Equatable {
    ...
}

But that will fail when you try to declare the array because Equatable uses Self.

The best option I can come up with is:

func removeDelegate(delegate:LocationManagerDelegate) {
    _delegates = filter(_delegates) { return $0 !== delegate }
}
Adalai answered 17/12, 2014 at 19:49 Comment(1)
Heh. I just spent about ten minutes coming up with the same filter(). That'll teach me to read all the answers before I get distracted...Civilized
M
0
protocol LocationManagerDelegate {
    // ...
    var index_delegate:Int?{get set}
}



class LocationManager {
    private var delegates:[LocationManagerDelegate] = []

    func add(delegate: LocationManagerDelegate?){
        if let d = delegate {
            self.delegates.append(d)
            let index = self.delegates.count - 1
            self.delegates[index].index_delegate = index
            }
    }

    func remove(delegate: LocationManagerDelegate) {
        delegates = delegates.filter({ return $0.index_delegate != delegate.index_delegate })
    }

}
Martica answered 11/7, 2018 at 22:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.