I'm looking for a way to add a default initializer to a protocol via protocol extensions.
My protocol is:
protocol TestProtocol {
var myVar : Double { get set }
init(value: Double)
init(existingStruct : TestProtocol)
}
I've implemented a struct using this protocol as:
struct TestStruct : TestProtocol {
var myVar : Double
init(value : Double) {
myVar = value
}
init (existingStruct : TestProtocol) {
myVar = existingStruct.myVar
}
}
However if I try via extension to make a default initializer for this protocol I run into self issues:
extension TestProtocol {
init(value : Double) {
myVar = value
}
init(existingStruct : TestProtocol) {
myVar = existingStruct.myVar
}
}
Where both assignment lines issue the error
Variable 'self' passed by reference before being initialized
Is there a way to make this work - or am i limited to using classes?
Your question is almost the same as in this post I answered yesterday.
Here is the trick to solve this :)
protocol TestProtocol {
var myVar : Double { get set }
init() // designated initializer which will ensure that your class or structer type will instantiate correctly
}
struct TestStruct : TestProtocol {
var myVar : Double
init() {
myVar = 0
}
}
extension TestProtocol {
init(value : Double) {
self.init()
myVar = value
}
init(existingStruct : TestProtocol) {
self.init()
myVar = existingStruct.myVar
}
}
Have a good day. :) Protocol extension is so nice.
Related
I know that you can give a default value with a protocol extension like this
protocol SomeProtocol {
var prop: String { get }
}
extension SomeProtocol {
var prop: String {
return "defaultValue"
}
}
struct SomeA: SomeProtocol {}
struct SomeB: SomeProtocol {}
let a = SomeA()
let b = SomeB()
debugPrint(a.prop) // prints defaultValue
debugPrint(b.prop) // prints defaultValue
but is there a way to have different default value for different implementations of the protocol like this without implementing the property for every class or struct that conforms to this protocol?
debugPrint(a.prop) // prints defaultValue
debugPrint(b.prop) // prints differentDefaultValue
or some similar pattern for doing something like this?
Protocol inheritance.
protocol 😺: SomeProtocol { }
extension 😺 {
var prop: String { "😺" }
}
struct SomeA: SomeProtocol { }
struct SomeB: 😺 { }
struct SomeC: 😺 { }
SomeA().prop // "defaultValue"
SomeB().prop // "😺"
SomeC().prop // "😺"
Let's say I have two non-generic protocols (1)
protocol StringValue {
var asString: String {get}
}
protocol StringProvider {
var value: StringValue {get}
}
I want to have a generic version of the second one (2)
protocol TypedStringProvider: StringProvider { // inherits from StringProvider
associatedtype TypedStringValue: StringValue
var typedValue: TypedStringValue { get }
}
And extension with default implementation of non-generic version to have a code free conformance to StringProvider (doesn't work, pls read below) (3)
extension TypedStringProvider {
var value: TypedStringValue { return typedValue }
}
Now I want several classes to conform both generic TypedStringProvider and non-generic StringProvider protocols (4)
extension UIView: TypedStringProvider {
typealias TypedStringValue = String
var typedValue: String { return "Some String" }
}
extension Double: TypedStringProvider {
typealias TypedStringValue = String
var typedValue: String { return String(self) }
}
extension String: StringValue {
var asString: String { return self }
}
And get compiler error: Type 'UIView' does not conform to protocol 'StringProvider'.
Seemingly extension (3) doesn't work like I want because TypedStringValue is not a StringValue in despite of this constraint associatedtype TypedStringValue: StringValue from (2)
The question is how to conform to non-generic StringProvider while keeping value typed
Example:
0.5.value.lowercased()
Of course StringValue doesn't have lowercased method from String so it won't compile.
What I have tried:
First is to add untyped property to extension (3)
extension TypedStringProvider {
var value: TypedStringValue { return typedValue }
var value: StringValue { return typedValue }
}
Doesn't work because of Invalid redeclaration of 'value' error
Second is to extend my classes and add untyped property there (5)
extension UIView {
var value: StringValue { return typedValue }
}
extension Double {
var value: StringValue { return typedValue }
}
It works without compiler errors but
No autocompletion for lowercased in our example.
With extensions (5) I need to write a lot of code for every class conforming StringProvider and every property this protocol has
Any ideas?
value is defined as type StringValue, so this is the type you should specify in your extension of TypedStringProvider in order to complete the protocol conformance:
extension TypedStringProvider {
var value: StringValue {
return typedValue
}
}
Problem
On StringProvider you are defining value to a StringValue:
protocol StringProvider {
var value: StringValue { get }
}
but here you are defining the type to TypedStringValue which is not the same as StringValue. (underlying value can be TypedStringValue, however it needs to be typecasted from StringValue to TypedStringValue when using it)
extension TypedStringProvider {
var value: TypedStringValue { return typedValue }
}
Solution
There are two solutions that I can come up for this scenario right now:
1. Generic Approach
If you want to make value generic and change the type based on TypedStringProvider you can:
protocol StringProvider {
associatedtype StringProviderValue: StringValue
var value: StringProviderValue { get }
}
Conform to the protocol by defining StringProviderValue to TypedStringValue
extension TypedStringProvider {
var value: TypedStringValue { return typedValue }
}
2. StringValue approach
Keep the StringProvider as it is:
protocol StringProvider {
var value: StringValue { get }
}
Conform to the protocol by using the correct StringValue type, and inside it return typedValue: TypedStringValue which can be downcasted to StringValue
extension TypedStringProvider {
var value: StringValue { return typedValue }
}
Output
Both solutions give the same output:
Found solution
extension StringProvider where Self: TypedStringProvider {
var value: StringValue { return typedValue }
}
With this extension there's no need to write similar extensions to every class and autocompletion works too.
Full code:
protocol StringValue {
var asString: String {get}
}
protocol StringProvider {
var value: StringValue {get}
}
protocol TypedStringProvider: StringProvider {
associatedtype TypedStringValue: StringValue
var typedValue: TypedStringValue { get }
}
extension StringProvider where Self: TypedStringProvider {
var value: StringValue { return typedValue }
}
extension TypedStringProvider {
var value: TypedStringValue { return typedValue }
}
extension UIView: TypedStringProvider {
typealias TypedStringValue = String
var typedValue: String { return "some string" }
}
extension Double: TypedStringProvider {
typealias TypedStringValue = String
var typedValue: String { return String(self) }
}
extension String: StringValue {
var asString: String { return self }
}
let doubleValue = 0.5.value.lowercased()
Suppose there is the following protocol with a default implementation of someFuncWithDefaultImplementation() provided by an extension.
Then is it possible for MyClass2 to both provide its own implementation of someFuncWithDefaultImplementation() which also invokes the default implementation of that method from the extension?
protocol MyProtocol : class {
func someFuncWithDefaultImplementation()
func someFunc()
var someInt:Int { get set }
}
extension MyProtocol {
func someFuncWithDefaultImplementation() {
someInt = 5
}
func someFunc() {
someFuncWithDefaultImplementation()
}
}
class MyClass : MyProtocol {
var someInt = 6
}
class MyClass2 : MyProtocol
{
var someInt: Int = 4
func someFuncWithDefaultImplementation()
{
// do some additional stuff
/*** someFuncWithDefaultImplementation() invoke MyProtocol extension implementation here ***/
}
}
let class1 = MyClass()
class1.someFunc()
let class2 = MyClass2()
class2.someFunc()
The following answer in the thread
Calling protocol default implementation from regular method
describes using a nested dummy type to access the default implementation of a protocol from within a type that already has supplied its own implementation of that blueprinted method. We may extend this method to also allow actually making use of (blueprinted) properties of the MyClass2 instance, but within the call to the default implementation which MyClass2 already implements its custom version of (hence taking precedence over the default one).
We start by looking at a slightly more light weight version of your example, with a default implementation supplied for someFuncWithDefaultImplementation()
protocol MyProtocol : class {
func someFuncWithDefaultImplementation()
var someInt: Int { get set }
}
extension MyProtocol {
func someFuncWithDefaultImplementation() {
print("Called default impl. Currently, someInt = \(someInt)")
print("Mutates someInt from within default implementation (0) ...")
someInt = 0
}
}
We use the non-elegant nested type solution in the custom implementation of someFuncWithDefaultImplementation() of MyClass2, to call the default implementation of the latter, but stores a reference in the Dummy instance back to the MyClass2 instance, to allow the someInt property of MyClass2 to be used in the default implementation call (for reading and writing), even if this is called from the Dummy type.
class MyClass2 : MyProtocol
{
var someInt: Int = 42
func someFuncWithDefaultImplementation()
{
// do some additional stuff ...
print("In MyClass2 implementation, currently someInt = \(someInt)")
/* Dummy 'MyClass2'-capturing type used to call the default
implementation of 'MyProtocol', but with read and write
access to 'MyClass2':s self:s 'someInt' instance. */
class Dummy : MyProtocol {
unowned let myClass2: MyClass2
init(_ myClass2: MyClass2) { self.myClass2 = myClass2 }
var someInt: Int {
get { return myClass2.someInt }
set { myClass2.someInt = newValue }
}
}
// call default implementation of 'someFuncWithDefaultImplementation'
// supplying 'self' to read/write access to self.someInt.
Dummy(self).someFuncWithDefaultImplementation()
print("Back in MyClass2:s implementation; now someInt = \(someInt)")
// 0, woah, mutated in default implementation!
}
}
let a = MyClass2()
a.someFuncWithDefaultImplementation()
/* In MyClass2 implementation, currently someInt = 42
Called default impl. Currently, someInt = 42
Mutates someInt from within default implementation (0) ...
Back in MyClass2:s implementation; now someInt = 0 */
You could also choose to declare the nested Dummy outside of the function, just marking it private to make sure it cannot be accessed from outside MyClass2:
class MyClass2 : MyProtocol
{
var someInt: Int = 42
/* Dummy 'MyClass2'-capturing type used to call the default
implementation of 'MyProtocol', but with read and write
access to 'MyClass2':s self:s 'someInt' instance. */
private class Dummy : MyProtocol {
unowned let myClass2: MyClass2
init(_ myClass2: MyClass2) { self.myClass2 = myClass2 }
var someInt: Int {
get { return myClass2.someInt }
set { myClass2.someInt = newValue }
}
}
func someFuncWithDefaultImplementation()
{
// do some additional stuff ...
print("In MyClass2 implementation, currently someInt = \(someInt)")
// call default implementation of 'someFuncWithDefaultImplementation'
// supplying 'self' to read/write access to self.someInt.
Dummy(self).someFuncWithDefaultImplementation()
print("Back in MyClass2:s implementation; now someInt = \(someInt)")
// 0, woah, mutated in default implementation!
}
}
I will, however, repeat the same as the author of the linked answer: this approach is not very elegant.
Only if you subclass as well, see in:
import Foundation
protocol MyProtocol : class {
func someFuncWithDefaultImplementation()
func someFunc()
var someInt:Int { get set }
}
extension MyProtocol {
func someFuncWithDefaultImplementation() {
someInt = 5
print("a")
}
func someFunc() {
someFuncWithDefaultImplementation()
}
}
class MyClass : MyProtocol {
var someInt = 6
}
class BaseClass: MyProtocol {
var someInt: Int = 4
}
class MyClass2 : BaseClass
{
func someFuncWithDefaultImplementation()
{
// do some additional stuff
print("b")
(self as BaseClass).someFuncWithDefaultImplementation()
}
}
If you add the above in a Playground and then call:
MyClass2().someFuncWithDefaultImplementation()
It prints:
b
a
Which is what you are asking for, see screenshot.
I have 2 class which have same number of properties with same name. I want to access property without type casting.
class A : NSObject {
var amount : Int = 10
}
class B : NSObject {
var amount : Int = 20
}
Now I want to double the value of amount property like this
main() {
let classA : A()
print(doubleValue(classA))
let classB : B()
print(doubleValue(classB))
}
func doubleValue(myClass:AnyObject) -> Int {
return myClass.amount * 2
}
Please suggest how can I achieve this.
This is exactly what protocol are used for. Let us call this new protocol Amountable and add the amount property.
protocol Amountable {
var amount: Int { get set }
}
If you want to provide a default implementation for doubleValue() you can event use protocol extension as follows:
extension Amountable {
mutating func doubleValue() {
self.amount *= 2
}
}
Finally, let your classes conform to the protocol:
class ClassA: Amountable {
// Implementation of classA here
}
class ClassB: Amountable {
// Implementation of classB here
}
objectA = ClassA()
objectA.doubleValue()
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 })
}
}