Swift SpriteKit use struct instead of class to render sprites - swift

I have been updating my game recently to use more value types. I am still not 100% confident with weak and unowned in some cases so I went the struct way to avoid strong reference cycles. As per apples newer keynotes it seems value types are they way to go for the most part anyway.
I have never seen an example where structs are used to render sprites in a spriteKit game so I wonder what the drawbacks are.
I understand that they are copied and not referenced but for my usage it seems to work.
So basically is there something I need to watch out for when doing this
struct Flag {
let post: SKSpriteNode
let flag: SKSpriteNode
init(postImage: String, flagImage: String) {
post = SKSpriteNode(imageNamed: postImage)
// other set ups for post sprite
flag = SKSpriteNode(imageNamed: flagImage)
// other set ups for flag sprite
post.addChild(flag)
}
func animate() {
// code to animate flag
}
}
Than in my SKScenes I simply add them as usual
let flag = Flag(postImage: "FlagPostImage", flagImage: "FlagImage")
flag.post.position = ...
addChild(flag.post)
flag.animate()
Now even if I create multiple flags in the same scene I seem to have no problems with this way.
I am just curious because I have never really seen an example like this so I wonder if I am missing something, like performance drawbacks etc.
Thanks for any help.

Personally I avoid creating Structs that contain Classes. Because Structs copy, each and every copy that get's passed around your app will increase the reference count of the Classes. This makes it harder to manage them instead of easier.
It is also useful to take a look at how UIKit uses Structs. A UIView is an object but has many defining properties that are Structs. For example it's frame.
Drop the code below in a playground to see some effects of this behaviour.
The protocol is just to get some meaningful feedback form the playground.
protocol IDLookable : CustomPlaygroundQuickLookable {
var id : Int { get set }
}
extension IDLookable {
func customPlaygroundQuickLook() -> PlaygroundQuickLook {
return PlaygroundQuickLook.AttributedString(NSAttributedString(string: "\(self.dynamicType) with id : \(self.id)"))
}
}
class MyClass : IDLookable {
var id : Int = 0
init(id : Int) {
self.id = id
}
}
struct MyContainerStruct : IDLookable {
var id : Int = 0
var object : MyClass
init(id : Int, object:MyClass) {
self.id = id
self.object = object
}
}
class Scope {
// ref count = 1
var object = MyClass(id: 11)
var structContainer : MyContainerStruct
init() {
// ref count = 2
structContainer = MyContainerStruct(id: 222, object: object)
messWithAClassInAStruct()
}
func messWithAClassInAStruct() {
// ref count = 3
var structContainerTwo = structContainer
structContainerTwo.id = 333
structContainerTwo.object // 11
// altering the object in one struct will obvously update all references
structContainerTwo.object.id = 1
structContainer.object // 1
structContainerTwo.object // 1
}
}
let test = Scope()
One pattern that does make it easy to work with Reference Types in Value Types is to store them as weak optionals in the Value Types. This means that something will need to have a strong reference but chances are that some Class will be responsible for creating the Structs this is a good place to keep that strong reference.
struct MyContainerStruct : IDLookable {
var id : Int = 0
weak var object : MyClass?
init(id : Int, object:MyClass) {
self.id = id
self.object = object
}
}
class Scope {
// ref count = 1
var object = MyClass(id: 11)
var structContainer : MyContainerStruct
init() {
// ref count = 1
structContainer = MyContainerStruct(id: 222, object: object)
messWithAClassInAStruct()
}
func messWithAClassInAStruct() {
// ref count = 1
var structContainerTwo = structContainer
structContainerTwo.id = 333
structContainerTwo.object // 11
}
}
let test = Scope()

Related

Swift Error Code: Instance member 'getStory' cannot be used on type 'StoryBrain'; did you mean to use a value of this type instead?

