Find delegate in a swift Array of delegates - ios

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

Related

Access function protocol in generic class

I want to access function in protocol, but XCode complaint
Instance member 'createColumns' cannot be used on type 'T'; did you
mean to use a value of this type instead?
What I have done:
Create protocol:
protocol StorageModelDelegate {
func createColumns(for tableBuilder: TableBuilder)
}
Create class generic that receive StorageModelDelegate:
class SQLiteStorage<T: StorageModelDelegate> {
func createTable(tableName: TableKey) -> Bool {
let table = Table(tableName.rawValue)
let query = table.create(ifNotExists: true) { (builder: TableBuilder) in
T.createColumns(for: builder) // -> this is the error comes up.
}
}
}
Create class that implement SQLiteStorage:
final class InfoStorageModel {
private let sqlite: SQLiteStorage = SQLiteStorage<Info>()
}
so, how to fix the error in SQLiteStorage class?
The error indicates that you need an instance of T, not the type itself.
So you need something like:
class SQLiteStorage<T: StorageModelDelegate> {
var delegate:T
init (delegate:T) {
self.delegate = delegate
}
func createTable(tableName: TableKey) -> Bool {
let table = Table(tableName.rawValue)
let query = table.create(ifNotExists: true) { (builder: TableBuilder) in
self.delegate.createColumns(for: builder) // -> this is the error comes up.
}
}
}
You want to call static method instead of instance method.
In order to fix, you should add instance parameter:
First of all, use weak var delegate in order to prevent retain cycles.
protocol StorageModelDelegate: class {
func createColumns(for tableBuilder: TableBuilder)
}
final class SQLiteStorage<T: StorageModelDelegate> {
weak var delegate: T?
func createTable(tableName: TableKey) -> Bool {
let table = Table(tableName.rawValue)
let query = table.create(ifNotExists: true) { (builder: TableBuilder) in
delegate?.createColumns(for: builder)
}
}
}
Or use static protocol methods:
protocol StorageModelDelegate {
static func createColumns(for tableBuilder: TableBuilder)
}
final class SQLiteStorage<T: StorageModelDelegate> {
weak var delegate: T?
func createTable(tableName: TableKey) -> Bool {
let table = Table(tableName.rawValue)
let query = table.create(ifNotExists: true) { (builder: TableBuilder) in
T.createColumns(for: builder)
}
}
}

Swift switch between generics type and protocol conformance

I want to reach this goal:
func parse<T>(element: Any?) -> [T] {
// if T is kind of MyProtocol, return get result
// else
let array = [T]()
//do some stuff
return array
}
func get<T: MyProtocol>(obj: Any?) -> [T] {
return //some other stuffs
}
Is it possible in Swift language?
EDIT:
I have a class, let's say Parser, with some properties. I want a unique function signature, but the executed code must vary in base of property type.
class Parser: ParserProtocol {
let property1 : [MyClass1] = parse(element: elem1)
let property2 : [MyClass2] = parse(element: elem2)
}
protocol ParserProtocol {
func parse<T>(element: Any?) -> [T]
}
is this something you could use?
protocol GenericsTypeProtocol {
func callParseLogic() -> Void
}
protocol MyProtocol : GenericsTypeProtocol {}
extension GenericsTypeProtocol {
func callParseLogic() -> Void {
print("Generic logic called")
}
}
extension GenericsTypeProtocol where Self : MyProtocol {
func callParseLogic() -> Void {
print("MyProtocol logic called")
}
}
class GenericClass : GenericsTypeProtocol { }
class MyClass : MyProtocol { }
class MixedClass : GenericsTypeProtocol, MyProtocol {}
let a = GenericClass()
a.callParseLogic() //prints: Generic logic called
let b = MyClass()
b.callParseLogic() //prints: MyProtocol logic called
let c = MixedClass()
c.callParseLogic() //prints: MyProtocol logic called

Extend #objc protocol with Comparable in Swift

