How do I get the screen dimensions before init()? - swift

I'm making a small game in swift and want to make my player move across 3 fixed points on the screen.
I created an array to store the values in which I calculate in the init function, but the compiler doesn't agree with me not declaring the array before super.init(). Here's what I have
//properties
var player:SKSpriteNode = SKSpriteNode()
var playerPos = 1
var legalPositions: [CGFloat]
override init(size:CGSize)
{
super.init(size: size)
legalPositions[0] = self.frame.width/4
legalPositions[0] = self.frame.width/2
legalPositions[0] = self.frame.width/0.5
}
When I try to build it I get the error
GameScene.swift:31:9: Property 'self.legalPositions' not initialized at super.init call
Is there a way to initialize the property before calling the init function?

This is actually a good use case for Implicitly Unwrapped Optionals. Basically you'd define legalPositions as [CGFloat]! which will default it to nil. Then you can set it after your call to super.init(size: size).
Since it's implicitly unwrapped, you don't have to worry about unwrapping it with optional binding or forced unwrapping each time you use it. And, since you're always setting it in your init method, you don't have to worry about it being nil and causing a crash.
//properties
var player:SKSpriteNode = SKSpriteNode()
var playerPos = 1
var legalPositions: [CGFloat]!
override init(size:CGSize)
{
super.init(size: size)
legalPositions[0] = self.frame.width/4
legalPositions[0] = self.frame.width/2
legalPositions[0] = self.frame.width/0.5
}

Yes, you can use a dummy value that gets overwritten in the initializer:
var legalPositions: [CGFloat] = [0]
That way, the legalPositions array is initialized before you're calling the base class initializer. Swift's two-phase initialization requires this process in order to be able to guarantee that all properties are initialized after creating a new instance of a class.
The benefit of this approach is that there's no need for implicitly unwrapped optionals. They open up the possibility for nil values being set, which is not what you want.
Note that the first two assignments in your snippet are unnecessary since the third one will overwrite the value of the first item of the legalPositions array anyway.

Related

How to Initialize OpaquePointer in Swift

I want to use OpaquePointer in Swift.
This is my Code.
#State var OP : OpaquePointer = OpaquePointer.init(bitPattern: <#T##Int#>)
In my Project, I use SwiftGit2 Library.
And because of that, I must initialize the OpaquePointer.
It doesn't matter what way.
In addition, I put any bitPatter in the Parameter.
And, here is the Error
#State var OP : OpaquePointer = OpaquePointer.init(bitPattern: 1)
Value of optional type 'OpaquePointer?' must be unwrapped to a value of type 'OpaquePointer'
Thank you.
What the compile error is telling you is that the initialiser that takes a bit pattern returns an optional OpaquePointer.
So you either should use it as an optional:
#State var OP: OpaquePointer? = OpaquePointer(bitPattern: 1)
Or if you're a 100% certain the opaque pointer is never nil, you can force unwrap it:
#State var OP: OpaquePointer = OpaquePointer(bitPattern: 1)!
However, if the OpaquePointer fails to initialise, your app will crash.
Please note that a bitPattern of 1 will most certainly fail, since this should point to an address in memory.
A better way to get an OpaquePointer is to derive it from an UnsafePointer or UnsafeMutablePointer, so you're sure the pointer is actually pointing to the thing you want it to.
What are you trying to point to? And do you really need an OpaquePointer? If so, I'd highly advise to not mess with pointers inside your SwiftUI view, do this stuff in a (or multiple) layers 'above' that (so either ViewModel or Model/Data).

Cannot use instance member 'ie' within property initializer [duplicate]

This question already has answers here:
How to initialize properties that depend on each other
(4 answers)
Closed 5 years ago.
The code:
class man: human {
var ie = "ie/1/3/1///"
let pe = "pe/1/3/3///"
let ol = "ol/1/1/1///"
let sejong = "sejong/3/1/1///"
let gong = "gong/1/1/1///"
let mans = [ie, pe, ol, sejong, gong]
}
The error:
Col 15: cannot use instance member 'ie' within property initializer;
property initializers run before 'self' is available
How can I debug this?
Instance variables cannot be used upon non-lazy initialization of each other.
Consider your code taking into account the following thing: it doesn't matter in which order you define and initialize a variable. From your (developer) perspective, they will all get initialized simultaneously (they are, of course, not from low-level perspective).
It means that when you write:
let mans = [ie,pe,ol,sejong,gong]
You basically tell compiler to make up an array of something not yet initialized. None of the constituents of your array are existent when you make this assignment.
Common solution is to make your initialization - that which relies on other instance variables - lazy:
class man {
var ie = "ie/1/3/1///"
let pe = "pe/1/3/3///"
let ol = "ol/1/1/1///"
let sejong = "sejong/3/1/1///"
let gong = "gong/1/1/1///"
lazy var mans : [String] = {
[ie,pe,ol,sejong,gong]
}()
}
Unlike ordinary, lazy variable obtains its value on first use of this variable, but not instantly once an object has been created. You tell compiler: I don't mean to make up an array right away, but make me one when I firstly use this variable, after the object has already been created:
let theMan = man()
On this stage, theMan.mans property is not yet initialized. However, suffice it to perform even basic operation, like printing:
print(theMan.mans)
that this property is initialized. This is called lazy initialization and this is the way you can make up an array consisting of other instance variables upon initialization. Of course, you should remember that any dependent data may be modified and affect the init.

