Protocol and init argument - swift

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
}

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

Method cannot be marked #objc because the type of the parameter 2 cannot be represented in Objective-C [duplicate]

After I have updated Swift 1 to Swift 2.0 I have an issue.
I am getting the following error on the first line of this code:
Method cannot be marked #objc because the type of the parameter cannot be represented in Objective-C
#objc func personsToFirstStep(persons: [Person]) {
for person in persons {
if !self.persons.contains(person) && person.id != userID {
self.persons.append(person)
}
}
collectionView.reloadData()
collectionViewPlaceholder.hidden = true
collectionView.hidden = false
collectionGradientView.hidden = false
}
This this Person class:
class Person: Hashable {
var intID: Int = 0
var id: String = ""
var name: String = ""
var type: String = ""
var hashValue: Int {
return self.intID
}
init(id: String, name: String, type: String) {
self.id = id
self.intID = Int(id)!
self.name = name
self.type = type
}
}
func ==(lhs: Person, rhs: Person) -> Bool {
return lhs.intID == rhs.intID
}
You have very nicely explained the problem yourself:
class Person: Hashable {
Person is not an NSObject. But only an NSObject-derived class type can be seen by Objective-C. Therefore your Person type is invisible to Objective-C. But your #objc func declaration is for a function that takes an array of Person — and we have just said that Person is invisible to Objective-C. So your #objc func declaration is illegal. Objective-C cannot be shown this function, because it cannot be shown its parameter.
You would need to change your class declaration to start like this:
class Person: NSObject {
...and then you might of course have to make any necessary further adjustments in the class's implementation. But that change would make your #objc func declaration legal. (NSObject is Hashable, so the amount of work needed to make this adaptation might not be very great.)
I was getting this because I declared a class Notification of my own and it was messing with Foundation's Notification class.
#objc func playerItemDidReachEnd(notification: Notification) {...}
So I changed it to Foundation.Notification
#objc func playerItemDidReachEnd(notification: Foundation.Notification) {...}
With this less informations I can only try to suggest you to put this before Person declaration.
#objc(Person)
class Person {
...
}

Variable that conforms to a protocol that has a generic function

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

Swift 2.0 Method cannot be marked #objc because the type of the parameter cannot be represented in Objective-C

After I have updated Swift 1 to Swift 2.0 I have an issue.
I am getting the following error on the first line of this code:
Method cannot be marked #objc because the type of the parameter cannot be represented in Objective-C
#objc func personsToFirstStep(persons: [Person]) {
for person in persons {
if !self.persons.contains(person) && person.id != userID {
self.persons.append(person)
}
}
collectionView.reloadData()
collectionViewPlaceholder.hidden = true
collectionView.hidden = false
collectionGradientView.hidden = false
}
This this Person class:
class Person: Hashable {
var intID: Int = 0
var id: String = ""
var name: String = ""
var type: String = ""
var hashValue: Int {
return self.intID
}
init(id: String, name: String, type: String) {
self.id = id
self.intID = Int(id)!
self.name = name
self.type = type
}
}
func ==(lhs: Person, rhs: Person) -> Bool {
return lhs.intID == rhs.intID
}
You have very nicely explained the problem yourself:
class Person: Hashable {
Person is not an NSObject. But only an NSObject-derived class type can be seen by Objective-C. Therefore your Person type is invisible to Objective-C. But your #objc func declaration is for a function that takes an array of Person — and we have just said that Person is invisible to Objective-C. So your #objc func declaration is illegal. Objective-C cannot be shown this function, because it cannot be shown its parameter.
You would need to change your class declaration to start like this:
class Person: NSObject {
...and then you might of course have to make any necessary further adjustments in the class's implementation. But that change would make your #objc func declaration legal. (NSObject is Hashable, so the amount of work needed to make this adaptation might not be very great.)
I was getting this because I declared a class Notification of my own and it was messing with Foundation's Notification class.
#objc func playerItemDidReachEnd(notification: Notification) {...}
So I changed it to Foundation.Notification
#objc func playerItemDidReachEnd(notification: Foundation.Notification) {...}
With this less informations I can only try to suggest you to put this before Person declaration.
#objc(Person)
class Person {
...
}