I am trying to extend my protocol Option with Comparable to use simple .sort() method.
Below short example only with Equatable to show errors.
#objc protocol Option: Equatable {
var title: String { get }
var enabled: Bool { get }
var position: Int { get }
}
func ==(lhs: Option, rhs: Option) -> Bool {
return lhs.position == rhs.position
}
The Option protocol must be marked as #objc or inherit from NSObjectProtocol because it will be used with UIKit.
Errors:
#objc protocol 'Option' cannot refine non-#objc protocol
'Equatable'
Protocol 'Option' can only be used as a generic constraint
because it has Self or associated type requirements
Do you have any suggestion how to solve this problem?
Equatable lives in the Swift world only, thus you cannot extend it to a protocol that will be used by Objective-C. Trying to do this results in error #1
Protocols that have a Self requirement (i.e. at least one method from the protocol declaration contains Self) cannot be used as arguments to functions, or to variable declarations, only as arguments to a generic clause, e.g. func doSomething<T: Option>(argument: T).
Removing Equatable from the Option protocol declaration, and declaring == as generic on Option will solve the compile errors. As for sorting, you can also overload the < operator, and sort via that operator (without needing to implement Comparable):
#objc protocol Option {
var title: String { get }
var enabled: Bool { get }
var position: Int { get }
}
func ==<T: Option>(lhs: T, rhs: T) -> Bool {
return lhs.position == rhs.position
}
func <<T: Option>(lhs: T, rhs: T) -> Bool {
return lhs.position < rhs.position
}
This allows you to pass objects that conform to the protocol to UIKit, and to also compare them within your swift code.
class A: NSObject, Option { .. }
class B: NSObject, Option { ... }
let a = A()
let b = B()
a == b // compiles, and returns true if a and b have the same position
let c: [Option] = [a, b]
c.sort(<) // returns a sorted array by the `position` field
One important note regarding the sorting code above: if you don't specify the type for c, then the compiler infers its type as [NSObject], and the sort call will not compile due to ambiguity of the < operator. You need to explicitly declare c as [Option] to take advantage of the overloaded operator.
The issue can be fixed by the new protocol oriented programming features introduced in swift 2.0
#objc protocol 'Option' cannot refine non-#objc protocol 'Equatable'
As the error states, the Equatable protocol is a swift protocol that you can't to Obj C context
Protocol 'Option' can only be used as a generic constraint because it
has Self or associated type requirements
You can achieve this in the following way:
#objc protocol Option {
var title: String { get }
var enabled: Bool { get }
var position: Int { get }
}
extension Equatable where Self : Option
{
}
extension Comparable where Self : Option
{
}
func ==(lhs: Option, rhs: Option) -> Bool
{
return lhs.position == rhs.position
}
func <(lhs: Option, rhs: Option) -> Bool
{
return lhs.position < rhs.position
}
func >(lhs: Option, rhs: Option) -> Bool
{
return lhs.position > rhs.position
}
And your class and implementation looks like:
class MyClass: Option
{
#objc var title: String = ""
#objc var enabled: Bool = true
#objc var position: Int = 0
init()
{
}
convenience init(title : String, enabled : Bool, position: Int)
{
self.init()
self.title = title
self.enabled = enabled
self.position = position
}
}
let firstObj = MyClass()
let secondObj = MyClass()
let optionArray : [Option] = [firstObj, secondObj]
// Sort array of options
optionArray.sort(<)

Type "MyClass" does not conform to protocol "Collection_Delegate"