NSDictionary: error Unexpectedly found nil while unwrapping an Optional value

I was playing with an idea in the Playground. This idea NSDictionaries used as a way to "keep" data.
I started creating a variable called layer type [String:[String:String]].
So, an error occurred. I'm an hour trying to solve, and could not find the error reason happen. I am new to Swift.
var layers: [String:[String:String]]!
layers["key"] = ["layer":"layer"]
layers["key2"] = ["asd":"12312"]
print(layers)
Could someone help me? Or tell me how can I get the end result of my NSDictionaries?
You've declared the type of the variable layers but you haven't allocated storage for it yet.
Try this instead:
var layers = [String:[String:String]]()
If you insist on layers being an implicitly unwrapped optional, then you could initialize it this way:
var layers: [String:[String:String]]! = [:]
This would allow you to assign nil to it later, but that would be dangerous because it would crash if you try to use it when it is nil. That is the reason for your crash.
If you want it to be optional, you should declare it with ? so that it can be safely unwrapped:
var layers: [String:[String:String]]?
// Sometime later
layers = [:]
// use optional chaining to assign values, this safely does
// nothing if layers is nil
layers?["key"] = ["layer":"layer"]
layers?["key2"] = ["asd":"12312"]
// use optional binding to unwrap layers
if let unwrapped_layers = layers {
print(unwrapped_layers)
}
Try this in a Playground, and then try commenting out the layers = [:] part and you will see that it doesn't crash because all accesses to layers are done in a safe manner that properly handle the case when layers is nil.

assigned enum value doesn't change

I have a Player class which has an enum for changing the Player type. See init below.
init(playerType: PlayerType) {
self.playerType = playerType
spriteTexture = SKTexture(imageNamed: playerType.simpleDescription())
sprite = SKSpriteNode(texture: spriteTexture)
sprite.name = playerCategoryName
sprite.zPosition = 10
}
In the GameScene I change some player properties using the following method, but the enum value for player.playerType doesn't change when assigned. What is cause and how can I correct this?
func changePlayer (newPlayerType:Player.PlayerType) {
player.spriteTexture = SKTexture(imageNamed: newPlayerType.simpleDescription())
let action = SKAction.setTexture(player.spriteTexture)
player.sprite.runAction(action)
player.playerType == newPlayerType //this doesn't seem to work, player.playerType remains unchanged every time
println("raw value of newPlayerType is \(newPlayerType.rawValue)")
println("raw value of player.playerType is \(player.playerType.rawValue)")
}
You cannot assign a different case to an existing enum; you can make an existing enum change its own case, but not by assigning into it.
You can replace an enum instance (with one case) by another enum instance (with a different case) of the same enum, but only if the enum is a var reference, not a let reference.
== is the equality operator.
You're looking for = for assignment (note, only one equals sign).
Check your enum reference it should be var not let

how to declare variables in Swift

I am learning Swift and noticed there are different ways to declare a variable.
Questions: What is the difference between the following 3 lines, and when to use which?
var mySprite: SKSpriteNode?
var mySprite = SKSpriteNode()
var mySprite = SKSpriteNode(imageNamed: String())
Similarly, what is the difference between the following 3 lines and when to use which?
var myLabel: SKLabelNode?
var myLabel = SKLabelNode()
var myLabel = SKLabelNode(fontNamed: "name")
var mySprite: SKSpriteNode?
The type of your variable is optional in this case which means it is nil at the time you create an instance of a class containing this variable, which is a property of your class.
var mySprite = SKSpriteNode()
Here you initialize your property at the time you make an instance of your class which contains it.
var mySprite = SKSpriteNode(imageNamed: String())
Similar as number two. But different in the initialization method used. Init().
There is no difference in 2 last lines in both cases in terms of declaring a variable.
First declaration differs from last two in that it is declaring an optional value of type SKLabelNode with a default value of nil.
The first line of code will declare the variable as
Optional
. Optional variable in swift means that it could be nil or it could have a value, and you always should safely unwrap it using the "if let" expression.
the second and the third line will define a new object of the class SKSpriteNode or SKLabelNode but each with different parameters.