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

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.

Related

How do I make the properties of a class iterable is swift using Sequence and IteratorProtocol?

I would like to make my class in Swift iterable.
My goal is to be able to create a class called Contact that holds properties such as the givenName, familyName, and middleName, like iOS CNContact. I would like to be able to have a class function that compares two instances of the class Contact, and finds which property the two contact objects have that match, so that say if both contacts have the same value for the givenName property, then the class function returns the result.
Here is a sample code:
class Contact {
static func compare(left: Contact, right: Contact) {
for property in left.properties {
if property == right.property {
// match is found
}
}
}
var givenName: String = ""
var familyName: String = ""
var middleName: String = ""
private var properties = [givenName, familyName, middleName]
}
let left = Contact()
let right = Contact()
Contact.compare(left: left, right: right)
I found posts that used mirroring and reflection, but I want to use Sequence and IteratorProtocol. I suspect there is already the ability to do exactly what I want to do. It seems to be a logical need that would arise.
What is the way to do this that has a balance between simplicity and the ability to address common needs to iterate through the instance properties of a class. An enumeration can be declared with given has values. Is there a way to make that work for this purpose? Is there a protocol that a class can use that assigns a hash value or other identifiable value that would allow for a sequential order to iterate through the properties of a class?
I was able to find posts and documentation that allowed me to get as far as the following code in playground that generated the following in debug window:
struct Name: Sequence {
typealias Iterator = NameIterator
typealias Element = Name
typealias Name = String
var name = "<name>"
func makeIterator() -> NameIterator {
return NameIterator()
}
}
struct NameIterator: IteratorProtocol {
typealias Iterator = String
typealias Element = Name
typealias Name = String
mutating func next() -> Name? {
let nextName = Name()
return nextName
}
}
let nameStrings = ["Debbie", "Harper", "Indivan", "Juniella"]
for nameString in nameStrings {
print(nameString)
}
Debbie
Harper
Indivan
Juniella
If you really don't want to use mirror, a straightforward way is to cycle through a list of key paths. This is particularly easy in your case because the properties are all strings:
class Contact {
static let properties = [\Contact.givenName, \Contact.familyName, \Contact.middleName]
static func compare(left: Contact, right: Contact) {
for property in properties {
if left[keyPath: property] == right[keyPath: property] {
print("got a match"); return
}
}
print("no match")
}
var givenName: String = ""
var familyName: String = ""
var middleName: String = ""
}
I think there's some confusion going on here.
The Sequence protocol and friends (IteratorProtocol, Collection, etc.) exist for you to be able to define custom sequences/collections that can leverage the existing collection algorithms (e.g. if you conform to Sequence, your type gets map "for free"). It has absolutely nothing to do with accessing object properties. If you want to do that, the only official reflection API in Swift is Mirror.
It's possible to...
...just Mirror, to create a standard collection (e.g. Array) of properties of an object
...just Sequence/Collection, to create a custom collection object that lists the property values of an object from hard-coded keypaths
...or you can use both, together, to create a custom collection object that uses Mirror to automatically list the properties of an object and their values

Why Swift Enum generate object, internal variable can't be altered?

I have an enum that could generate Some object, as shown below. However, when I assign the badgeValue for the Some object, it doesn't store it.
class Some {
public var badgeValue : String = "default"
}
enum MyEnum: Int {
case x
var some: Some {
let some: Some
switch self {
case .x:
some = Some()
}
return some
}
}
let x = MyEnum(rawValue: 0)
x?.some.badgeValue = "New"
print("\(x?.some.badgeValue ?? "Nothing")") // This will print `default`
I'm expecting it to print New. Why is it print default instead? If I instantiate Some directly, then the badgeValue can be updated. What's so special about the enum store var that can't be
You have made your MyEnum var some a computed property. A computed property gets evaluated every time you read its value. Thus, every time you invoke x?.some, you get a new Some object.
As Rob pointed out, my initial suggestion for a way to fix this won't work. Enums can't have stored properites. I think you'll have to use an associated value on your enum case to do what you want.
Look on you property. It's computed. Every time you create a new instance of Some class.
var some: Some {
let some: Some
switch self {
case .x:
some = Some()
}
return some
}

