SpriteKit SKAction.runBlock passing parameter issue - swift

I'm experiencing some weird behavior using SKAction.runBlock. Basically I'm using the following syntax :
AnAction = SKAction.runBlock({ SomeFunction(SomeParameter) })
in a switch statement in which I have 100+ case
IDTargetSprite = FindTargetSprite()
TypeSprite = FindTypeSprite(IDTargetSprite)
switch TypeAction
{
...
case eTypeAction.DropBomb.rawValue:
ActionBuilt = SKAction.runBlock({ DropBomb(IDTargetSprite) })
case eTypeAction.DropStar.rawValue:
dParamsAction = LoadParamsAction()
ActionBuilt = SKAction.runBlock({ DropStar(IDTargetSprite, dParamsAction) })
...
}
This code is included in a loop at the beginning of the program in which I go over an array of TypeAction
for TypeAction in tTypeAction
{
// execute above code
}
then I execute the actions like this in another loop later :
switch TypeSprite
{
case eActionTarget.SpriteA.rawValue:
SpriteA.runAction(ActionBuilt)
case eActionTarget.SpriteB.rawValue:
SpriteB.runAction(ActionBuilt)
}
So far nothing unusual, except that it does not work and when I try to understand why I see that the parameter passed to the block is not good but in a very weird way : some times the parameter passed to the function in the block (IDTargetSprite or dParamsAction) does not have the right value inside the function called, it kept the value he just had in the previous iteration of the loop.
In these cases, if I look at the value of the parameter just before the runBlock lines, the value is right but when the action is executed the value found within the function called is not right anymore and equal to the value of the previous iteration of the loop.
I have founded a way (but not yet extensively tested) by doing this :
case eTypeAction.DropBomb.rawValue:
TempParam = IDTargetSprite
ActionBuilt = SKAction.runBlock({ DropBomb(TempParam) })
where I have to have a specific TempParam for each case...
Am I missing something obvious in the use of SKAction.runBlock or block in general ?

Found a quick way by passing constant instead of variable as parameters. So it works if I do like this :
case eTypeAction.DropStar.rawValue:
let dParamsAction = LoadParamsAction()
ActionBuilt = SKAction.runBlock({ DropStar(IDTargetSprite, dParamsAction) })

Related

swift 5.1 evaluate a closure in the wrong way

