Is it possible to constrain generic type to accept protocol in Swift?
I have implemented wrapper to have weak list of objects, and I need to extend that to protocols.
protocol Incrementable: class {
func inc()
}
class Counter: Incrementable {
var n: Int = 0
func inc() {
n += 1
}
}
struct Weak<T: AnyObject> {
weak var value : T?
init (value: T?)
{
self.value = value
}
}
var cnt: Counter? = Counter()
let counters : [Weak<Counter>] = [Weak(value: cnt), Weak(value: Counter())]
for counter in counters
{
counter.value?.inc()
}
Now if I want to store any object that implements Incrementable I have to use AnyObject and that is not very type safe and also includes as? casting
let counters : [Weak<AnyObject>] = [Weak(value: cnt), Weak(value: Counter())]
for counter in counters
{
(counter.value as? Incrementable)?.inc()
}
And code I would like to have would be
let counters: [Weak<Incrementable>] = [Weak(value: cnt), Weak(value: Counter())]
for counter in counters
{
counter.value?.inc()
}
Of course, above code cannot be compiled and fails with:
Using 'Incrementable' as concrete type conforming to protocol
'AnyObject' is not supported
Is it possible to write Weak wrapper so it can accept and store weak references to protocol?
While root cause of my problem is same as in Using as a concrete type conforming to protocol AnyObject is not supported that question deals with hash tables and I need solution with lists that allows duplicate entries.
Following answer pointed me in right direction and I was able to come up with following solution for implementing weak list of protocol references that allows duplicates and nil (convenience) entries.
struct Weak<T>
{
weak var value: AnyObject?
init (value: T?)
{
if value != nil
{
guard value is AnyObject else { fatalError("Object (\(value)) should be subclass of AnyObject") }
self.value = value as? AnyObject
}
}
}
class WeakList<T>: SequenceType
{
var items : [Weak<T>] = []
func add(item: T?)
{
items.append(Weak(value: item))
}
func generate() -> AnyGenerator<T>
{
var nextIndex = items.count - 1
return anyGenerator
{
while nextIndex >= 0
{
let item = self.items[nextIndex--]
if item.value != nil
{
return item.value as? T
}
}
return nil
}
}
}
let incrementables = WeakList<Incrementable>()
incrementables.add(Counter())
incrementables.add(cnt)
incrementables.add(nil)
incrementables.add(Counter())
incrementables.add(cnt)
for counter in incrementables
{
counter.inc()
}
Related
I'm trying to pass data between viewControllers, but something seems wrong.
The first viewController I want to set the "Bool" to the protocol function to be able to recover in the other screen. What am I doing wrong, I always used protocols but at this time I got in trouble.
That's how I'm doing that:
//
// ComboBoxNode.swift
//
import Foundation
import SWXMLHash
protocol ComboBoxNodeDelegate {
func getCustomOption(data:Bool)
}
class ComboBoxNode: FormControlNode, IFormControlDataSource {
var listType: String?
var dataSource: String?
var dataSourceValue: String?
var dataSourceText: String?
var hasCustomOption:Bool?
var customOptionText: String?
var ctrlDataSourceType: String?
var parameters = [ParameterNode]()
var staticList: FormControlStaticListNode?
var delegate:ComboBoxNodeDelegate?
override init(indexer: XMLIndexer) {
super.init(indexer: indexer)
guard let element = indexer.element else {
preconditionFailure("Error")
}
let isCustomOption = element.bool(by: .hasCustomOption) ?? hasCustomOption
if isCustomOption == true {
self.delegate?.getCustomOption(data: hasCustomOption!)
}
self.readFormControlDataSource(indexer: indexer)
}
override func accept<T, E: IViewVisitor>(visitor: E) -> T where E.T == T {
return visitor.visit(node: self)
}
}
That's how I'm trying to recover on next screen:
// FormPickerViewDelegate.swift
import Foundation
import ViewLib
import RxSwift
class FormPickerViewDelegate: NSObject {
var items = Variable([(value: AnyHashable, text: String)]()) {
didSet {
PickerNodeDelegate = self
self.setDefaultValues()
}
}
private var controlViewModel: FormControlViewModel
private var customText:Bool?
private var PickerNodeDelegate:ComboBoxNodeDelegate?
init(controlViewModel: FormControlViewModel) {
self.controlViewModel = controlViewModel
}
func getItemByValue(_ value: Any) -> (AnyHashable, String)? {
if value is AnyHashable {
let found = items.value.filter {$0.value == value as! AnyHashable}
if found.count >= 1 {
return found[0]
}
}
return nil
}
}
extension FormPickerViewDelegate:ComboBoxNodeDelegate {
func getCustomOption(data: Bool) {
customText = data
}
}
Instead of setting PickerNodeDelegate = self in didSet {} closure
var items = Variable([(value: AnyHashable, text: String)]()) {
didSet {
PickerNodeDelegate = self
self.setDefaultValues()
}
}
Assign it in your init() function instead
init(controlViewModel: FormControlViewModel) {
self.controlViewModel = controlViewModel
PickerNodeDelegate = self
}
Note, your should declare your delegate to be weak also, since it's a delegate, your protocol should conform to be a class type in order to be weakified.
protocol ComboBoxNodeDelegate: class
...
weak var delegate: ComboBoxNodeDelegate?
Here is an example, hope it helps!
protocol ComboBoxNodeDelegate {
func getCustomOption(data:Bool) -> String
}
class ViewOne:ComboBoxNodeDelegate {
var foo:Bool = false
var bar:String = "it works!"
/** Return: String */
func getCustomOption(data:Bool) -> String { //conform here to protocol
// do whatever you wanna do here ...example
self.foo = data // you can set
return bar // even return what you want
}
//initialize
func initalizeViewTwo() {
let v2 = ViewTwo()
v2.delegate = self //since `self` conforms to the ComboBoxNodeDelegate protcol you are allowed to set
}
}
class ViewTwo {
var delegate:ComboBoxNodeDelegate?
func getCustomOption_forV1() {
let view2_foo = delegate.getCustomOption(data:true)
print(view2_foo) // should print "it works!"
}
}
All parameters passed around in Swift are constants -- so you cannot change them.
If you want to change them in a function, you must declare your protocol to pass by reference with inout:
protocol ComboBoxNodeDelegate {
func getCustomOption(data: inout Bool)
}
Note: you cannot pass a constant (let) to this function. It must be a variable -- which I see you are doing!
protocol ParentProtocol { }
protocol ChildProtocol: ParentProtocol { }
protocol Child_With_Value_Protocol: ParentProtocol {
associatedType Value
func retrieveValue() -> Value
}
Attempting to create a single array of type ParentProtocol that contains both ChildProtocol and Child_With_Value_Protocol. Is there any possible way to create a function that loops through the heterogeneous array and returns the values of just type Child_With_Value_Protocol?
This may require an architecture change. Open to all solutions.
Attempted Failed Solution #1
var parents: [ParentProtocol] = [...both ChildProtocol & Child_With_Value_Protocol...]
func retrieveValues() -> [Any] {
var values = [Any]()
for parent in parents {
if let childWithValue = parent as? Child_With_Value_Protocol { // Fails to compile
values.append(childWithValue.retrieveValue())
}
}
return values
}
This fails with an error of protocol 'Child_With_Value_Protocol' can only be used as a generic constraint because it has Self or associated type requirements which makes sense since the compiler would not know the type when converted to just Child_With_Value_Protocol, this leads to the next failed solution.
Attempted Failed Solution #2
If the array was a homogeneous array of just Child_With_Value_Protocol, type erasing could be used to retrieve the values.
var parents: [ParentProtocol] = [...both ChildProtocol & Child_With_Value_Protocol...]
struct AnyValue {
init<T: Child_With_Value_Protocol>(_ protocol: T) {
_retrieveValue = protocol.retrieveValue as () -> Any
}
func retrieveValue() -> Any { return _retrieveValue() }
let _retrieveValue: () -> Any
}
func retrieveValues() -> [Any] {
var values = [Any]()
for parent in parents {
values.append(AnyValue(parent).retrieveValue()) // Fails to compile
}
return values
}
This fails to compile due to the fact that the struct AnyValue has no initializer for the ParentProtocol.
Attempted Failed Solution #3
struct AnyValue {
init<T: Child_With_Value_Protocol>(_ protocol: T) {
_retrieveValue = protocol.retrieveValue as () -> Any
}
func retrieveValue() -> Any { return _retrieveValue() }
let _retrieveValue: () -> Any
}
var erased: [AnyValue] = [AnyValue(...), AnyValue(...), AnyValue(...)]
func retrieveValues() -> [Any] {
var values = [Any]()
for value in erased {
values.append(value.retrieveValue())
}
return values
}
Unlike the other solutions, this solution actually compiles. Problem with this solution resides in the fact that the array erased can only hold values of the type-erased versions of Child_With_Value_Protocol. The goal is for the array to hold types of both Child_With_Value_Protocol and ChildProtocol.
Attempted Failed Solution #4
Modifying the type-erase struct to include an initializer for ParentProtocol still creates a solution that compiles, but then the struct will only use the less specific init, instead of the more specific init.
struct AnyValue {
init?<T: ParentProtocol>(_ protocol: T) {
return nil
}
init?<T: Child_With_Value_Protocol>(_ protocol: T) {
_retrieveValue = protocol.retrieveValue as () -> Any
}
func retrieveValue() -> Any { return _retrieveValue() }
let _retrieveValue: (() -> Any)?
}
The prior comments are likely right. Nevertheless, you could box the variants in an enum and create an array of those. The reference would then switch on the enum value, each having associated data of the right type
EDIT: I didn't bother with the associatedValue, because it seems irrelevant to the question being asked. The following works in a playground:
protocol ParentProtocol: CustomStringConvertible {
static func retrieveValues(parents: [FamilyBox]) -> [ParentProtocol]
}
protocol ChildProtocol: ParentProtocol { }
protocol Other_Child_Protocol: ParentProtocol { }
enum FamilyBox {
case Parent(parent: ParentProtocol)
case Child(child: ChildProtocol)
case OtherChildProtocol(withValue: Other_Child_Protocol)
}
var parents: [FamilyBox] = []
struct P: ParentProtocol {
var description: String { return "Parent" }
static func retrieveValues(parents: [FamilyBox]) -> [ParentProtocol] {
var values = [ParentProtocol]()
for parent in parents {
switch parent {
case .Parent(let elementValue):
values.append(elementValue)
default:
break;
}
}
return values
}
}
struct C: ChildProtocol {
var description: String { return "Child" }
static func retrieveValues(parents: [FamilyBox]) -> [ParentProtocol] {
var values = [ParentProtocol]()
for parent in parents {
switch parent {
case .Child(let elementValue):
values.append(elementValue)
default:
break;
}
}
return values
}
}
struct CV: Other_Child_Protocol {
var description: String { return "Other Child" }
static func retrieveValues(parents: [FamilyBox]) -> [ParentProtocol] {
var values = [ParentProtocol]()
for parent in parents {
switch parent {
case .OtherChildProtocol(let elementValue):
values.append(elementValue)
default:
break;
}
}
return values
}
}
let p = FamilyBox.Parent(parent: P())
let c = FamilyBox.Child(child: C())
let cv = FamilyBox.OtherChildProtocol(withValue: CV())
let array:[FamilyBox] = [p, c, cv]
print(P.retrieveValues(array))
print(C.retrieveValues(array))
print(CV.retrieveValues(array))
The prints from the last three lines are:
[Parent]
[Child]
[Other Child]
While I'm sure it can be improved, I think that meets the original intent. No?
protocol Decodable {
init?(data: [String: AnyObject])
}
struct A: Decodable {
var data: [String: AnyObject]!
init?(data: [String: AnyObject]) {
self.data = data
}
}
This works when i want to create an object
let d = ["name":"Rahul"]
let a = A(data: d)
I am trying to achieve the following but it is giving errors at the compile time.
let dArray = [["name":"Rahul"],["name":"Rahul"],["name":"Rahul"]]
let aArray = [A](data: dArray)
The following code is giving me error 'nil is the only return value permitted in an initializer'.
public extension CollectionType where Generator.Element: Decodable {
init?(data: [[String: AnyObject]]) {
var elements: [Generator.Element] = []
for d in data {
let element = Generator.Element(data: d)
if let element = element {
element.append(element)
}
}
return elements
}
}
=================================
Answer : -
public extension Array where Element: Decodable {
init?(data: [String: AnyObject]) {
var elements: [Element] = []
for d in data {
let element = Element(data: d)
if let element = element {
element.append(element)
}
}
self = elements
}
}
This will allow you to initialise using the following code
let dArray = [["name":"Rahul"],["name":"Rahul"],["name":"Rahul"]]
let aArray = [A](data: dArray)
Your error is because CollectionType is a protocol, which cannot be initialised.
Have you tried to create a helper class method to return you the collection, e.g.
func collectionWithData(data: [[String: AnyObject]]) -> [Generator.Element] {
let's define a protocol
protocol P {
init?(b: Bool)
}
as mentioned by Oliver, protocol cannot be initialized, but we still are able to define init in protocol extension
extension P {
init?(b: Bool) {
print("init defined in extension")
if b == false { return nil }
self.init(b: b)
}
}
see, that you cannot return anything from init but nil (if initialization failed)
let's define a class which conforms to our protocol
class C: P {
var b: Bool
required init(b: Bool) {
self.b = b
}
}
and see what happens in next snippet
let c1: C? = C(b: false)
let c2 = C(b: false)
dump(c1)
dump(c2)
/*
init defined in extension
- nil
▿ C #0
- b: false
*/
the same expression C(b: false) give us two different results :-). So, be careful if you define init and init? via protocol extension. If you try to define init and init? with the same parameters without protocol extension, the compiler will complain.
I m trying to implement Queue collection type in Swift platform. I have got some problems about peek, poll and offer functions. When I try to use these functions in my code, it fails. Do you have any advice or true algorithm for that?
import Foundation
class Node<T> {
var value: T? = nil
var next: Node<T>? = nil
var prev: Node<T>? = nil
init() {
}
init(value: T) {
self.value = value
}
}
class Queue<T> {
var count: Int = 0
var head: Node<T> = Node<T>()
var tail: Node<T> = Node<T>()
var currentNode : Node<T> = Node<T>()
init() {
}
func isEmpty() -> Bool {
return self.count == 0
}
func next(index:Int) -> T? {
if isEmpty() {
return nil
} else if self.count == 1 {
var temp: Node<T> = currentNode
return temp.value
} else if index == self.count{
return currentNode.value
}else {
var temp: Node<T> = currentNode
currentNode = currentNode.next!
return temp.value
}
}
func setCurrentNode(){
currentNode = head
}
func enQueue(key: T) {
var node = Node<T>(value: key)
if self.isEmpty() {
self.head = node
self.tail = node
} else {
node.next = self.head
self.head.prev = node
self.head = node
}
self.count++
}
func deQueue() -> T? {
if self.isEmpty() {
return nil
} else if self.count == 1 {
var temp: Node<T> = self.tail
self.count--
return temp.value
} else {
var temp: Node<T> = self.tail
self.tail = self.tail.prev!
self.count--
return temp.value
}
}
//retrieve the top most item
func peek() -> T? {
if isEmpty() {
return nil
}
return head.value!
}
func poll() -> T? {
if isEmpty() {
return nil
}else{
var temp:T = head.value!
deQueue()
return temp
}
}
func offer(var key:T)->Bool{
var status:Bool = false;
self.enQueue(key)
status = true
return status
}
}
Aside from the bugs, there are a couple of things about your implementation that you probably want to change to make it more Swift-like. One is it looks like you're replicating the Java names like poll and offer – these names are (IMHO) a little strange, and partly related to needing to have two functions, an exception-throwing version and a non-exception version. Since Swift doesn't have exceptions, you can probably just name them using the conventional names other Swift collections use, like append.
The other issue is that your implementation incorporates traversing the queue into the queue itself. It's better to do this kind of traversal outside the collection than mix the two. Swift collections do this with indexes.
Here's a possible Swift-like queue implementation. First, the node and base queue definition:
// singly rather than doubly linked list implementation
// private, as users of Queue never use this directly
private final class QueueNode<T> {
// note, not optional – every node has a value
var value: T
// but the last node doesn't have a next
var next: QueueNode<T>? = nil
init(value: T) { self.value = value }
}
// Ideally, Queue would be a struct with value semantics but
// I'll leave that for now
public final class Queue<T> {
// note, these are both optionals, to handle
// an empty queue
private var head: QueueNode<T>? = nil
private var tail: QueueNode<T>? = nil
public init() { }
}
Then, extend with an append and dequeue method:
extension Queue {
// append is the standard name in Swift for this operation
public func append(newElement: T) {
let oldTail = tail
self.tail = QueueNode(value: newElement)
if head == nil { head = tail }
else { oldTail?.next = self.tail }
}
public func dequeue() -> T? {
if let head = self.head {
self.head = head.next
if head.next == nil { tail = nil }
return head.value
}
else {
return nil
}
}
}
At this point, you're almost done if all you want to do is add and remove. To add traversal, first create an index type, which is a simple wrapper on the node type:
public struct QueueIndex<T>: ForwardIndexType {
private let node: QueueNode<T>?
public func successor() -> QueueIndex<T> {
return QueueIndex(node: node?.next)
}
}
public func ==<T>(lhs: QueueIndex<T>, rhs: QueueIndex<T>) -> Bool {
return lhs.node === rhs.node
}
Then, use this index to conform to MutableCollectionType:
extension Queue: MutableCollectionType {
public typealias Index = QueueIndex<T>
public var startIndex: Index { return Index(node: head) }
public var endIndex: Index { return Index(node: nil) }
public subscript(idx: Index) -> T {
get {
precondition(idx.node != nil, "Attempt to subscript out of bounds")
return idx.node!.value
}
set(newValue) {
precondition(idx.node != nil, "Attempt to subscript out of bounds")
idx.node!.value = newValue
}
}
typealias Generator = IndexingGenerator<Queue>
public func generate() -> Generator {
return Generator(self)
}
}
From conforming to collection type, you get a whole load of stuff for free:
var q = Queue<String>()
q.append("one")
q.append("two")
for x in q {
println(x)
}
isEmpty(q) // returns false
first(q) // returns Optional("one")
count(q) // returns 2
",".join(q) // returns "one,two"
let x = find(q, "two") // returns index of second entry
let counts = map(q) { count($0) } // returns [3,3]
Finally, there's 3 more protocols that are good to conform to: ExtensibleCollectionType, Printable and ArrayLiteralConvertible:
// init() and append() requirements are already covered
extension Queue: ExtensibleCollectionType {
public func reserveCapacity(n: Index.Distance) {
// do nothing
}
public func extend<S : SequenceType where S.Generator.Element == T>
(newElements: S) {
for x in newElements {
self.append(x)
}
}
}
extension Queue: ArrayLiteralConvertible {
public convenience init(arrayLiteral elements: T...) {
self.init()
// conformance to ExtensibleCollectionType makes this easy
self.extend(elements)
}
}
extension Queue: Printable {
// pretty easy given conformance to CollectionType
public var description: String {
return "[" + ", ".join(map(self,toString)) + "]"
}
}
These mean you can now create queues as easily arrays or sets:
var q: Queue = [1,2,3]
println(q) // prints [1, 2, 3]
There are a lot of little issues regarding the internal consistency of your model:
When you first instantiate a new Queue, you are initializing head, tail and current to three different Node objects (even though nothing's been queued yet!). That doesn't make sense. Personally, I'd be inclined to make those three properties optional and leave them as nil until you start enqueuing stuff.
By the way, when you start using optionals for these properties, many of the other methods are simplified.
It looks like you're trying to implement a doubly linked list. So, when you dequeue, you need to not only update the Queue properties, but you also need to update the next pointer for the next item that will be dequeued (because it still will be pointing to that item you already dequeued). You don't want your linked list maintaining references to objects that have been dequeued and should be removed.
When you dequeue the last item, you really should be clearing out head and tail references.
You're implementing a doubly linked list, without regard to the object ownership model. Thus, as soon as you have more than one item in your list, you've got a strong reference cycle between nodes and if not remedied, this will leak if there are still objects in the queue when the queue, itself, is deallocated. Consider making one of the references weak or unowned.
I'd suggest keeping this simple (just enqueue and dequeue). The concept of poll and offer may make sense in terms of an arbitrary linked list, but not in the context of a queue. The implementations of poll and offer are also incorrect (e.g. poll calls deQueue which removes the tail, but the object you return is the head!), but I presume you'd just remove these functions altogether. Likewise, I do not understand the intent of current in the context of a queue.
I'd suggest you make Queue and Node conform to Printable. It will simplify your debugging process.
The following is code of a playground consisting of a queue implemented with an array and a queue implemented with nodes. There are substantial performance differences between the two but if you going to be iterating through a queue you might want to use one with an array.
import UIKit // for NSDate() used in testing)
// QUEUE WITH ARRAY IMPLEMENTATION (For ease of adaptibility, slow enque, faster deque):
struct QueueArray<T> {
private var items = [T]()
mutating func enQueue(item: T) {
items.append(item)
}
mutating func deQueue() -> T? {
return items.removeFirst()
}
func isEmpty() -> Bool {
return items.isEmpty
}
func peek() -> T? {
return items.first
}
}
// QUEUE WITH NODE IMPLEMENTATION (For performance, if all you need is a queue this is it):
class QNode<T> {
var value: T
var next: QNode?
init(item:T) {
value = item
}
}
struct Queue<T> {
private var top: QNode<T>!
private var bottom: QNode<T>!
init() {
top = nil
bottom = nil
}
mutating func enQueue(item: T) {
let newNode:QNode<T> = QNode(item: item)
if top == nil {
top = newNode
bottom = top
return
}
bottom.next = newNode
bottom = newNode
}
mutating func deQueue() -> T? {
let topItem: T? = top?.value
if topItem == nil {
return nil
}
if let nextItem = top.next {
top = nextItem
} else {
top = nil
bottom = nil
}
return topItem
}
func isEmpty() -> Bool {
return top == nil ? true : false
}
func peek() -> T? {
return top?.value
}
}
// QUEUE NODES TEST
let testAmount = 100
var queueNodes = Queue<Int>()
let queueNodesEnqueStart = NSDate()
for i in 0...testAmount {
queueNodes.enQueue(i)
}
let queueNodesEnqueEnd = NSDate()
while !queueNodes.isEmpty() {
queueNodes.deQueue()
}
let queueNodesDequeEnd = NSDate()
// QUEUE ARRAY TEST
var queueArray = QueueArray<Int>()
let queueArrayEnqueStart = NSDate()
for i in 0...testAmount {
queueArray.enQueue(i)
}
let queueArrayEnqueEnd = NSDate()
while !queueArray.isEmpty() {
queueArray.deQueue()
}
let queueArrayDequeEnd = NSDate()
// QUEUE NODES RESULT:
print("queueEnqueDuration: \(queueNodesEnqueEnd.timeIntervalSinceDate(queueNodesEnqueStart)), Deque: \(queueNodesDequeEnd.timeIntervalSinceDate(queueNodesEnqueEnd))")
// QUEUE ARRAY RESULT:
print("queueArrayEnqueDuration: \(queueArrayEnqueEnd.timeIntervalSinceDate(queueArrayEnqueStart)), Deque: \(queueArrayDequeEnd.timeIntervalSinceDate(queueArrayEnqueEnd))")
Queue with Array
struct Queue<T> {
private var list = [T]()
var isEmpty: Bool { return self.list.isEmpty }
var front: T? { return self.list.first }
mutating func enqueue(_ item: T) {
self.list.append(item)
}
mutating func dequeue() -> T? {
guard self.isEmpty == false else { return nil }
return self.list.removeFirst()
}
}
Swift 4 simple Stack for any type; string, int, array, etc.
struct Stack<Element> {
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
mutating func peek() -> Element {
return items.last!
}
mutating func pushFirst(_ item: Element) {
items.insert(item, at: 0)
}
}
example with strings:
let names = ["Bob", "Sam", "Sue", "Greg", "Brian", "Dave"]
//create stack of string type
var stackOfStrings = Stack<String>()
//add to bottom of stack
for stringName in names {
stackOfStrings.push(stringName)
}
//print and remove from stack
for stringName in names {
print(stringName)
stackOfStrings.pop(stringName)
}
//add to top of stack
for stringName in names {
stackOfStrings.pushFirst(stringName)
}
//look at item in stack without pop
for stringName in names {
//see what Top item is without remove
let whatIsTopItem = stackOfStrings.peek(stringName)
if whatIsTopItem == "Bob" {
print("Best friend Bob is in town!")
}
}
//stack size
let stackCount = stackOfStrings.items.count
more info here:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Generics.html
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)'
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)
}
}
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 }
}
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 })
}
}