Protocol function implementation without actually conforming to a protocol

I am a beginner Swift learner and I have a question about protocols. I have followed a tutorial that teaches you about linked lists, which is as follows:
Node:
class LinkedListNode<Key> {
let key: Key
var next: LinkedListNode?
weak var previous: LinkedListNode?
init (key: Key) {
self.key = key
}
}
And the linked list:
class LinkedList<Element>: CustomStringConvertible {
typealias Node = LinkedListNode<Element>
private var head: Node?
// irrelevant code removed here
var description: String {
var output = "["
var node = head
while node != nil {
output += "\(node!.key)"
node = node!.next
if node != nil { output += ", " }
}
return output + "]"
}
}
The var description: String implementation simply lets you to print each elements in the linked list.
So far, I understand the structure of the linked list, my problem isn't about the linked list actually. What I don't understand is the protocol CustomStringConvertible. Why would it be wrong if I have only the var description: String implementation without conforming to the protocol? I mean, this protocol just simply say "Hey, you need to implement var description: String because you are conformed to me, but why can't we just implement var description: String without conforming to the protocol?
Is it because in the background, there is a function or some sort that takes in a type CustomStringConvertible and run it through some code and voila! text appears.
Why can't we just implement var description: String without conforming to the protocol?
Compare:
class Foo {
var description: String { return "my awesome description" }
}
let foo = Foo()
print("\(foo)") // return "stackoverflow.Foo" (myBundleName.Foo)
and
class Foo: CustomStringConvertible {
var description: String { return "my awesome description" }
}
let foo = Foo()
print("\(foo)") // return "my awesome description"
When you use CustomStringConvertible, you warrant that this class have the variable description, then, you can call it, without knowing the others details of implementation.
Another example:
(someObject as? CustomStringConvertible).description
I don't know the type of someObject, but, if it subscriber the CustomStringConvertible, then, I can call description.
You must conform to CustomStringConvertible if you want string interpolation to use your description property.
You use string interpolation in Swift like this:
"Here's my linked list: \(linkedList)"
The compiler basically turns that into this:
String(stringInterpolation:
String(stringInterpolationSegment: "Here's my linked list: "),
String(stringInterpolationSegment: linkedList),
String(stringInterpolationSegment: ""))
There's a generic version of String(stringInterpolationSegment:) defined like this:
public init<T>(stringInterpolationSegment expr: T) {
self = String(describing: expr)
}
String(describing: ) is defined like this:
public init<Subject>(describing instance: Subject) {
self.init()
_print_unlocked(instance, &self)
}
_print_unlocked is defined like this:
internal func _print_unlocked<T, TargetStream : TextOutputStream>(
_ value: T, _ target: inout TargetStream
) {
// Optional has no representation suitable for display; therefore,
// values of optional type should be printed as a debug
// string. Check for Optional first, before checking protocol
// conformance below, because an Optional value is convertible to a
// protocol if its wrapped type conforms to that protocol.
if _isOptional(type(of: value)) {
let debugPrintable = value as! CustomDebugStringConvertible
debugPrintable.debugDescription.write(to: &target)
return
}
if case let streamableObject as TextOutputStreamable = value {
streamableObject.write(to: &target)
return
}
if case let printableObject as CustomStringConvertible = value {
printableObject.description.write(to: &target)
return
}
if case let debugPrintableObject as CustomDebugStringConvertible = value {
debugPrintableObject.debugDescription.write(to: &target)
return
}
let mirror = Mirror(reflecting: value)
_adHocPrint_unlocked(value, mirror, &target, isDebugPrint: false)
}
Notice that _print_unlocked only calls the object's description method if the object conforms to CustomStringConvertible.
If your object doesn't conform to CustomStringConvertible or one of the other protocols used in _print_unlocked, then _print_unlocked creates a Mirror for your object, which ends up just printing the object's type (e.g. MyProject.LinkedList) and nothing else.
CustomStringConvertible allows you to do a print(linkedListInstance) that will print to the console whatever is returned by the description setter.
You can find more information about this protocol here: https://developer.apple.com/reference/swift/customstringconvertible

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

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

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.