Why can't I set the speed of a GKAgent2D in iOS9?
Asked Answered
S

1

9

If I try to change the value of the speed parameter of a GKAgent2D (or its parent class GKAgent) in iOS9 I get this unrecognised selector error:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[GKAgent2D setSpeed:]: unrecognized selector sent to instance

However in iOS10 this error does not occur and the speed of the agent is changed correctly.

I can reproduce the error in a very simple example (single view iOS application, change the view controller code to the following, note that the error occurs with either GKAgent2D or GKAgent):

import UIKit
import GameplayKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        var agent = GKAgent()
        agent.speed = 10

        print(agent.speed)
    }

}

The above always crashes with this unrecognised selector error on the line that sets the agent's speed on the simulator running iOS9.3, but not iOS10.3 (all this under Xcode 8.3.2).

The speed property of GKAgent is documented as being read and write and being supported under iOS9 - see Apple's Documentation for GKAgent, speed Property.

I had a similar problem with GKAgentDelegate, which would crash on iOS9 only with an unrecognised selector to agentWillUpdate. I worked around this by putting a dummy method in my code:

func agentWillUpdate(_ agent: GKAgent) {
}

I have tried a similar solution to the new error, by overriding the speed property in my GKAgent2D sub class and providing an explicit setter and getter, and even backing the speed parameter by a private Float as suggested by Kdawg, but the same error still occurs when the super's speed parameter is set:

class Agent:GKAgent2D {
    override var speed:Float {
        get {return localSpeed}
        set {localSpeed = newValue}
    }
    private var localSpeed:Float {
        get {return super.speed}
        set {super.speed = newValue}
    }
}

Any thoughts?

Specifically: are there any known issues with GKAgent in iOS9 regarding selectors?

Alternatively, any thoughts on an alternative work around?

I would like to support iOS9 if I can. It looks to me like a bug in GameplayKit - but I expect Apple's response to a report would be that it is fixed in iOS10.

Sensuous answered 8/7, 2017 at 20:22 Comment(0)
N
2

Edited

What I meant was to have your subclass's property setter/getter just override the base class's and not try to access the parent class's setter at all, like this:

class Agent:GKAgent2D {
    private var _speed: Float = 0.0 
    override var speed:Float {
        get {return _speed}
        set {_speed = newValue}
    }
}

At that point, assuming that the GKAgent and GKAgent2d accesses speed through its property getter, it will get the value of _speed from the overridden getter in your subclass. This is why I suggested in the comments to try this after making that subclass:

var agent: GKAgent 
agent = GKAgent2dSubType()
agent.speed = 10

I would then expect that if you then tried to read out the property of your GKAgent agent, it would be 10. And so the expected behavior of your agent might work properly with your speed in there.


Original This is just a total guess (I'm not really familiar with the technologies here), but have you tried changing your

override var speed:Float {
    get {return super.speed}
    set {self.speed = newValue}
}

to not reference super.speed or self.speed (incidentally, shouldn't setting self.speed from your speed setter result in an infinite recursive call to that setter?) but rather to reference a private backing floating point value? Then maybe you'll be able to instantiate an object of type GKAgent2dSubClass (however you've named that subclass) and have it work.

Niece answered 22/7, 2017 at 1:41 Comment(9)
Yes, replacing speed in the setter/getter with a private backing type removes the initial unrecognised selector error: but I need to set the GKAgent's speed: if I then try to do that (e.g in the setter for the backing type) it rolls up to the super classes 'speed' setter and the error occurs again at that higher level. You are also correct that referencing self.speed in the setter causes an infinite loop - it should have been super (the error occurs before the infinite loop though and using super just pushed the error to the super class!)Sensuous
I think the fundamental problem is that the speed parameter did not exist in GKAgent in iOS9 (despite the documentation stating otherwise): if I try to override speed in a extension of GKAGent2D it compiles (but if I call super.speed the error occurs again due to GKAgent not having a speed). If I try to override speed in an extension of GKAgent it fails to compile - with an odd error claiming that speed is referencing itself in the definition.Sensuous
@Ali Beadle: But if you've overridden the parent class's property, that should get treated as the getter/setter for that property for objects of your subtype, even if they're accessed from the base class, shouldn't it? If you create this GKAgent2dSubClass with an overridden property and access it as a GKAgent, does it work? var agent: GKAgent <newline> agent = GKAgent2dSubType <newline> agent.speed = 10 I haven't tested this, but I would assume it would work.Niece
yes, the overridden getter and setter will be called. But I will not have set the GKAgent's speed, just another variable pretending to be speed. I need to set the GKAgent's actual speed parameter (it is used to determine how the GKAgent moves).Sensuous
@Ali Beadle: Have you tried it and found it not to work? I would assume that the GKAgent accesses its speed through its property getter and not through some separate backing variable private to its class, which is why I figure this could work. Regardless, hopefully you figure out a way to do this.Niece
Yes I have tried, unless I am misunderstanding what you are suggesting: I have posted the modified code in my original question.Sensuous
@Ali Beadle: I have edited the answer to provide more information on what I'm suggesting. Your edited code still accesses the apparently non-existent parent setter; I'm suggesting not referencing that setter at all and completely overriding it with your subclass's setter.Niece
Oh I see! Yes, that resolves the error - but my GKAgent is now static! HOWEVER I think that is probably because speed never did exist in iOS9 - so you have answered my question, and although this has not resolved my particular problem the answer is correct and possibly useful to others, so I am going to accept it and award the bounty. Thanks for your efforts.Sensuous
Thanks! Sorry that it doesn't resolve your problem, but we tried :)Niece

© 2022 - 2024 — McMap. All rights reserved.