I'm having an issue with evaluation of one line of code
if i break it down to two lines, it's working , but in one line of code, it's just evaluate in a 'new' to a 'wrong' way.
my main reason for asking this question, is not to solve it, I know I can use parenthesis to solve it, and break it to Two line, but don't want to solve it, I just want to know why its evaluated like this , and if there's a solution for this : some setting to patch , in Order THAT it will work in ONE LINE OF CODE :
Heres the code that working in Two lines
Heres the code that trying to do the same thing, but rise an error as you can see:
full code of both working and not working :
class ClosuresStack {
var dic = Dictionary<String,(()->String)->String >()
subscript(_ str:String)-> (()->String)->String {
get {
return dic[str]!
}
set {
dic[str] = newValue
}
}
}
func createClosuresStak() -> ClosuresStack {
let cs = ClosuresStack()
func takesAClosureReturnA_string(_ closure:()->String) ->String {
return closure() + " Two"
}
cs["C"] = takesAClosureReturnA_string
return cs
}
let c = createClosuresStak()["C"]
let str = c{"One"}
print(str) // print: One Two
let c = createClosuresStak()["C"]{"One"} // error -->
now, I want to somehow understand how to change it that it will work in ONE LINE OF CODE : meaning that the evaluation of 'createClosuresStak()["C"]{"One"}' will create a closure after ["C"] , and then from that point writing the {"One"}
will make it a full evaluate of the line :
let c = createClosuresStak()["C"]{"One"}
making 'c' a String
if that's not possible, I need to know it Too , tnx :)
UPDATE
tnx for the comments , its help me understand the problem more clearly :
1) im understanding that the createClosuresStak()["C"]{"One"}
acutely trying to add the string 'One' as another parameter to the sub script , and there for the error from the compiler was that is cannot subscript (String,()->String} , 'C' as the string inside the [] , and the other parameter {"One"} -> BUT , isn't that some kind of a bug?, been that i'm using [] ,Cleary the compiler need to 'understand' that I want to subscript a String, also by power of inferring that swift has,
2) now I'm still trying to get that syntax to work as it is so I try to change some things, in order to get it to work :
so I created a function that take a string, and return a dictionary of type : Dictionary<String,()->String>, and then trying so subscript it
and the compiler don't rise an error that way :
func closuresDictionary(_ s:String) -> Dictionary<String,()->String> {
var dic = Dictionary<String,()->String>()
func foo()->String {
return s + " Two"
}
dic["C"] = foo
return dic
}
let c = closuresDictionary("One")["C"]{ "SomeString" }
c is now a closure of type ()->String which does noting with string that I put inside, so the syntax works, but the outcome is not doing anything.
when im changing the return type of the dictionary to a different closure : (String)->String instead of ()->String , im getting the same old error, that I'm trying to subscript a (String,(String)->String)
and I need a function that will take the string inside the {} , and create something from it meaning that I need to subscript to return a closure of (String)->String
its seems like there's no way to do that
im adding two more pictures of my last trying in order to get this line of code in current syntax to work
the wanted syntax working but the outcome is not an outcome not doing any thing with the string inside the {}:
same error, by changing the function to (String)->String
Your example:
let c = createClosuresStak()["C"]{"One"}
is using trailing closure syntax.
Trailing closure syntax works by including the trailing closure as an additional parameter to a function call. Subscripting an array is really a function call under the hood (to a function called subscript), and Swift is trying to pass that closure as a second parameter to the subscripting call, which is what the error is explaining:
Cannot subscript a value of type 'ClosuresStack' with an argument of type '(String, () -> String)'.
In other words, you can't pass both "C" and the closure {"One"} to the subscripting function.
There are at least 3 ways to fix this and still put it on one line:
Option 1: Use an explicit call to pass the closure instead of using trailing closure syntax
Wrap the closure in () to make the call explicit:
let c1 = createClosuresStak()["C"]({"One"})
print(c1)
Option 2: Wrap the createClosureStak()["C"] in parentheses
That lets Swift know the subscripting only gets "C" as a parameter and allows trailing closure syntax to work as expected:
let c2 = (createClosuresStak()["C"]){"One"}
print(c2)
Option 3: Add .self to the result before the trailing closure syntax:
That again finishes the subscripting call and avoids the confusion.
let c3 = createClosuresStak()["C"].self {"One"}
print(c3)
Personally, I would choose option one, because trailing closure syntax is unnecessary syntactic sugar that clearly is not working here.

Swift initialization when you don't know what value to initialize with

I am learning swift and am quite surprised at a paradigm, I would like to better understand, about initialization. (please note I am not a professional developer).
In the following code, I get an error at build time on the last line :
Variable 'ex1' used before being initialized
class Example {
var data:Int
init (value: Int){
self.data = value
}
}
var ex1: Example
var ex2: Example
for i in 1...2 {
switch i {
case 1:
ex1 = Example(value: 10)
case 2 :
ex2 = Example(value: 20)
default:
break
}
}
var result = ex1
So, my understanding is that the compiler is not smart enough to understand that ex1 is initialized at execution in the "for" loop before.
Ok fine. So now I have to initialize my variables a priori, to a random value. This seems odd to me, as not initializing them feels as a safety to spot errors.
Now I have to carry a possibly wrong value without warning. Is this the good practice ? Am I missing something ?
It is possible to declare a variable without initializing it immediately and without the compiler complaining. For example:
let ex: Example
if someBoolean {
ex = Example(value: 10)
} else {
ex = Example(value: 20)
}
print(ex) // this should work fine
However, the compiler only knows how to handle this if all declared variables are initialized in all logical paths. In your question, you only initialize one variable in each case of the switch, and neither in the default. You may know that they will all be initialized by the end of the loop, but the compiler isn't smart enough to figure that out.
There's also very few situations where you would want to do that anyway. If you find yourself needing to initialize different variables in different stages of a loop, there's a good chance you're doing something wrong or you could write your code more efficiently. It's hard to say how to simplify your actual code without seeing it, but for the example code you would get the same result by simply immediately initializing both variables.
If you must do it that way, and you know all of the variables will be initialized before they're used but the compiler doesn't, you can use implicitly unwrapped optionals:
var ex1: Example!
var ex2: Example!
// other code is the same
As with all uses of force/implicit unwrapping, this comes with the risk of your app crashing if you made a mistake or forgot an edge case, so I only recommend it if there aren't any other ways of doing it, or if the other ways require excessively complex code.

