unwrapping SKPhysicsBody doesn't work - sprite-kit

In my project, I create new nodes using a class called RocketMaker. Inside this class, I have a function called applyRecurringForce()...
func applyRecurringForce() {
var thrust: CGVector = CGVectorMake(100, 100)
physicsBody!.applyForce(thrust)
}
My problem is I cannot access this function from the main scene.
override func update(currentTime: NSTimeInterval) {
/* Called before each frame is rendered */
for rocketNode in rocketShips.children {
println("physicsBody: \(rocketNode.physicsBody.description)")
rocketNode.physicsBody.applyRecurringForce()
}
}
Starting with the above code, I get two errors, one for the println, and one for the call to applyRecurringForce().
Approach 1: No forced unwrapping...
1) The println error is "Value of optional type 'SKPhysiceBody' not unwrapped; did you mean to use '!' or '?'?" and proposes I force unwrap
2) The call returns "Cannot invoke 'applyRecurringForce' with no arguments"
Approach2: I add "!" following the suggested solution...
1) The println error is the same as for the previous approach": "Value of optional type 'SKPhysiceBody' not unwrapped; did you mean to use '!' or '?'?" and again it proposes I force unwrap
2) The call returns the same error as previously: "Cannot invoke 'applyRecurringForce' with no arguments"
Approach3: So I follow the chain, using "physicsBody!!" in both lines in the override function. This time, one of the two errors is removed...
1) The println error is gone
2) The call returns "'SKPhysicsBody does not have a member named 'applyRecurringForce'"
If I comment out the call, I get
physicsNodeName: type: representedObject:[ name:'rocket1' position:{56, 294} accumulatedFrame:{{16.849998474121094, 280.20001220703125}, {78.300003051757812, 27.5999755859375}}]
Does anyone have an idea what's up?? This double-unwrapping looks very strange to me.

Well your applyRecurringForce method is declared in your custom node class (RocketMaker), not the SKPhysicsBody class. So you need to change the inside of your for-loop to this:
println("physicsBody: \(rocketNode.physicsBody!.description)")
rocketNode.applyRecurringForce()
Also i'm not really sure what rocketShips is. I originally though it was an array but given that you are accessing the children property i'm going to assume it is some kind of SKNode. Usually you don't want to name a single node plural.
Assuming that rocketShips is a node, then you will need to cast its children to your custom node class because by default children is an array of AnyObject (which is why you see the double unwrapping). See below for full solution. Do note though that i'm casting the entire children array. If your array contains a mix of RocketMaster nodes and other nodes you will need to cast each child separately.
import SpriteKit
class GameScene: SKScene {
var rocketShips: SKNode! //var rocketShips: [RocketMaster] = []
override func update(currentTime: NSTimeInterval) {
/* Called before each frame is rendered */
for rocketNode in rocketShips.children as! [RocketMaster] {
println("physicsBody: \(rocketNode.physicsBody!.description)")
rocketNode.applyRecurringForce()
}
}
}
class RocketMaster: SKNode {
func applyRecurringForce() {
var thrust: CGVector = CGVectorMake(100, 100)
physicsBody!.applyForce(thrust)
}
}

Related

Is it possible to initialize properties at the beginning of a class?

