Convert Swift variable name to string, to use as string? - swift

I've got a texture to import that's named the same as the variable boolean that represents its state.
let myButtonNameABC = false
is there a way to convert the name of this variable to a String so it can be used to load the images that are named the same?

You can make this a function instead of a variable, and do the same thing. I'll explain it in steps.
So first, how do we make a function the same thing as a variable?
Basically, we are "wrapping" the variable inside of the function. This is a similar to approach to the first block I suggested in your latest RefBool thread, where we grabbed or set a value via function.
Here, we are using methods/functions to alter a variable directly without parameters--so, we will be using the function by name instead of using the variable by name to access it throughout our code:
enum Static {
private static var _button1 = false
static func button1() { toggle(&_button1) }
}
Note, the Enum is just a namespace for us to call the method to emphasize that the variable's name is not needed for our code to work--Only the function needs to know its name.This code would work inside of a struct or class as well, or even in a global namespace.
So here, if I want to change the value of _button1, I must call button1(). _button1 is essentially invisible to everyone and everything--and that's ok, because we only need button1() to do our work.
So now, every time we call Static.button1() , Static._button1 will toggle true to false, false to true, and so on (using the function I gave you earlier)
Why are we doing this?
Well, because there is no way to get the name of a variable without Mirror and reflection, and there is already a built-in command called #function. We are jumping through the added hoop of using the function, so we don't have to set-up a mirror and other OBJc hoops.
Let's alter our enum to demonstrate how we can get the name of the func:
enum Static {
private static var _button1 = false
static func button1() -> String {
toggle ( &_button1 )
return ( #function )
}
}
Now, when we call button1(), we do two things: 1, we toggle the state of the button (_button1), and we return a string with the name "button1()"
let buttonName = Static.button1() // buttonName = "button1()"
We can make this more usable by calling a member of String to mutate itself:
let trimmedButtonName = Static.button1().replacingOccurrences(of: "()", with: "")
// trimmedButtonName = "button1"
Let's update our enum now with this handy replacement:
enum Static {
private static var _button1 = false
static func button1() -> String {
toggle ( &_button1 )
return ( #function.replacingOccurrences(of: "()", with: "") )
}
}
And our final use case is as follows:
let buttonName = Static.button1()
Note:
You don't have to have _button1 as private, that was just to demonstrate that you don't need it to be public. If you need to grab the state of it, you can make another function, or return a tuple type from button1():
static func button1() -> (name: String, state: Bool) {
// ...
return ( #function.etc, _button1)
}
let results = Static.button1()
print( results.name ) // "button1"
print( results.state ) // true / false
You can also set something in the parameter if you need more explicit control over setting the variable, just as you would any normal function.
UPDATE:
For example, if you wanted to use explicit control, you could do:
func button1(equals: Bool? = nil) -> (name: String, state: Bool) {
if equals != nil {
// do the code mentioned above
} else {
_button1 = equals!
}
return (#function.replacingOccurrences(of: "()", with: ""), _button1)
}
let buttonState.state = Static.button1(equals: false) // false
let buttonStateAgain.state = Static.button1(equals: true) // true
print(Static.button1().name) // "button1"

You can use Mirror for this.
struct Test {
let myButtonNameABC = false
}
let childrens = Mirror(reflecting: Test()).children
childrens.forEach {
print($0.label ?? "")
}
This will print:
myButtonNameABC
You can find for information about Mirror here.

Related

Why does calling a method via a pointer end up with a different result than calling via the method directly?

In the example following, I'm calling an instance method of a View using the method directly (saveTitle), and via a pointer to that method (saveAction).
When calling the method, I am passing in the current value of the title variable.
When I call it directly, the current value matches the value inside the method.
When I call it via a pointer to the method, the value inside is the value it was when the struct was first instantiated.
It is almost like there is a new instance of the Struct being created, but without the init method being called a second time.
I suspect this has something to do with how SwiftUI handles #State modifiers, but I'm hoping someone out there will have a better understanding, and be able to enlighten me a bit.
Thanks much for your consideration :)
import SwiftUI
struct EditContentView: View {
#Binding var isPresented: Bool
#State var title: String
var saveAction: ((String) -> Void)?
var body: some View {
TextField("new title", text: $title)
Button("save") {
print("calling saveText")
// this call works as expected, the title inside the saveTitle method is the same as here
saveTitle(title)
print("now calling saveAction, a pointer to saveTitle")
// with this call the title inside the method is different than the passed in title here
// even though they are theoretically the same member of the same instance
saveAction!(title)
isPresented = false
}
}
init(isPresented: Binding<Bool>, title: String) {
print("this method only gets called once")
self._isPresented = isPresented
self._title = State(initialValue: title)
saveAction = saveTitle
}
func saveTitle(_ expected: String) {
if (expected == title) {
print("the title inside this method is the same as before the call")
}
else {
print("expected: \(expected), but got: \(title)")
}
}
}
struct ContentView: View {
#State var title = "Change me"
#State var showingEdit = false
var body: some View {
Text(title)
.onTapGesture { showingEdit.toggle() }
.sheet(isPresented: $showingEdit) {
EditContentView(isPresented: $showingEdit, title: title)
}
}
}
I don't think this is related to #State. It is just a natural consequence of structs having value semantics, i.e.
struct Foo {
init() {
print("This is run only once!")
}
var foo = 1
}
var x = Foo() // Prints: This is run only once!
let y = x // x and y are now two copies of the same *value*
x.foo = 2 // changing one of the copies doesn't affect the other
print(y.foo) // Prints: 1
Your example is essentially just a little more complicated version of the above. If you understand the above, then you can easily understand your SwiftUI case, We can actually simplify your example to one without all the SwiftUI distractions:
struct Foo {
var foo = 1
var checkFooAction: ((Int) -> Void)?
func run() {
checkFoo(expectedFoo: foo)
checkFooAction!(foo)
}
init() {
print("This is run only once!")
checkFooAction = checkFoo
}
func checkFoo(expectedFoo: Int) {
if expectedFoo == foo {
print("foo is expected")
} else {
print("expected: \(expectedFoo), actual: \(foo)")
}
}
}
var x = Foo()
x.foo = 2 // simulate changing the text in the text field
x.run()
/*
Output:
This is run only once!
foo is expected
expected: 2, actual: 1
*/
What happens is that when you do checkFooAction = checkFoo (or in your case, saveAction = saveTitle), the closure captures self. This is like the line let y = x in the first simple example.
Since this is value semantics, it captures a copy. Then the line x.foo = 2 (or in your case, the SwiftUI framework) changes the other copy that the closure didn't capture.
And finally, when you inspect what foo (or title) by calling the closure, you see the unchanged copy, analogous to inspecting y.foo in the first simple example.
If you change Foo to a class, which has reference semantics, you can see the behaviour change. Because this time, the reference to self is captured.
See also: Value and Reference Types
Now you might be wondering, why does saveAction = saveTitle capture self? Well, notice that saveTitle is an instance method, so it requires an instance of EditContentView to call, but in your function type, (String) -> Void, there is no EditContentView at all! This is why it "captures" (a copy of) the current value of self, and says "I'll just always use that".
You can make it not capture self by including EditContentView as one of the parameters:
// this doesn't actually need to be optional
var saveAction: (EditContentView, String) -> Void
assign it like this:
saveAction = { this, title in this.saveTitle(title) }
then provide self when calling it:
saveAction(self, title)
Now you won't get different copies of self flying around.

Cannot assign to property: <Enum:case> is not settable

for example we have simple enum
public enum CXActionSheetToolBarButtonItem {
case cancel
case done
case now
private static var titles: [CXActionSheetToolBarButtonItem: String] = [
.cancel: "Cancel",
.done: "Done",
.now: "Now",
]
public var title: String {
get { return CXActionSheetToolBarButtonItem.titles[self] ?? String(describing: self) }
// what am I want to do
set(value) { CXActionSheetToolBarButtonItem.titles[self] = value }
}
// what am I forced to do
public static func setTitle(_ title: String, for item: CXActionSheetToolBarButtonItem) {
CXActionSheetToolBarButtonItem.titles[item] = title
}
}
why I don't can set new title like this
CXActionSheetToolBarButtonItem.cancel.title = "asd"
compiler responded error - Cannot assign to property: 'cancel' is not settable, but I can set title with function
CXActionSheetToolBarButtonItem.setTitle("asd", for: .cancel)
what should I change for worked my var as settable? .cancel.title = "asd"
Using an enum for this seems inappropriate, but I'll address the porblem at face value. You need to mark your setter as nonmutating, so that it can be called on non-var instances of your enum:
public enum CXActionSheetToolBarButtonItem {
// ...
public var title: String {
get { return CXActionSheetToolBarButtonItem.titles[self] ?? String(describing: self) }
nonmutating set(value) { CXActionSheetToolBarButtonItem.titles[self] = value }
}
}
CXActionSheetToolBarButtonItem.cancel.title = "foo" // Works... but why would you want this?!
I think the main reason why you can't do that is because the Swift compiler sees enum cases as immutable by default (similar to an immutable struct declared with let), and here you are trying to mutate it.
A good way to see this is if you try to add a mutating function to this enum
mutating public func setTitle2(_ newValue: String) {
CXActionSheetToolBarButtonItem.titles[self] = newValue
}
You will then receive the error message
error: cannot use mutating member on immutable value: 'cancel' returns immutable value
How to work around this
One way to have a similar behavior is by changing this enum into a set of static variables (which is more consistent with what you are trying to achieve).
public struct CXActionSheetToolBarButtonItem {
var title: String
static var cancel = CXActionSheetToolBarButtonItem(title: "Cancel")
static var done = CXActionSheetToolBarButtonItem(title: "Done")
static var now = CXActionSheetToolBarButtonItem(title: "Now")
}
Now you can use the following
CXActionSheetToolBarButtonItem.cancel.title = "asd"
Also, you still have the ability to use the dot-syntax
let buttonItem: CXActionSheetToolBarButtonItem = .cancel // it works
Hope that helps!
TD;DR: Because enum is an immutable object.
First of all: Whatever you're trying to do here – it's certainly not a good approach and pretty dangerous. As Craig pointed out in his comment, you're messing around with instance properties and static properties. You can have multiple instances of an enum – but when you want to change the title of a particular instance, you also change the title for all other instances. That's unexpected behavior and you should really think of another solution.
That being said, your code actually does work – with a little modification: Instead of
CXActionSheetToolBarButtonItem.cancel.title = "asd"
you can write
var item = CXActionSheetToolBarButtonItem.cancel
item.title = "asd"
This will compile.
The reason behind it is that an enum is a value type. Everytime you create an instance of an enum type, e.g. var item = CXActionSheetToolBarButtonItem.cancel, the enum's value is copied into the new variable item. You can choose if you want that value to be mutable or not by using either var or let. That's how Swift enums are intended to be used.
A single enum case like CXActionSheetToolBarButtonItem.cancel is immutable by definition.
CXActionSheetToolBarButtonItem.cancel.title = "asd"
wouldn't have any meaning because there is no instance to which the title can be assigned. The enum case .cancel is not bound to a variable.

Swift function can be called only once

What is the simplest way to write a piece of code that can be executed only once?
I know a way but has a problem.
first, I write a Boolean variable that has negative value but can be set to positive and cannot change after that
var hasTheFunctionCalled : Bool = false {
didSet{
hasTheFunctionCalled = true
}
}
and then write the function and the code inside it:
func theFunction(){
if !hasTheFunctionCalled{
//do the thing
}
hasTheFunctionCalled = true
}
but the problem is that the variable can be changed from somewhere else in the scope and this solution doesn't really look so simple and concrete.
A simple solution is to take advantage of lazy variables in the following way:
// Declare your "once-only" closure like this
private lazy var myFunction: Void = {
// Do something once
}()
...
// Then to execute it, just call
_ = myFunction
This ensures that the code inside the myFunction closure is only executed the first time that the program runs _ = myFunction
Edit: Another approach is to use so called "dispatch once tokens". This comes from Objective-C and was available in Swift until Swift 3. It is still possible to make it work, however you will need to add a little bit of custom code. You can find more information on this post -> dispatch_once after the Swift 3 GCD API changes
Edit2: Should be _ = myFunction and not _ = myFunction(), as JohnMontgomery pointed out.
You might use a static bool inside a struct nested into the function itself doing so:
func theFunction(){
struct Holder { static var called = false }
if !Holder.called {
Holder.called = true
//do the thing
}
}
One possible technique is to put the code into the initializer of a static type property, which is guaranteed to be lazily initialized only once (even when accessed across multiple threads simultaneously):
func theFunction() {
struct Once {
static let once = Once()
init() {
print("This should be executed only once during the lifetime of the program")
}
}
_ = Once.once
}
(Compare Singleton in the "Using Swift with Cocoa and Objective-C" reference.)
Example:
print("Call #1")
theFunction()
print("Call #2")
theFunction()
print("Done")
Output:
Call #1
This should be executed only once during the lifetime of the program
Call #2
Done
You can do smth like:
class Once {
var already: Bool = false
func run(#noescape block: () -> Void) {
guard !already else { return }
block()
already = true
}
}
and than use it like
class ViewController: UIViewController {
let once = Once()
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
once.run {
cameraMan.setup()
}
}
}
ref: https://dev.to/onmyway133/how-to-run-action-once-in-swift-3k7o
Depending on what you are doing inside your method : you may check if the end result has already been accomplished :
e.g. if you instantiate a class, check if it is different from nil
You can also use UserDefaults, and the knowledge that the default UserDefault Bool is false:
if !UserDefaults.standard.bool(forKey: "ExecuteOnce") {
func()
UserDefaults.standard.set(true, forKey: "ExecuteOnce")
}
This code will execute exactly once.

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
}
}

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
}
}