Capturing a property of an object for a closure in Swift

I'm rewriting some code to append jobs to an array of closures rather than execute them directly:
var someObject: SomeType?
var jobsArray: [() -> ()] = []
// before rewriting
doExpensiveOperation(someObject!.property)
// 1st attempt at rewriting
jobsArray.append {
doExpensiveOperation(someObject!.property)
}
However, because the value of someObject might change before the closure is executed, I'm now adding a closure list as follows:
// 2nd attempt at rewriting
jobsArray.append { [someObject] in
doExpensiveOperation(someObject!.property)
}
Hopefully then if, say, someObject is subsequently set to nil before the closure executes, the closure will still access the intended instance.
But what's the neatest way to deal with the possibility that the value of .property might change before the closure is executed? Is there a better way than this?
// 3rd attempt at rewriting
let p = someObject!.property
jobsArray.append {
doExpensiveOperation(p)
}
I'm not very keen on this solution because it means changing the original line of code. I'd prefer this but it doesn't work:
// 4th attempt at rewriting
jobsArray.append { [someObject!.property] in
doExpensiveOperation(someObject!.property)
}
Pretty new to Swift, so all guidance gratefully received. Thanks!
A capture list such as [someObject] is actually syntactic sugar for [someObject = someObject], where the right hand side can be an arbitrary expression that gets bound to a new constant upon the closure being formed.
Therefore one option is to write your example as:
jobsArray.append { [property = someObject!.property] in
doExpensiveOperation(property)
}

Swift canceling rest of function

Remember the good old days when End would stop everything and goto would take you somewhere without coming back? Well, "End" is essentially what I am trying to do, is cancel the rest of a function.
ie.
func function () {
x = 5
//code to cancel and stop function
x = 0
}
I want this to readout x = 5. The reason I want to do this is because I have numerous functions called from within functions. To keep it simple, is there anyway for this to happen?
From Apple (search for guard):
Early Exit
A guard statement, like an if statement, executes statements depending
on the Boolean value of an expression. You use a guard statement to
require that a condition must be true in order for the code after the
guard statement to be executed.
That would be:
func function() {
x = 5
guard <yourConditionToContinue> else {
return
}
// Code to be executed if <yourConditionToContinue> is met.
}

Swift: How do I wait until a variable has a certain value

I am trying to write code that waits until a variable equals a certain value. Here is the code I have written:
var i = 0
for i in 1...10 {
playtrial()
repeat {
} while (WasItCorrect=="")
WasItCorrect = ""
}
The idea is that the function will call playtrial, wait until the variable WasItCorrect has a value (other than ""), reset WasItCorrect, and then repeat (for a total of 10 times).
The code produces no errors. However, after plastral is done, the program seems to stop responding and does not allow any new input / button pressing.
I assume the problem is that my repeat/while loop goes forever and never gives a chance for anything else to happen and therefore other functions which change the value of WasItCorrect cannot run. As a VisualBasic 6 programmer who is just starting to learn Swift, I would guess I would need the Swift version of DoEvents. Is that true?
Any advice on how to fix this code would be greatly appreciated.
You can use didSet property of the variable. It will be called whenever the value changed.
var i = 0 {
didSet {
print("Hello World.")
if i == certainValue {
// do something
}
}
}