I am writing my project and wondered.
When I read literature or watch videos, I see that this is bad practice. Why? Is this bad for the system?
What is the difference between this
class SomeClass {
var someView = SomeView()
var someViewModel = SomeViewModel()
// ...
}
and this
class SomeClass {
var someView: SomeView!
var someViewModel: SomeViewModel?
// ...
}
How to get used to it better?
You have to initialize all instance properties somehow. And you have to do it right up front, either in the declaration line or in your init method.
But what if you don't actually have the initial value until later, like in viewDidLoad? Then it is silly to supply a real heavyweight value only to replace it later:
var v = MyView()
override func viewDidLoad() {
self.v = // get _real_ MyView and assign it in place of that
}
Instead, we use an Optional to mark the fact that we have no value yet; until we obtain and assign one, it will be nil:
var v : MyView? // means it is initially `nil`
override func viewDidLoad() {
self.v = // get _real_ MyView and assign it to our property
}
There's nothing wrong with the first way (which is called a "default property value", by the way), and in fact, often times it's preferable. But of course, the devil is in the details:
How would the initialization of a SomeViewModel work? Without acess the initializer parameters of SomeClass, you're stuck with only being able to construct an instance from a parameter-less init, like SomeViewModel(). What exactly could that do? Suppose it was a person view model, and you had PersonViewModel(). What person? Whats their name? What will this default value do at all?
It's not a great pattern if it requires overwriting the default value with some other value in the initializer
It initializes the value up-front, where sometimes a lazy or computed value might be more appropriate.

Initialize Non-Optional Property Before super.init call without duplicating code

One of the most frustrating situations for me in Swift is when I have a class like this:
class A: NSObject {
var a: Int
override init() {
clear()
super.init()
}
func clear() {
a = 5
}
}
Of course, this causes multiple compiler errors ('self' used in method call 'clear' before 'super.init' call and Property 'self.a' not initialized at super.init call.
And changing the order of the constructor lines only fixes the first error:
override init() {
super.init()
clear()
}
Usually, I end up doing one of two things. The first option is to make a an implicitly unwrapped optional, but I feel that this is terrible style because a will never be nil after the constructor returns:
var a: Int! = nil
The second option is to copy the functionality of my clear() function in the constructor:
override init() {
a = 5
super.init()
}
This works fine for this simplified example, but it unnecessarily duplicates a lot of code in more complex code bases.
Is there a way to initialize a without duplicating my clear function or making a an optional? If not, why not?!
The "why not" in this specific case is very straightforward. What you've written would allow me to write this:
class B: A {
override func clear() {}
}
And then a would not be initialized. So what you've written can never be legal Swift.
That said, there's a deeper version that probably could be legal but isn't. If you marked the class final or if this were a struct, then the compiler might be able to prove that everything is correctly initialized along all code paths by inlining all the possible method calls, but the compiler doesn't do all that today; it's too complicated. The compiler just says "my proof engine isn't that strong so knock it off."
IMO, the correct solution here is a ! type, though I wouldn't add = nil. That's misleading. I would write it this way:
class A: NSObject {
var a: Int! // You don't have to assign `!` types; they're automatically nil
override init() {
super.init()
clear()
}
func clear() {
a = 5
}
}
This says "I am taking responsibility to make sure that a is going to be set before it is used." Which is exactly what you are doing. I do this all the time when I need to pass self as a delegate. I wish the compiler could explore every possible code path across every method call, but it doesn't today (and given what it might do to compile times, maybe I don't wish that).
but I feel that this is terrible style because a will never be nil after the constructor returns
That's exactly the point of ! types. They should never be nil by the time any other object can get their hands on it. If it could be nil, then you shouldn't be using !. I agree that ! is dangerous (since the compiler is no longer helping you), but it's not bad style.
The only other reasonable approach IMO is to assign default values. I wouldn't use the actual values of course; that would an invitation to subtle bugs. I would just use some default value to get things in place.
class A: NSObject {
var a = Int.min // -1 might be reasonable. I just like it to be clearly wrong
override init() {
super.init()
clear()
}
func clear() {
a = 5
}
}
I don't love this approach, though I've used it in a few places. I don't think it gives you any safety over the ! (it won't crash, but you'll almost certainly give you subtle behavioral bugs, and I'd rather the crash). This is a place where the programmer must pay attention because the compiler is powerful enough to prove everything's correct, and the point of ! is to mark those places.

Understanding optional global variables in swift

