Variable that conforms to a protocol that has a generic function - swift

I have a protocol that looks like this:
protocol MyProtocol {
associatedtype SpeedType
var name: String {get set}
func forward(_: SpeedType)
}
I made 2 simple classes that conform to this protocol:
class A: MyProtocol {
typealias SpeedType = Double
var name: String
init(name:String) {
self.name = name
}
func forward(_ s: Double) {
print("Moving \(s) km/h")
}
}
class B: MyProtocol {
typealias SpeedType = Int
var name: String
init(name:String) {
self.name = name
}
func forward(_ s: Int) {
print("Moving \(s) km/h")
}
}
What I want to achieve is to be able to declare a variable of type MyProtocol, and initialize it later like so:
let x: Bool = true
var person: MyProtocol
if x {
person = A(name: "Robot")
} else {
person = B(name: "Human")
}
Before I made forward() method "generic" I was able to do this, however now I am getting the next error:
Protocol "MyProtocol" can only be used as generic constraint because it has Self or associated type requirement.
So my goal is to have a method forward() that can take as an argument parameter of a type that I specify, and also be able to declare a variable of a type that conforms to my protocol.

Swift doesn't allow this.
Here's why: you don't know anything about the type of argument person.forward(_:) takes. There is no way to call it. MyProtocol essentially defines an open-ended set of independent types.
If you don't want to be able to call person.forward(_:), and you just want to be able to access the non-generic person.name property, then split your protocol into a base, non-generic protocol defining name, and a sub-protocol that adds the generic forward(_:) method.
protocol NamedThing {
var name: String {get set}
}
protocol MovableNamedThing: NamedThing {
associatedtype SpeedType
func forward(_: SpeedType)
}
class A: MovableNamedThing {
typealias SpeedType = Double
var name: String
init(name:String) {
self.name = name
}
func forward(_ s: Double) {
print("Moving \(s) km/h")
}
}
class B: MovableNamedThing {
typealias SpeedType = Int
var name: String
init(name:String) {
self.name = name
}
func forward(_ s: Int) {
print("Moving \(s) km/h")
}
}
let x: Bool = true
var person: NamedThing
if x {
person = A(name: "Robot")
} else {
person = B(name: "Human")
}

Related

Type 'American' does not conform to protocol 'Food'

have been fighting the code for some time now, and can't resolve the issue of: Type 'American' does not conform to protocol 'Food'
protocol Food {
var type: String { get }
var ingredient1: String { get }
var price: Int { get set}
func showHistory()
mutating func transfer()
init(type: String)
init(ingredient1: String)
}
struct American: Food {
let type: String
let ingredient1: String
var price: Int = 125
init(type: String, ingredient1: String) {
self.type = type
self.ingredient1 = ingredient1
}
func showHistory() {
print("American history")
}
mutating func transfer() {
print("transfering burgers")
}
}
I doubt you intended to separate your inits into two separate calls which is causing your error. You could solve this by implementing two separate inits as well but then you'd have to initialize both properties in separate inits which will give you an error
protocol Food {
var type: String { get }
var ingredient1: String { get }
var price: Int { get set}
func showHistory()
mutating func transfer()
init(type: String, ingredient1: String)
}
struct American: Food {
let type: String
let ingredient1: String
var price: Int = 125
init(type: String, ingredient1: String) {
self.type = type
self.ingredient1 = ingredient1
}
func showHistory() {
print("American history")
}
mutating func transfer() {
print("transfering burgers")
}
}

Swift function call and protocol

In swift is it possible to declare a method property not as a type but as a protocol?
like:
protocol myProtocol {
var data1: String {get set}
var node: Int {get set}
}
class myData: myProtocol {
var data1: String = "Boo"
var node: int = 10
}
class myClass {
func myFunc(data: myProtocol) {
data.data1 = "Hello"
}
}
Basically I want to say to the method look I don't care about the type. As long as the object conforms to the protocol its ok
Yes this is fine, but to modify data you will have to declare a class only protocol.
protocol MyProtocol: AnyObject {
var data1: String {get set}
var node: Int {get set}
}
class MyData: MyProtocol {
var data1: String = "Boo"
var node: Int = 10
}
class MyClass {
func myFunc(data: MyProtocol) {
data.data1 = "Hello"
}
}
I've also fixed the capitalisation of your classes.
You can use associatedtype.
protocol MyProtocol {
associatedtype CustomData
var data1: String { get set }
var node: Int { get set }
func myFunc(data: CustomData)
}
class MyData: MyProtocol {
func myFunc(data: String) {
print(data)
}
var data1: String = "Boo"
var node: Int = 10
}
Also, you should use PascalCase for both protocols and classes, and Int is the integer type for swift.
EDIT:
I misunderstood your question. You can also specify a function parameter by an abstract protocol, not just a class or a struct!
protocol MyProtocol {
var data1: String { get set }
var node: Int { get set }
}
class MyData: MyProtocol {
var data1: String = "Boo"
var node: Int = 10
}
class MyClass {
func myFunc(data: MyProtocol) {
print(data.data1)
}
}
let data = MyData()
let instance = MyClass()
instance.myFunc(data: data) // Boo

