Why can't I set the speed of a GKAgent2D in iOS9? - ios

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.

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.

Related

How to get the AutoreleasingUnsafeMutablePointer of a Swift Object

I'm currently the owncloud iOS library for an upload task of my Swift app.
It is written in Objective-C and requires me to pass an AutoreleasingUnsafeMutablePointer<NSProgress?> to the upload method.
Say I create a new object like so
let progress: NSProgress? = NSProgress()
How can I get the AutoreleasingUnsafeMutablePointer<NSProgress?> of this object in Swift?
I tried it the following way:
var progress: NSProgress? = NSProgress()
let unsafeAutoreleasingProgressPointer = AutoreleasingUnsafeMutablePointer<NSProgress?>.init(&progress)
But I get an
EXC_BAD_ACCESS (code=1, address = 0x15942320)
when executing the code. I would like to keep a reference to the progress object because I want to add an observer callback that tells me the upload progress in percent (as also demonstrated in the example link).
AutoreleasingUnsafeMutablePointer is just a struct. All you have to do is create the pointer and then assign the value to its memory property.
var pointer = AutoreleasingUnsafeMutablePointer<NSProgress>.init()
pointer.memory = progress!
There were 2 issues in fact.
We can create an AutoreleasingUnsafeMutablePointer<NSProgress?> object like so:
let progress = NSProgress()
let progressPointer = AutoreleasingUnsafeMutablePointer<NSProgress?>.init(&progress)
It's important to note that you have to keep the pointer around. If you create the pointer in a method, the reference to it will be lost as soon as we exit the method scope. This causes the
EXC_BAD_ACCESS
exception. So your implementation might look like this:
class MyClass: NSObject {
dynamic var progress: NSProgress = NSProgress()
var progressPointer: AutoreleasingUnsafeMutablePointer<NSProgress?> = nil
override init(){
//...
//init non optional variables here
super.init()
progressPointer = AutoreleasingUnsafeMutablePointer<NSProgress?>.init(&progress)
}
}
In my case I did Key-Value Observing because I was using a third party Objective C dependency. Note that you have to put the dynamic keyword in front of the object you want to observe using KVO.
If you still get an
EXC_BAD_ACCESS
exception, use the Singleton pattern.

Observing object properties with ReactiveCocoa 4 in Swift

As a preface, this might be an incredibly simple and/or ignorant question.
In ReactiveCocoa 2.x, we were able to use RACObserve and RAC to observe properties of an object. From the documentation I can find in Reactive 3 and 4, we now use PropertyType to observe changes to an object property. I have so far been unsuccessful in being able to observe any property change when using MutableProperty or DynamicProperty.
class TempObject {
var property: String
}
let tempObject = TempObject()
let propertyObserver: MutableProperty<String> = MutableProperty(tempObject.property)
From what I understand, I should be able to use propertyObserver to view changes to tempObject.property. I tried adding a map function to the signal producer from propertyObserver to see if it was firing, but don't see anything when updating tempObject.property. Again, could be a trivial thing that I am missing, thanks so much.
Edit
NachoSoto nailed it - I needed to make my property KVO compliant. I also ended doing this:
let tempObjectSignal: MutableProperty<TempObject> = MutableProperty(tempObject)
let propertyObserver: MutableProperty<String> <~ tempObjectSignal.producer.map({ $0.property })
And whenever tempObject.property is updated I make sure to call
tempObjectSignal.value = tempObject
This fires off all the necessary signals. I don't know if this breaks any best practices, though. Let me know what you think!
MutableProperty(value) creates a mutable property but only with value that as the initial value.
What you want to use is DynamicProperty, which will use the Objective-C runtime and KVO to detect changes to an object's property:
let property = DynamicProperty(tempObject, "property")
For that reason, however, you need to make sure that the property you want to observe is part of the Objective-C runtime, by making the class a subclass of NSObject, and by either using the dynamic keyword:
class TempObject: NSObject {
dynamic var property: String
}
Or using #objc to ensure that it gets exported to the runtime:
class TempObject: NSObject {
#objc var property: String
}