I'm working through a book on Swift and I understand the idea of scope in functions so what I'd like to understand next is why we set global variables using optional types in classes. Honestly it looks like we don't set these variables per say but just let the class know that there will be a variable of a specific type somewhere throughout the code base: var sut: ItemManager!.
From what I understand the variable sut is an unwrapped optional of the type ItemManger which definitely has a valid value and not nil. The reason we've set it with an exclamation mark or question mark is because there isn't an initializer within this class. What isn't clear is since this class doesn't have an initializer, what factors come into play when deciding weather to set this global variable to an optional using a questions mark or implicitly unwrapped with an exclamation mark?
import XCTest
#testable import ToDo
class ItemManagerTests: XCTestCase {
var sut: ItemManager!
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
sut = ItemManager.init()
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func test_ToDoCount_InitiallySetAtZero(){
let sut = ItemManager.init()
XCTAssertEqual(sut.toDoCount, 0)
}
func test_DoneCount_InitiallySetAtZero(){
let sut = ItemManager.init()
XCTAssertEqual(sut.doneCount, 0)
}
}
sut is not a global variable.
sut is an instance variable of the ItemManagerTests. Every time a new instance, i.e. an object, of a ItemManagerTests is instantiated, memory is allocated for sut.
It's of type ItemManager!. This is an implicitly unwrapped optional. It's similar to ItemManager? (a.k.a. Optional), with the distinction that it is implicitly forcefully unwrapped everywhere it's used directly, as if the force unwrap operator (!) was used.
Its value is initialized to nil, but is set to a new ItemManager object when setUp is called by Xcode's testing framework.
Firstly, sut is not a global. A global variable in Swift is declared outside any enclosing scope (So, before the class statement). sut is an instance property; each instance of your ItemManagerTests class will have a sut property.
In Swift, all non-optional properties must be initialised by the time the initialiser has completed. In some cases this can be achieved by code inside the initialiser and in other cases by assigning a default value.
In some cases, s you cannot assign or don't want to assign a default value and you can't (or don't want to) override the initialiser.
If you used a normal optional then the compiler would be satisfied that the property wasn't initialised by default or in the initialiser, but each time you referred to the property you would have to unwrap it (e.g. sut?.donecount).
Since your test case is assigning a value to sut in setup, you know that it will have a value and you could use a force unwrap (e.g. sut!.donecount), or, take it one step further and use an implicitly unwrapped optional. This allows you to refer to the property without any unwrapping, but it is still an optional and will still cause a crash if it is nil.
Note that your current code isn't even using the property, since you are allocating a local variable in your test cases. You can use:
class ItemManagerTests: XCTestCase {
var sut: ItemManager!
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
sut = ItemManager.init()
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func test_ToDoCount_InitiallySetAtZero() {
XCTAssertEqual(sut.toDoCount, 0)
}
func test_DoneCount_InitiallySetAtZero() {
XCTAssertEqual(sut.doneCount, 0)
}
}
The reason we've set it with an exclamation mark or question mark is
because there isn't an initializer within this class
Because when every instance of a class is initialized, it has to allocate memory for its properties/instance var. So with var sut: ItemManager!, the value of sut is nil when init. Without !, ? the compiler can't allocate, init, so you have to init manually in an initializer. That is what compiler told you.
For using ! or ?.
! is used when the property/var always has value after the first assigning. And after the first time it's assigned, it can't be nil later
? is used when the pro/var can have value or not

Cannot place Swift assertions inside init() before call to super.init()

I would like to use assertions inside init() before it calls super.init(). They are meant to check certain invariant conditions when calculating local properties.
However, if I try place such assertions I get this error message:
'self' used before super.init call
I assume this is because assert prints a description of the object and hence uses self, which at this point is not yet fully initialized. Is there anything I can do about this (i.e. calculate local properties before call to super.init() and place assertions there).
UPDATE Here is an concise example:
class Test: NSObject {
let x: Int
override init() {
x = 1
assert(x == 1) // causes error
super.init()
assert(x == 1) // no error
}
}
The reason is that asserthas to read the value of the property x.
Although you can omit self as a convenience syntax assert uses the implicit setter of the property and has to call self.x == 1.
That's what the error message says.
You can use temporary local variables to calculate what you need but you cannot use instance variables before calling super.init().
Alternatively use a struct or protocol type which doesn't require to call super.init()

Swift - Extra Argument in call

I am trying to call a function declared in ViewController class from DetailViewController class.
When trying to debug the 'Extra Argument in call" error pops up.
In ViewController class:
func setCity(item : Cities, index : Int)
{
citiesArray!.removeObjectAtIndex(index)
citiesArray!.insertObject(item, atIndex: index)
}
In detailViewController Class
// city of type Cities
ViewController.setCity(city ,5 ) //Error: "Extra argument in call"
This is pretty simple yet I'm baffled.
In some cases, "Extra argument in call" is given even if the call looks right, if the types of the arguments don't match that of the function declaration. From your question, it looks like you're trying to call an instance method as a class method, which I've found to be one of those cases. For example, this code gives the exact same error:
class Foo {
func name(a:Int, b: Int) -> String {
return ""
}
}
class Bar : Foo {
init() {
super.init()
Foo.name(1, b: 2)
}
}
You can solve this in your code by changing your declaration of setCity to be class func setCity(...) (mentioned in the comments); this will allow the ViewController.setCity call to work as expected, but I'm guessing that you want setCity to be an instance method since it appears to modify instance state. You probably want to get an instance to your ViewController class and use that to call the setCity method. Illustrated using the code example above, we can change Bar as such:
class Bar : Foo {
init() {
super.init()
let foo = Foo()
foo.name(1, b: 2)
}
}
Voila, no more error.
SwiftUI:
This error message "extra argument in call" is also shown, when all your code is correct, but the maximum number of views in a container is exceeded (in SwiftUI). The max = 10, so if you have some different TextViews, images and some Spacers() between them, you quickly can exceed this number.
I had this problem and solved it by "grouping" some of the views to a sub container "Group":
VStack {
Text("Congratulations")
.font(.largeTitle)
.fontWeight(.bold)
Spacer()
// This grouping solved the problem
Group {
Text("You mastered a maze with 6 rooms!")
Text("You found all the 3 hidden items")
}
Spacer()
// other views ...
}
In my case calling non-static function from static function caused this error. Changing function to static fixed the error.
This error will ensue, if there is a conflict between a class/struct method, and a global method with same name but different arguments. For instance, the following code will generate this error:
You might want to check if there is such conflict for your setCity method.
You have to call it like this:
ViewController.setCity(city, index: 5)
Swift has (as Objective-C) named parameters.
I have had this error when there is nothing at all wrong with the expression highlighted by the compiler and nothing wrong with the arguments specified, but there is an error on a completely different line somehow linked to the original one. For example: initialising object (a) with objects (b) and (c), themselves initialised with (d) and (e) respectively. The compiler says extra argument on (b), but in fact the error is a type mismatch between the type of (e) and the expected argument to (c).
So, basically, check the whole expression. If necessary, decompose it assigning the parts to temporary variables.
And wait for Apple to fix it.
This can also happen if you have more than 10 views for ViewBuilder arguments in SwiftUI
This is not the direct answer to this question but might help someone.
In my case problem was that class with same name existed in a different (Test) target.
While running Test target I got this error as a new argument was added to init method but was missing in the class in Test target.
Adding the same argument also to other init solved the issue.
In latest Swift 2.2, I had a similar error thrown which took me a while to sort out the silly mistake
class Cat {
var color: String
var age: Int
init (color: String, age: Int) {
self.color = color
self.age = age
}
convenience init (color: String) {
self.init(color: color, age: 1){ //Here is where the error was "Extra argument 'age' in call
}
}
}
var RedCat = Cat(color: "red")
print("RedCat is \(RedCat.color) and \(RedCat.age) year(s) old!")
The fix was rather simple, just removing the additional '{ }' after 'self.init(color: color, age: 1)' did the trick, i.e
convenience init (color: String) {
self.init(color: color, age: 1)
}
which ultimately gives the below output
"RedCat is red and 1 year(s) old!"