Swift accessing struct by variable - swift

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.

Related

How to reference an attribute as a method default in a Swift class? [duplicate]

In a Swift class, I want to use a property as a default parameter value for a method of the same class.
Here is my code :
class animal {
var niceAnimal:Bool
var numberOfLegs:Int
init(numberOfLegs:Int,animalIsNice:Bool) {
self.numberOfLegs = numberOfLegs
self.niceAnimal = animalIsNice
}
func description(animalIsNice:Bool = niceAnimal,numberOfLegs:Int) {
// I'll write my code here
}
}
The problem is that I can't use my niceAnimal property as a default function value, because it triggers me a compile-time error :
'animal.Type' does not have a member named 'niceAnimal'
Am I doing something wrong ? Or is it impossible in Swift ? If that's impossible, do you know why ?
I don't think you're doing anything wrong.
The language specification only says that a default parameter should come before non-default parameters (p169), and that the default value is defined by an expression (p637).
It does not say what that expression is allowed to reference. It seems like it is not allowed to reference the instance on which you are calling the method, i.e., self, which seems like it would be necessary to reference self.niceAnimal.
As a workaround, you could define the default parameter as an optional with a default value of nil, and then set the actual value with an "if let" that references the member variable in the default case, like so:
class animal {
var niceAnimal: Bool
var numberOfLegs: Int
init(numberOfLegs: Int, animalIsNice: Bool) {
self.numberOfLegs = numberOfLegs
self.niceAnimal = animalIsNice
}
func description(numberOfLegs: Int, animalIsNice: Bool? = nil) {
if let animalIsNice = animalIsNice ?? self.niceAnimal {
// print
}
}
}
I think for now you can only use literals and type properties as default arguments.
The best option would be to overload the method, and you can implement the shorter version by calling the full one. I only used a struct here to omit the initializer.
struct Animal {
var niceAnimal: Bool
var numberOfLegs: Int
func description(#numberOfLegs: Int) {
description(niceAnimal, numberOfLegs: numberOfLegs)
}
func description(animalIsNice: Bool, numberOfLegs: Int) {
// do something
}
}

Swift SpriteKit use struct instead of class to render sprites

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()

Using a property as a default parameter value for a method in the same class

In a Swift class, I want to use a property as a default parameter value for a method of the same class.
Here is my code :
class animal {
var niceAnimal:Bool
var numberOfLegs:Int
init(numberOfLegs:Int,animalIsNice:Bool) {
self.numberOfLegs = numberOfLegs
self.niceAnimal = animalIsNice
}
func description(animalIsNice:Bool = niceAnimal,numberOfLegs:Int) {
// I'll write my code here
}
}
The problem is that I can't use my niceAnimal property as a default function value, because it triggers me a compile-time error :
'animal.Type' does not have a member named 'niceAnimal'
Am I doing something wrong ? Or is it impossible in Swift ? If that's impossible, do you know why ?
I don't think you're doing anything wrong.
The language specification only says that a default parameter should come before non-default parameters (p169), and that the default value is defined by an expression (p637).
It does not say what that expression is allowed to reference. It seems like it is not allowed to reference the instance on which you are calling the method, i.e., self, which seems like it would be necessary to reference self.niceAnimal.
As a workaround, you could define the default parameter as an optional with a default value of nil, and then set the actual value with an "if let" that references the member variable in the default case, like so:
class animal {
var niceAnimal: Bool
var numberOfLegs: Int
init(numberOfLegs: Int, animalIsNice: Bool) {
self.numberOfLegs = numberOfLegs
self.niceAnimal = animalIsNice
}
func description(numberOfLegs: Int, animalIsNice: Bool? = nil) {
if let animalIsNice = animalIsNice ?? self.niceAnimal {
// print
}
}
}
I think for now you can only use literals and type properties as default arguments.
The best option would be to overload the method, and you can implement the shorter version by calling the full one. I only used a struct here to omit the initializer.
struct Animal {
var niceAnimal: Bool
var numberOfLegs: Int
func description(#numberOfLegs: Int) {
description(niceAnimal, numberOfLegs: numberOfLegs)
}
func description(animalIsNice: Bool, numberOfLegs: Int) {
// do something
}
}

Re-initialize a lazy initialized variable in Swift