Why does referencing .self work instead of type.sharedInstance?

I have some code like this:
class miniUser {
///The shared instance of miniUser.
static let SI = miniUser()
init()
{
self.currentUser = self.getCurrentProfile()! as String
self.refreshUserList()
}
...
}
That code works fine. However, this code using the shared instance...
class miniUser {
///The shared instance of miniUser.
static let SI = miniUser()
init()
{
miniUser.SI.currentUser = miniUser.SI.getCurrentProfile()! as String
miniUser.SI.refreshUserList()
}
...
}
That does not work. In fact, it doesn't even spit out an error during runtime, it just freezes at 0 fps (I'm using SpriteKit) when another part of the code tries to initialize it.
Ideally, both versions of the code should mean the same thing.
Could anyone explain to me why using self.function() works instead of type.sharedInstance.function() during initialization? I'm not sure about it and need an explanation to understand what is happening here.
Any help is-- err, any explanations are appreciated.
The definition let SI = miniUser() will call the init() of your class in order to create the shared instance. However, init() is trying to access the shared instance miniUser.SI, so this is a recursive definition. It probably shouldn't be allowed at all by the compiler — I'd suggest filing a bug. If you need to modify instance variables during initialization, that's what self. is for. (In fact, you can leave out self. because it's implied.)

What is the cause of EXC_BAD_ACCESS crashes for classes declared in separate files?

I have come across this problem several times now, without ever finding the cause.
When attempting to access a property of ClassY in ClassX I get an EXC_BAD_ACCESS error. This happens even if I instantiate a clean copy and attempt to access the property immediately:
let classY = ClassY()
println(classY.someTextProperty)
EXC_BAD_ACCESS
As far as I am aware this error should only be caused by trying to access a deallocated instance, which presumably cannot be the case in the above code.
Where it gets strange, is that if I declare ClassY in the same file as ClassX then the problem disappears. (ClassY does have some private properties, but I am not trying to access these and anyway, making them all public does not solve the problem)
I am not including the source code here since there is more of it than is practical to isolate. I have not been able to reproduce this reliably in a standalone project because I am unable to find any kind of pattern.
Has anyone come across a similar kind of problem, and does anyone have an idea for a solution (obviously I can include the definition in the same file, but that is a workaround rather than a solution)?
I am currently running iOS 8.3 / Swift 1.2 but had this problem already in iOS 8.1.
UPDATE
Following lots of trial and error, it seems that the problem comes from lazy properties and protocol conformance. Again this is not something I can reproduce in a plain playground (perhaps the declarations need to be in different files).
My ClassY conforms to ProtocolY which has several properties:
protocol ProtocolY {
var number: Int { get }
var text: String { get }
}
When ClassY conforms to ProtocolY, if it implements either of the properties as a lazy property then accessing a property causes the EXC_BAD_ACCESS crash. If ClassY is not marked as conforming to ProtocolY or if none of the properties declared in ProtocolY as implemented by ClassY are lazy properties then no crash occurs.
Obviously this is just narrowing down but still does not give me an explanation. I know of no reason why implementations of protocol properties should not be lazy.
UPDATE Here is an example of properties on ClassY:
private(set) lazy var currentPage: Int = {
let subject: RACReplaySubject = RACReplaySubject(capacity: 1)
subject.sendNext(self.currentPage)
return subject
}()
var currentPage: Int {
didSet {
(self.currentPageSignal as? RACSubject)?.sendNext(self.currentPage)
}
}
currentPage is set in the init method.
If I use instead:
private(set) var currentPage: Int = {
let subject: RACReplaySubject = RACReplaySubject(capacity: 1)
return subject
}()
var currentPage: Int {
didSet {
(self.currentPageSignal as? RACSubject)?.sendNext(self.currentPage)
}
}
(and do a sendNext in the init method) then everything works fine.
I'm fairly confident that the fact that I'm using ReactiveCocoa is irrelevant - I have also tried lazy properties which are simple Strings, with the same result.

Strange behaviour when naming variable in lowerCamelCase