Protocol and init argument

An ex-collegue left a game uncomplete and undocumented.
When reading his code I found:
protocol EnemyMovement {
func forward(speedPercent: Int)
func reverse(speedPercent: Int)
func left(speedPercent: Int)
func right(speedPercent: Int)
}
protocol Enemy {
var name: String {get set}
var enemyMovement: EnemyMovement {get set}
init (name: String, enemyMovement: EnemyMovement)
}
class EnemyInstance: Enemy {
var name = "No enemy Name"
var enemyMovement: EnemyMovement
required init (name: String, enemyMovement: EnemyMovement) {
self.name = name
self.enemyMovement = enemyMovement
//...
}
I could not found a concrete instance of EnemyInstance, but if it's quite clear how to pass the name string, I don't understand how is EnemyMovement supposed to be passed.
var enemy = EnemyInstance(name: "zombie", enemyMovement?...)
Any idea?
Since parameter has to be of type conforming to EnemyMovement, including these methods, you have to pass this object. So, you can try to create example struct
struct Movements: EnemyMovement {
func forward(speedPercent: Int) {
print(speedPercent)
}
func reverse(speedPercent: Int) {
print(speedPercent)
}
func left(speedPercent: Int) {
print(speedPercent)
}
func right(speedPercent: Int) {
print(speedPercent)
}
}
now as parameter for EnemyInstance initializer pass new instance of Movements
var enemy = EnemyInstance(name: "zombie", enemyMovement: Movements())
then you can call some method on enemyMovement property of your class and code inside this certain method gets executed (in this case it should print speedPercent)
required init (name: String, enemyMovement: EnemyMovement) {
self.name = name
self.enemyMovement = enemyMovement
enemyMovement.forward(speedPercent: 2) // prints 2
}

type A requires that type B be a class type swift 4

the following code gives me a compile error
'WeakReference' requires that 'ServiceDelegate' be a class type
protocol ServiceDelegate: AnyObject {
func doIt()
}
class SomeClass() {
// compile error occurs in this line
private var observers = [WeakReference<ServiceDelegate>]()
}
WeakReference code:
final class WeakReference<T: AnyObject> {
private(set) weak var value: T?
init(value: T?) {
self.value = value
}
}
How can I fix this error? Delegate should be declared correctly as per this site.
What I have tried so far:
Changing the delegate protocol conformance from AnyObject to
class does not solve the problem.
Try the above code in a clean playground.
You can't have a WeakReference<ServiceDelegate>. ServiceDelegate itself is not an AnyObject, it just requires that anything that conforms to it be an AnyObject.
You would need to make SomeClass generic and use the generic type as the type for the WeakReference:
class SomeClass<T: ServiceDelegate> {
private var observers = [WeakReference<T>]()
}
If the generic on SomeClass is too constricting and you want to be able to have instances of multiple unrelated classes as observers then I would do it by abandoning the generic parameter on WeakReference:
final class WeakServiceDelegate {
private(set) weak var value: ServiceDelegate?
init(value: ServiceDelegate?) {
self.value = value
}
}
class SomeClass {
private var observers = [WeakServiceDelegate]()
}
Alternatively you could make WeakReference conditionally conform to ServiceDelegate:
extension WeakReference: ServiceDelegate where T: ServiceDelegate {
func doIt() {
value?.doIt()
}
}
And then use an array of ServiceDelegate in SomeClass:
class SomeClass {
private var observers = [ServiceDelegate]()
func addObserver<T: ServiceDelegate>(_ observer: T) {
observers.append(WeakReference(value: observer))
}
}
As you see, ServiceDelegate is a protocol, not a class type.
Even if all types which can conform to ServiceDelegate are class types, ServiceDelegate itself is not a class type. It is the fact of the pure Swift protocols currently.
Try #obc, Objective-C protocols are a bit different:
#objc protocol ServiceDelegate {
func doIt()
}
You may want to exclude Objective-C something and to make some pure Swift classes conform to ServiceDelegate, but I cannot find other ways around.
The problem is that WeakReference<ServiceDelegate> is wrong at line
private var observers = [WeakReference<ServiceDelegate>]()
You have to use a concrete class instead of protocol inside <>
You have two possible solutions:
Create a concrete class and use it:
class ServiceClass: ServiceDelegate {
//...
}
private var observers = [WeakReference<ServiceClass>]()
Or use a protocol. I mean this:
final class WeakReference<T: AnyObject> {
private(set) weak var value: T?
init(value: T?) {
self.value = value
}
}
protocol SomeContainer: AnyObject { }
extension WeakReference: SomeContainer { }
and use this way:
private var observers = [SomeContainer]()
Note
Using this way:
class SomeClass<T: ServiceDelegate> {
private var observers = [WeakReference<T>]()
}
You just move the problem to another part of the code.
I had similar problem and ended up keeping generic WeakReference, but removing type constraint:
struct WeakReference<T> {
private weak var storage: AnyObject?
var value: T? {
get { return storage.map { $0 as! T } }
set {
storage = newValue.map { $0 as AnyObject }
}
}
init(value: T?) {
self.value = value
}
}
This works for classes, Objective-C protocols and Swift protocols:
protocol P: class {}
#objc protocol Q {}
class Z: P, Q {}
var z = Z()
var rz = WeakReference<Z>(value: z)
var rp = WeakReference<P>(value: z)
var rq = WeakReference<Q>(value: z)
assert(rz.value === z)
assert(rp.value === z)
assert(rq.value === z)
z = Z()
assert(rz.value === nil)
assert(rp.value === nil)
assert(rq.value === nil)
Unfortunately it compiles for other things too:
protocol R {}
struct S: R {}
var rr = WeakReference<R>(value: S())
print("rr =", rr.value as Any) // nil
var rs = WeakReference<S>(value: S())
print("rs =", rs.value as Any) // nil
In Swift anything can be casted to AnyObject, but for value types that means boxing - new instance is allocated and immediately lost, so it always produces nil.
This can be used to implement an assertion that casting to AnyObject preserves identity:
struct WeakReference<T> {
private weak var storage: AnyObject?
var value: T? {
get { return storage.map { $0 as! T } }
set {
storage = newValue.map {
let asObject = $0 as AnyObject
assert(asObject === $0 as AnyObject)
return asObject
}
}
}
init(value: T?) {
self.value = value
}
}
Alternative approach would be to use https://github.com/wickwirew/Runtime to validate kind of T.self.
create plain protocol
public protocol AnyWeakValue {
var anyValue: Any? { get }
}
inherit associatedtype protocol from AnyWeakValue
public protocol WeakValue: AnyWeakValue {
associatedtype ValueType
var value: ValueType? { get }
}
extension WeakValue {
public var anyValue: Any? { return value }
}
create class Weak inherit WeakValue
open class Weak<Value: AnyObject>: WeakValue {
public init(value: Value?) { self.value = value }
open private(set) weak var value: Value?
}
using example
private var _delegates: [AnyWeakValue] = []
public var delegates: [SomeProtocol] {
return _delegates.compactMap({$0.anyValue as? SomeProtocol})
}
public func register<Delegate>(_ delegate: Delegate) where Delegate: SomeProtocol {
let weak: Weak<Delegate> = Weak.init(value: delegate)
_delegates.append(weak)
}