I have a variable that initialized as:
lazy var aClient:Clinet = {
var _aClient = Clinet(ClinetSession.shared())
_aClient.delegate = self
return _aClient
}()
The problem is, at some point, I need to reset this aClient variable so it can initialize again when the ClinetSession.shared() changed. But if I set the class to optional Clinet?, LLVM will give me an error when I try to set it to nil. If I just reset it somewhere in the code using aClient = Clinet(ClinetSession.shared()), it will end up with EXEC_BAD_ACCESS.
Is there a way that can use lazy and being allowed to reset itself?
lazy is explicitly for one-time only initialization. The model you want to adopt is probably just an initialize-on-demand model:
var aClient:Client {
if(_aClient == nil) {
_aClient = Client(ClientSession.shared())
}
return _aClient!
}
var _aClient:Client?
Now whenever _aClient is nil, it will be initialized and returned. It can be reinitialized by setting _aClient = nil
Because the behavior of lazy changed in Swift 4, I wrote a few structs that give very specific behavior, which should never change between language versions. I put these on GitHub, under the BH-1-PD license: https://github.com/RougeWare/Swift-Lazy-Patterns
ResettableLazy
Here is the one relevant to this question, which gives you a way to lazily-initialize a value, cache that value, and destroy it so it can be lazily-reinitialized later.
Note that this requires Swift 5.1! For the Swift 4 version, see version 1.1.1 of that repo.
The simple usage of this is very straightforward:
#ResettableLazy
var myLazyString = "Hello, lazy!"
print(myLazyString) // Initializes, caches, and returns the value "Hello, lazy!"
print(myLazyString) // Just returns the value "Hello, lazy!"
_myLazyString.clear()
print(myLazyString) // Initializes, caches, and returns the value "Hello, lazy!"
print(myLazyString) // Just returns the value "Hello, lazy!"
myLazyString = "Overwritten"
print(myLazyString) // Just returns the value "Overwritten"
_myLazyString.clear()
print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!"
This will print:
Hello, lazy!
Hello, lazy!
Hello, lazy!
Hello, lazy!
Overwritten
Hello, lazy!
If you have complex initializer logic, you can pass that to the property wrapper:
func makeLazyString() -> String {
print("Initializer side-effect")
return "Hello, lazy!"
}
#ResettableLazy(initializer: makeLazyString)
var myLazyString: String
print(myLazyString) // Initializes, caches, and returns the value "Hello, lazy!"
print(myLazyString) // Just returns the value "Hello, lazy!"
_myLazyString.clear()
print(myLazyString) // Initializes, caches, and returns the value "Hello, lazy!"
print(myLazyString) // Just returns the value "Hello, lazy!"
myLazyString = "Overwritten"
print(myLazyString) // Just returns the value "Overwritten"
_myLazyString.clear()
print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!"
You can also use it directly (instaed of as a property wrapper):
var myLazyString = ResettableLazy<String>() {
print("Initializer side-effect")
return "Hello, lazy!"
}
print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!"
print(myLazyString.wrappedValue) // Just returns the value "Hello, lazy!"
myLazyString.clear()
print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!"
print(myLazyString.wrappedValue) // Just returns the value "Hello, lazy!"
myLazyString.wrappedValue = "Overwritten"
print(myLazyString.wrappedValue) // Just returns the value "Overwritten"
_myLazyString.clear()
print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!"
These will both print:
Initializer side-effect
Hello, lazy!
Hello, lazy!
Initializer side-effect
Hello, lazy!
Hello, lazy!
Overwritten
Initializer side-effect
Hello, lazy!
This answer has been updated; its original solution no longer works in Swift 4 and newer.
Instead, I recommend you use one of the solutions listed above, or #PBosman's solution
Previously, this answer hinged on behavior which was a bug. Both that old version of this answer, its behavior, and why it's a bug are described in the text and comments of Swift bug SR-5172 (which has been resolved as of 2017-07-14 with PR #10,911), and it's clear that this behavior was never intentional.
That solution is in that Swift bug's text, and also in the history of this answer, but because it's a bug exploit that doesn't work in Swift 3.2+ I recommend you do not do that.
EDIT: As per Ben Leggiero's answer, lazy vars can be nilable in Swift 3.
EDIT 2: Seems like nilable lazy vars are no more.
Very late to the party, and not even sure if this will be relevant in Swift 3, but here goes. David's answer is good, but if you want to create many lazy nil-able vars, you will have to write a pretty hefty block of code. I'm trying to create an ADT that encapsulates this behaviour. Here's what I've got so far:
struct ClearableLazy<T> {
private var t: T!
private var constructor: () -> T
init(_ constructor: #escaping () -> T) {
self.constructor = constructor
}
mutating func get() -> T {
if t == nil {
t = constructor()
}
return t
}
mutating func clear() { t = nil }
}
You would then declare and use properties like this:
var aClient = ClearableLazy(Client.init)
aClient.get().delegate = self
aClient.clear()
There are things I don't like about this yet, but don't know how to improve:
You have to pass a constructor to the initializer, which looks ugly. It has the advantage, though, that you can specify exactly how new objects are to be created.
Calling get() on a property every time you want to use it is terrible. It would be slightly better if this was a computed property, not a function, but computed properties cannot be mutating.
To eliminate the need to call get(), you have to extend every type you want to use this for with initializers for ClearableLazy.
If someone feels like picking it up from here, that would be awesome.
This allows setting the property to nil to force reinitialization:
private var _recordedFileURL: NSURL!
/// Location of the recorded file
private var recordedFileURL: NSURL! {
if _recordedFileURL == nil {
let file = "recording\(arc4random()).caf"
let url = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent(file)
NSLog("FDSoundActivatedRecorder opened recording file: %#", url)
_recordedFileURL = url
}
return _recordedFileURL
}
Swift 5.1:
class Game {
private var _scores: [Double]? = nil
var scores: [Double] {
if _scores == nil {
print("Computing scores...")
_scores = [Double](repeating: 0, count: 3)
}
return _scores!
}
func resetScores() {
_scores = nil
}
}
Here is how to use:
var game = Game()
print(game.scores)
print(game.scores)
game.resetScores()
print(game.scores)
print(game.scores)
This produces the following output:
Computing scores...
[0.0, 0.0, 0.0]
[0.0, 0.0, 0.0]
Computing scores...
[0.0, 0.0, 0.0]
[0.0, 0.0, 0.0]
Swift 5.1 and Property Wrapper
#propertyWrapper
class Cached<Value: Codable> : Codable {
var cachedValue: Value?
var setter: (() -> Value)?
// Remove if you don't need your Value to be Codable
enum CodingKeys: String, CodingKey {
case cachedValue
}
init(setter: #escaping () -> Value) {
self.setter = setter
}
var wrappedValue: Value {
get {
if cachedValue == nil {
cachedValue = setter!()
}
return cachedValue!
}
set { cachedValue = nil }
}
}
class Game {
#Cached(setter: {
print("Computing scores...")
return [Double](repeating: 0, count: 3)
})
var scores: [Double]
}
We reset the cache by setting it to any value:
var game = Game()
print(game.scores)
print(game.scores)
game.scores = []
print(game.scores)
print(game.scores)
There are some good answers here. Resetting a lazy var is indeed, desirable in a lot of cases.
I think, you can also define a closure to create client and reset lazy var with this closure. Something like this:
class ClientSession {
class func shared() -> ClientSession {
return ClientSession()
}
}
class Client {
let session:ClientSession
init(_ session:ClientSession) {
self.session = session
}
}
class Test {
private let createClient = {()->(Client) in
var _aClient = Client(ClientSession.shared())
print("creating client")
return _aClient
}
lazy var aClient:Client = createClient()
func resetClient() {
self.aClient = createClient()
}
}
let test = Test()
test.aClient // creating client
test.aClient
// reset client
test.resetClient() // creating client
test.aClient
If the objective is to re-initialize a lazy property but not necessarily set it to nil, Building from Phlippie Bosman and Ben Leggiero, here is something that avoids conditional checks every time the value is read:
public struct RLazy<T> {
public var value: T
private var block: () -> T
public init(_ block: #escaping () -> T) {
self.block = block
self.value = block()
}
public mutating func reset() {
value = block()
}
}
To test:
var prefix = "a"
var test = RLazy { () -> String in
return "\(prefix)b"
}
test.value // "ab"
test.value = "c" // Changing value
test.value // "c"
prefix = "d"
test.reset() // Resetting value by executing block again
test.value // "db"
I made #David Berry's answer into a property wrapper. Works great with UI-components that you want to reload if you need to apply size changes but otherwise want to hold in their configured state.
#propertyWrapper class Reloadable<T: AnyObject> {
private let initializer: (() -> T)
private var _wrappedValue: T?
var wrappedValue: T {
if _wrappedValue == nil {
_wrappedValue = initializer()
}
return _wrappedValue!
}
init(initializer: #escaping (() -> T)) {
self.initializer = initializer
}
func nuke() {
_wrappedValue = nil
}
}
Here's an example with a CAShapeLayer. Set you variable like so:
#Reloadable<CAShapeLayer>(initializer: {
Factory.ShapeLayer.make(fromType: .circle(radius: Definitions.radius, borderWidth: Definitions.borderWidth)) // this factory call is just what I use personally to build my components
}) private var circleLayer
and when you want to reload your view just call:
_circleLayer.nuke()
Then you can use the var circleLayer as you normally would in your re-layout routine, upon which it will get re-initialized.
PS: I made a gist for the file I use in my own project: https://gist.github.com/erikmartens/b34a130d11b62400ab13a59a6c3dbd91
This seems like a pretty bad code smell. Something strange is going on with:
var _aClient = Clinet(ClinetSession.shared())
What is ClinetSession.shared()?
This looks like a static function that returns a new instance of ClinetSession on every call.
So no wonder you are not seeing changes to this object. It seems like a broken singleton to me.
class ClinetSession {
static func shared() -> Self {
ClinetSession()
}
}
Try doing this instead:
class ClinetSession {
static let shared = ClinetSession()
var value: Int = 0
}
Now if you change ClinetSession value you will see it.
There's no need for a lazy property that can be reset here as far as I can tell.
Here's a fully working example. BTW if you don't own ClinetSession then write a wrapper to control this.
class Clinet {
let clinetSession: ClinetSession
init(_ clinetSession: ClinetSession) {
self.clinetSession = clinetSession
}
var delegate: P?
}
class ClinetSession {
static let shared = ClinetSession()
var value = 0
}
protocol P { }
struct Test: P {
lazy var aClient:Clinet = {
var _aClient = Clinet(ClinetSession.shared)
_aClient.delegate = self
return _aClient
}()
mutating func updateSession() {
aClient.clinetSession.value = 10
}
}
var test = Test()
test.updateSession()
print(test.aClient.clinetSession.value)
// prints 10
Note: If you don't want to use a singleton then don't use shared() as the constructor as this is a convention. But then it's up to you to make sure you pass in the same reference as the one you want to mutate. That's your job to manage. The singleton just makes sure there is only 1 instance so this becomes simpler but has its trade offs.