I came across a strange behaviour in Swift while programming a Master-Detail application.
Here's the scenario:
It's a simple Task Manager application. I have two text controls (TaskName, TaskDescription) on the TaskDetailView and two string variables with the same name but in lowerCamelCase (taskName, taskDescription) declared in the TaskDetailViewController.
#IBOutlet var TaskName:UITextField! //UpperCamelCase
#IBOutlet var TaskDescription:UITextView! //UpperCamelCase
var taskName:String? //lowerCamelCase
var taskDescription:String? //lowerCamelCase
I am setting the values of Text controls on ViewDidLoad() as usual:
override func viewDidLoad() {
super.viewDidLoad()
TaskName.text = taskName
TaskDescription.text = taskDescription
}
And I am passing the data in prepareForSegue (from TaskListViewController) as usual:
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if(segue.identifier == "TaskListSegue"){
let detailViewController = segue.destinationViewController as ToDoTaskViewController
let (task, desc) = m_ToDoListManager.GetTask(TaskListView.indexPathForSelectedRow().row)
println("selected \(task) \(desc)")
detailViewController.taskName = task
detailViewController.taskDescription = desc
}
}
The way everything is implemented is correct.
But now when you run the application, the values of text controls are not set.
In fact, the values of the variables also are not set.
What must be happening here?
I have already investigated this problem and also came up with a solution (see my answer below). Please also see Martin R's answer below for a detailed explanation. I just wanted to share this with everyone. I am not sure if anyone has come across this issue.
Update:
Here's the actual code:https://github.com/Abbyjeet/Swift-ToDoList
Here is an explanation:
Your Swift class is (ultimately) a subclass of NSObject.
Therefore the properties are Objective-C properties with getter and setter method.
The name of the setter method for a property is built by capitalizing the first
letter of the property name, e.g. property "foo" has the setter method setFoo:
As a consequence, the setter method for both properties TaskName and taskName is called setTaskName:.
In an Objective-C file, you would get a compiler error
synthesized properties 'taskName' and 'TaskName' both claim setter 'setTaskName:' - use of this setter will cause unexpected behavior
but the Swift compiler does not notice the conflict.
A small demo of the problem:
class MyClass : NSObject {
var prop : String?
var Prop : String?
}
let mc = MyClass()
mc.prop = "foo"
mc.Prop = "bar"
println(mc.prop) // bar
println(mc.Prop) // nil
In your case
TaskName.text = ...
sets the "taskName" property, not the "TaskName". The properties have different type,
so that the behavior is undefined.
Note that the problem does only occur for "Objective-C compatible" properties. If you remove the
NSObject superclass in above example, the output is as expected.
Conclusion: You cannot have two Objective-C properties that differ only in the
case of the first letter. The Swift compiler should fail with an error here (as the
Objective-C compiler does).
The problem you were facing with was not connected to the swift language. Method prepareForSegue is called before loadView. That mean UITextField and UITextView are not initialized yet. That's why fields were not initialized.
You also asked: Why compiler doesn't show any error? That's because any selector performed on nil object doesn't throw an exception. So for example (sorry for obj-c):
UITextField *tf = nil;
[tf setText:#"NewText"];
Will not show any error.
As you said on your own answer to solve your problem you need to add additional fields to your destination controller (copy-paste):
var tAskName:String? //cUstomCamelCase
var tAskDescription:String? //cUstomCamelCase
Why is it happening?
I believe that internally Swift is using lowerCamelCase for text controls names which are not yet initialized and thus failing to set the values. But it is also strange that I didn't get any kind of error.
How did I solve it?
I know that the Swift is case-sensitive. So that was not the issue. So I just changed the case of one letter and named the variables as (tAskName, tAskDescription) and the values were set as expected.
#IBOutlet var TaskName:UITextField! //UpperCamelCase
#IBOutlet var TaskDescription:UITextView! //UpperCamelCase
var tAskName:String? //cUstomCamelCase
var tAskDescription:String? //cUstomCamelCase
So the conclusion is that if I have a control named TaskName, I cannot have a variable named as taskName

Resources