swift 3 downcast to dynamic class

I am trying to create a couple of objects which are dependent one to each other and they mush have a method to downcast directly the concrete class of the other object. Something like this:
protocol aProt
{
var bVar:bProt! { get set }
}
protocol bProt
{
var aVar:aProt! { get set }
}
class a: aProt
{
var bVar: bProt!
func bConcrete() -> b {
return bVar as! b
}
}
class b: bProt
{
var aVar: aProt!
func aConcrete() -> a {
return aVar as! a
}
Now, the problem is that I want this behavior (func aConcrete(),func bConcrete()) to be inherited by the subclasses of a and b. Then I thought the perfect way of doing this was using generics, but... There's no way of doing this.
class a: aProt
{
var bVar: bProt!
func bConcrete() -> T {
return bVar as! T
}
}
class b: bProt
{
var aVar: aProt!
func aConcrete<T>() -> T {
return aVar as! T
}
You can do it but when you have to use it you must downcast the variable anyway, so there is no way of doing it in a clean manner:
let aObject = a()
let bSubclassObject = a.bConcrete() // The compiler complains it cannot infer the class of T
let bSubclassObject = a.bConcrete() as! bSubclass // this works, but this is exactly which I wanted to avoid... :(
Define the generic function and add where to T:
protocol aProt {
var bVar: bProt! { get set }
}
protocol bProt {
var aVar:aProt! { get set }
}
class a: aProt {
var bVar: bProt!
func bConcrete<T: b>(_ type: T.Type) -> T? {
return bVar as? T
}
}
class b: bProt {
var aVar: aProt!
func aConcrete<T: a>(_ type: T.Type) -> T? {
return aVar as? T
}
}
class a1: a { }
class b1: b {
var fullName: String = "new object"
}
let aObj = a()
aObj.bVar = b1()
let bObj = aObj.bConcrete(b1.self)
bObj?.fullName
According to your requirement, calls bConcrete(b1.self) might still not good enough, but at least you need to know what type of data you are expecting to return.