My first swift hello world application, build error when trying access a class

So this is my hello world application with swift. Also not used to XCode. So, might be a silly mistake here.
I just have one class Deck.swift
class Deck {
let decks : Integer = 0
init () {
decks = 1
}
init (amountOfDecks : Integer){
decks = amountOfDecks
}
func getAmountOfCards() -> Integer {
return 0
}
}
Then I'm trying to run a unit test that look like this
import XCTest
import helloWorldv2
class helloWorldv2Tests: XCTestCase {
override func setUp() {
super.setUp()
}
override func tearDown() {
super.tearDown()
}
func testDeckConstructor() {
var deck = Deck(amountOfDecks: 2)
XCTAssert(true, "Pass")
}
func testExample() {
// This is an example of a functional test case.
XCTAssert(true, "Pass")
}
}
If I comment out the line var deck = Deck(amountOfDecks: 2) then it builds fine. If that line is included I get build failed.
Anyone know what I'm doing wrong here?
If you want to modify deck param you have to declare it like this
var decks : Int = 0
If you put let your variable is read only after first assignment.
Whilst #jaumard is correct in saying that it is var rather than let that you should be using, you should use "Int" as the type, not "Integer" (which is a protocol, not a type). You get away with it because both are superfluous because of type inference...
var decks = 0
is quite sufficient, or
var decks: Int = 0
if you want to be verbose.
See this answer.
No need to turn your let constant into a var.
Simply don't provide a default value when declaring it.
let decks : Int
Your = 0 already initializes your constant, and than you're trying to change it in init, which is not allowed for constants.