Can't figure out how to go about this swift error code... Do I need to make an instance or make it static??
struct Story{
var storyTitle : String
var choice1 : String
var choice2 : String
init(t: String,c1: String, c2: String ) {
storyTitle = t
choice1 = c1
choice2 = c2
} }
struct StoryBrain{
var storyNumber = 0
let stories = [
Story(t: "You see a fork in the road", c1: "Take a left", c2: "Take a right"),
Story(t: "You see a tiger", c1: "Shout for help", c2: "Play dead"),
Story(t: "You find a treasure chest", c1: "Open it", c2: "Check for traps")
]
func getStory() -> String{
return stories[storyNumber].storyTitle
}
mutating func nextStory(userChoice: String) {
if storyNumber + 1 < stories.count{
storyNumber += 1
} else {
storyNumber = 0
}
}
}
func updateUI(){
storyLabel.text = StoryBrain.getStory()}
I guess your doing Angelas "iOS & Swift - The Complete iOS App Development Bootcamp" course on Udemy.
Inside the ViewController, create a var:
class ViewController: UIViewController {
var storyBrain = StoryBrain()
#IBOutlet weak var storyLabel: UILabel! }
This allows you to tap into your StoryBrain Model. Good luck!
The issue is here:
StoryBrain.getStory()
^ Instance member 'getStory' cannot be used on type 'StoryBrain'
As the error indicates, getStory is an instance method, meaning that you can only call it on instances of StoryBrain. Here are a couple other suggestions:
struct StoryBrain {
// Make private by default
private let stories = [...]
private var storyNumber = 0
// Make a computed property
var currentStoryTitle: String {
stories[storyNumber].storyTitle
}
// Make the name imperative; reflects that this is a mutating function
// Also don't need mutating anymore since this is a class
func advanceStory(...) {
...
}
}
If you initialize this object, like let brain = StoryBrain(), then you can use instance members like advanceStory and currentStoryTitle on brain. You’ll want to create this object/store it in whatever class you have updateUI in. If you use the same brain from a couple different places, then you might want to use the singleton pattern, which you can see in the original edit to this answer.

Swift accessing struct by variable

Let me first excuse for probably missing something basic (and the right expression)
I have a struct where I store several values for a board game. It looks like this and includes about 20 values.
struct Werte {
static var geld: [Int] = [] {didSet {NotificationCenter.default.post(name: .ResourcenAnzeigen, object: nil, userInfo: ["which" : 0])}}
static var erz: [Int] = [] {didSet {NotificationCenter.default.post(name: .ResourcenAnzeigen, object: nil, userInfo: ["which" : 1])}}
static var temperatur = Int() {didSet {NotificationCenter.default.post(name: .TemperaturAnzeigen, object: nil)}}
}
There are several classes like this:
class KarteBlauVerbraucheErhalte {
let use: //what should I declare
let useHowMuch: Int
init(use: /*what should I declare*/ , useHowMuch: Int) {
self.use = use
self.useHowMuch = useHowMuch
}
override func Aktion() {
use += useHowMuch
}
If I declare use with Int and use init(use: Geld[0] , useHowMuch: 99) the code works - but only the class variable use increased by 99.
Geld[0] doesnt change.
How can I make Geld[0] change?
One way is this:
var use: Int {
get {
return Werte.geld[0]
}
set {
// You have access to the newValue by property name 'newValue'.
// Use it. E.g.:
Werte.geld[0] += newValue
}
}
Omit it in the constructor, as it wouldn't make sense and I do not think it compiles.
Background of why your code doesn't work: you are referring to geld[0], which is just an Int. It just passes the actual value, not a reference. But that is a whole other topic.

Swift equivalent of C++ Pointer / Reference convention?

As a C++ dev picking up Swift, I'm wondering how, in a struct or other class, to store a "pointer" or "reference" to a property of of another object of another class.
For example- a controlResponder class (or struct) that listens for events to modify the value target of a target in an effect class.
Control responder:
struct controlResponder {
var ccNum : Int! // CC number to respond to
var target : Double! // Points to the variable of another object
var min : Double!
var max : Double!
}
Effects, in my case, can be of different classes but the thing that will be targeted for modification will always be a Double (To be precise- I'm working with AudioKit and am targeting effects like AKVariableDelay.time, or AKMoogLadderFilter.cutoff )
Any insight would be greatly appreciated, thanks!
Below is an abridged version of some of my actual (non functioning) code:
import Foundation
import AudioKit
import SwiftyJSON
class Effect : AKNode, AKMIDIListener {
enum CustomError: Error {
case badEffectName
}
struct ccListener {
var ccNum : Int!
var target : Int!
var min : Double!
var max : Double!
}
var listeners : [ccListener]
var effectType = ""
var effect = AKNode()
var channel = 0
var midi : AKMIDI
init(connectionInput : AKNode, midi : AKMIDI, subJson : JSON, channel : Int) throws {
self.midi = midi
self.channel = channel
var insertType = subJson["type"]
if insertType == "distortion" {
print("Adding a DISTORTION")
effect = AKTanhDistortion(connectionInput)
if let efx = effect as? AKTanhDistortion {
let gainVal = random(subJson["gain random low"].doubleValue, subJson["gain random high"].doubleValue)
print("gainVal: \(gainVal)")
efx.pregain = gainVal
}
}
else if insertType == "moog" {
print("Adding a MOOG FILTER")
/// CUTOFF
let cutoffVal = random(subJson["cutoff random low"].doubleValue, subJson["cutoff random high"].doubleValue)
print("cutoffVal: \(cutoffVal)")
/// RESONANCE
let resonanceVal = random(subJson["resonance random low"].doubleValue, subJson["resonance random high"].doubleValue)
print("resonanceVal: \(resonanceVal)")
effect = AKMoogLadder(connectionInput,
cutoffFrequency: cutoffVal,
resonance: resonanceVal)
}
else {
print("BAD EFFECT TYPE: \(insertType)")
throw CustomError.badEffectName
}
/////// MIDIZ
midi.openInput("vIn")
super.init()
for (key, cc) in subJson["ccs"] as JSON {
let efx = effect as! AKMoogLadder
listeners.append(ccListener(ccNum: cc["cc number"].intValue, target: efx.cutoffFrequency, min: cc["min"].doubleValue, max: cc["max"].doubleValue))
}
midi.addListener(self)
print("End of Effect init()")
}
func receivedMIDIController(_ controller: MIDIByte, value: MIDIByte, channel: MIDIChannel) {
print("Self channel: \(self.channel), incoming channel: \(Int(channel))")
if self.channel == Int(channel){
print("Effect got a CC!")
}
}
func changeVal(ccNum: Int, newValue: Int) {
for listener in listeners {
if listener.ccNum == ccNum {
listener.target = newValue
}
}
}
}
In Swift, the only pointers that you can reliably store are those allocated with UnsafeMutablePointer.allocate. The pointers that you can get from the address-of operator and withUnsafeMutablePointer are cheats and they are only valid for a short time; using them beyond that point will have unpredictable results.
This means that, generally speaking, you can't store a pointer to a value type (struct instances) that was allocated with "automatic storage" (to borrow from C++ terminology). If you need to share value types, you need to wrap them, at some convenient level, in a reference type (class instances).
The most generic you could get would be to use a pair of closures, one that returns the value and another one that sets it. However, there's probably a less generic but more useful way to do it for your specific case.

Is a static boolean a reference type in Swift?

I'm making some switches. In my MenuScene class there's some booleans that are static variables, booleans, to represent the states of these switches.
Are these addressable as reference types, so I can be sure that other objects are able to change their state with a unique reference to them?
The dream, in my dreamy pseudo code, I'm hoping changes to iAmOn impact the state of myButtonABC_state
class MenuScene {
static var myButtonABC_state: Bool = false
static var myButtonXYZ_state: Bool = false
override onDidMoveToView {
let buttonABC = Button(withState: MenuScene.myButtonABC_state)
let buttonXYZ = Button(withState: MenuScene.myButtonXYZ_state)
}
}
In a button class
class Button {
var iAmOn: Bool = false
init(withState state: Bool){
iAmOn = state
}
override onTouchesBegun(... etc...){
if iAmOn { iAMOn = false }
else { iAmOn = true}
}
}
Bool is a struct in Swift; structs are value types. It doesn't matter if it's static var, class var, let, var, etc., the type is what matters--so no, Bool is value type.
I think you are not 100% on all of the terminology (mostly because Apple doesn't really cover it much in documentation as usual, lol).
There are "Swift Types" (Bool, Int, your classes/structs, etc), and "Variable/Constant Types" (which hold data in a memory register, such as references or actual-values), as well as "Memory Register Write/Read Types" (variable vs vonstant, mutable vs immutable, var vs let).
Don't be frustrated.. It's a bit confusing for everyone... Especially at first and without great documentation. (I tried learning C++ pointers early age and it was way over my head).
Here's a good reference material: (towards the bottom)
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html
Basically, if you want to hold a reference to something, you have to use a Reference Type memory register. This means using a class instance Static makes no difference:
/* Test1: */
struct Hi {
static var sup = "hey"
}
var z = Hi.sup
Hi.sup = "yo"
print(z) // prints "hey"
/* Test 2: */
class Hi2 {
static var sup = "hey"
}
var z2 = Hi2.sup
Hi2.sup = "yo"
print(z2) // Prints "hey"
If you feel like you need a pointer to something that isn't inside of a class, then you can use UnsafeMutablePointer or something like that from OBJc code.
Or, you can wrap a bool inside of a class object (which are always references).
final class RefBool {
var val: Bool
init(_ value: Bool) { val = value }
}
And here is some interesting behavior for reference types using let:
let someBool: RefBool
someBool = RefBool(true)
someBool = RefBool(false) // wont compile.. someBool is a `let`
someBool.val = false // will compile because of reference type and member is `var`

deep copy for array of objects in swift

I have this class named Meal
class Meal {
var name : String = ""
var cnt : Int = 0
var price : String = ""
var img : String = ""
var id : String = ""
init(name:String , cnt : Int, price : String, img : String, id : String) {
self.name = name
self.cnt = cnt
self.price = price
self.img = img
self.id = id
}
}
and I have an array of Meal :
var ordered = [Meal]()
I want to duplicate that array and then do some changes to the Meal instances in one of them without changing the Meal instances in the second one, how would I make a deep copy of it?
This search result didn't help me
How do I make a exact duplicate copy of an array?
Since ordered is a swift array, the statement
var orderedCopy = ordered
will effectively make a copy of the original array.
However, since Meal is a class, the new array will contain references
to the same meals referred in the original one.
If you want to copy the meals content too, so that changing a meal in one array will not change a meal in the other array, then you must define Meal as a struct, not as a class:
struct Meal {
...
From the Apple book:
Use struct to create a structure. Structures support many of the same behaviors as classes, including methods and initializers. One of the most important differences between structures and classes is that structures are always copied when they are passed around in your code, but classes are passed by reference.
To improve on #Kametrixom answer check this:
For normal objects what can be done is to implement a protocol that supports copying, and make the object class implements this protocol like this:
protocol Copying {
init(original: Self)
}
extension Copying {
func copy() -> Self {
return Self.init(original: self)
}
}
And then the Array extension for cloning:
extension Array where Element: Copying {
func clone() -> Array {
var copiedArray = Array<Element>()
for element in self {
copiedArray.append(element.copy())
}
return copiedArray
}
}
and that is pretty much it, to view code and a sample check this gist
You either have to, as #MarioZannone mentioned, make it a struct, because structs get copied automatically, or you may not want a struct and need a class. For this you have to define how to copy your class. There is the NSCopying protocol which unifies that on the ObjC world, but that makes your Swift code "unpure" in that you have to inherit from NSObject. I suggest however to define your own copying protocol like this:
protocol Copying {
init(original: Self)
}
extension Copying {
func copy() -> Self {
return Self.init(original: self)
}
}
which you can implement like this:
class Test : Copying {
var x : Int
init() {
x = 0
}
// required initializer for the Copying protocol
required init(original: Test) {
x = original.x
}
}
Within the initializer you have to copy all the state from the passed original Test on to self. Now that you implemented the protocol correctly, you can do something like this:
let original = Test()
let stillOriginal = original
let copyOriginal = original.copy()
original.x = 10
original.x // 10
stillOriginal.x // 10
copyOriginal.x // 0
This is basically the same as NSCopying just without ObjC
EDIT: Sadly this yet so beautiful protocol works very poorly with subclassing...
A simple and quick way is to map the original array into the new copy:
let copyOfPersons: [Person] = allPersons.map({(originalPerson) -> Person in
let newPerson = Person(name: originalPerson.name, age: originalPerson.age)
return newPerson
})
The new Persons will have different pointers but same values.
Based on previous answer here
If you have nested objects, i.e. subclasses to a class then what you want is True Deep Copy.
//Example
var dogsForAdoption: Array<Dog>
class Dog{
var breed: String
var owner: Person
}
So this means implementing NSCopying in every class(Dog, Person etc).
Would you do that for say 20 of your classes? what about 30..50..100? You get it right? We need native "it just works!" way. But nope we don't have one. Yet.
As of now, Feb 2021, there is no proper solution of this issue. We have many workarounds though.
Here is the one I have been using, and one with less limitations in my opinion.
Make your class conforms to codable
class Dog: Codable{
var breed : String = "JustAnyDog"
var owner: Person
}
Create this helper class
class DeepCopier {
//Used to expose generic
static func Copy<T:Codable>(of object:T) -> T?{
do{
let json = try JSONEncoder().encode(object)
return try JSONDecoder().decode(T.self, from: json)
}
catch let error{
print(error)
return nil
}
}
}
Call this method whenever you need true deep copy of your object, like this:
//Now suppose
let dog = Dog()
guard let clonedDog = DeepCopier.Copy(of: dog) else{
print("Could not detach Dog")
return
}
//Change/mutate object properties as you want
clonedDog.breed = "rottweiler"
//Also clonedDog.owner != dog.owner, as both the owner : Person have dfferent memory allocations
As you can see we are piggy backing on Swift's JSONEncoder and JSONDecoder, using power of Codable, making true deep copy no matter how many nested objects are there under our object. Just make sure all your Classes conform to Codable.
Though its NOT an ideal solution, but its one of the most effective workaround.