I have a class MyClass implementing a generic function of a delegate Collection_Delegate.
My classes Collection and Item are superclasses for some specific classes
protocol Collection_Delegate {
func onFetchAllCompleted<T, U where T: Collection<U>, U: Item>(collection: T, error: String?)
}
class Collection<T>: Item {
private var items: [T]
override init (communicator: CG_API_Communicator) {
items = [T]()
super.init(communicator: communicator)
}
internal func fetchAll() {
fatalError(notImplemented)
}
internal func onFetchAllCompleted(error: String?, json: JSON?) {
fatalError(notImplemented)
}
internal func appendItem(item: T) {
self.items.append(item)
}
internal func getItems() -> [T] {
return self.items
}
}
class Item {
var itemDataRaw: JSON?
func toString() -> String? {
var retval: String?
if let value: String = itemDataRaw?.rawString(encoding: NSUTF8StringEncoding) {
retval = value
} else {
retval = "Something went badly wrong"
}
return retval
}
}
Now in some subclasses of Collection I want to call the generic onFetAllCompleted function of the delegate avery subclass has. But the class implementing the Collection_Delegate protocol causing compiler errors
class MyClass: Collection_Delegate { // Error
func onFetchAllCompleted<T, U where T: Collection<U>, U: Item>(collection: T, error: String?){
println("MyClass.onFetchAllCompleted:\(_stdlib_getTypeName(collection))") // This displays the right class name of the subclasses
let item: Item = collection.getItems()[0] //Error
let string = item.toString()
}
}
Here we go. The class **MyClass* gets the error
Type "MyClass" does not conform to protocol "Collection_Delegate"
Within the generic function I get the error
'U' is not convertible to 'Item'
So what am I doing wrong? Why doesn't the generic stuff work?
I think you over complicated things a bit with your generic function declaration. If I understood you correctly your onFetchAllCompleted function takes parameter T which is Collection of U's and U is an Item. If that is correct the above expression can be simplified like this : onFetchAllCompleted function takes parameter T which is Collection of Items. So your protocol and class should look like this
protocol Collection_Delegate {
func onFetchAllCompleted<T: Collection<Item>>(collection: T, error: String?)
}
class MyClass: Collection_Delegate {
func onFetchAllCompleted<T: Collection<Item>>(collection: T, error: String?){
println("MyClass.onFetchAllCompleted:\(_stdlib_getTypeName(collection))") // This displays the right class name of the subclasses
let item: Item = collection.getItems()[0] //Error
let string = item.toString()
}
}
Let me know if this helped you

Generic class inheritance in Swift

I have the following class:
class BaseCache<T: Equatable>: NSObject {
var allEntities = [T]()
// MARK: - Append
func appendEntities(newEntities: [T]) {
....
}
}
Now I want to subclass it, but I get annoying error, that my type "does not conform to protocol 'Equatable'":
It seems generics in Swift are real pain-in-the-ass.
Your class definition of TrackingCache is wrong. It repeats the generic parameter:
class TrackingCache<AftershipTracking>: BaseCache<AftershipTracking> { }
It should be left out:
class TrackingCache: BaseCache<AftershipTracking> { }
This triggers the underlying swift error Classes derived from generic classes must also be generic. You can work around this issue by specifying a type parameter that is required to be or inherit from AftershipTracking:
class TrackingCache<T: AftershipTracking>: BaseCache<AftershipTracking> { }
Full example:
class BaseCache<T: Equatable>: NSObject {
var items: [T] = []
func appendItems( items: [T]) {
self.items += items
didAppendItems()
}
func didAppendItems() {} // for overriding
}
class AftershipTracking: NSObject {
var identifier: Int
init( identifier: Int) {
self.identifier = identifier
super.init()
}
}
extension AftershipTracking: Equatable { }
func ==( lhs: AftershipTracking, rhs: AftershipTracking) -> Bool {
return lhs.identifier == rhs.identifier
}
class TrackingCache<T: AftershipTracking>: BaseCache<AftershipTracking> {
override func didAppendItems() {
// do something
}
}
let a = TrackingCache<AftershipTracking>()
let b = TrackingCache<AftershipTracking>()
a.appendItems( [AftershipTracking( identifier: 1)])
b.appendItems( [AftershipTracking( identifier: 1)])
let result = a.items == b.items // true
this should work: < swift 4 >
class TrackingCache<T: AftershipTracking>: BaseCache<T>
Another example:
protocol P {
}
class C: P {
}
class CS: C {
}
class L<T:P> {
let c: T
init(_ c: T) {
self.c = c
}
}
class LS<T:CS>:L<T> {
}
let i = LS(CS())
i.c
c is CS now.

Resources