Changing Associated Value of Enum Swift

Can I change the Associated values of a enum?

Your most immediate problem is that you're attempting to change the value of an immutable variable (declared with let) when you should be declaring it with var. This won't solve this particular problem though since your name variable contains a copy of the associated value, but in general this is something that you need to be aware of.

If you want to solve this, you need to declare the adjust() function as a mutating function and reassign self on a case by case basis to be a new enum value with an associated value composed from the old one and the new one. For example:

enum SimpleEnum{
case big(String)
case small(String)
case same(String)

mutating func adjust() {
switch self{
case let .big(name):
self = .big(name + "not")
case let .small(name):
self = .small(name + "not")
case let .same(name):
self = .same(name + "not")
}
}
}

var test = SimpleEnum.big("initial")
test.adjust()

switch test {
case let .big(name):
print(name) // prints "initialnot"
case let .small(name):
print(name)
case let .same(name):
print(name)
}

Changing associated value of enum Swift

You can only assign a new enumeration value to the variable.
As in Can I change the Associated values of a enum?,
the associated values of the current values can be retrieved in a switch statement,
with a case pattern which binds the associated value to a local variable.

The currently associated filtered value is not needed, therefore
we can use a wildcard pattern _ at that position.

Since var gameOrigin: Origin? is an optional, we need an “optional pattern” with a trailing question mark.

switch gameOrigin {
case .search(let searchTerm, _)?:
gameOrigin = .search(searchTerm: searchTerm, filtered: filtered)
default:
break
}

The same can be done also in an if-statement with case and pattern
matching:

if case .search(let searchTerm, _)? = gameOrigin {
gameOrigin = .search(searchTerm: searchTerm, filtered: filtered)
}

Is it possible to change an associated value within an enum?

Rob Napier's answer is fine but I have a different understanding of your question. I would have written something like this instead:

enum MyEnum {

case SomeCase(Int?)

mutating func someFunc() {
switch self {
case .SomeCase(.Some(_)):
self = .SomeCase(5)
default:
// or case .SomeCase(.None): if you prefer
break
}
}

}

With this code, the enum is mutated with someFunc if and only if its associated value is not nil. Thus, I have the following results while testing in a Playground (results appear as comments):

var x = MyEnum.SomeCase(nil)

switch x {
case .SomeCase(.Some(let a)):
println(a)
default:
println("Associated value is nil") // Will print "Associated value is nil"
}

x = MyEnum.SomeCase(20)
x.someFunc()

switch x {
case .SomeCase(.Some(let a)):
println(a) // Will print 5
default:
println("Associated value is nil")
}

Accessing an Enumeration association value in Swift

The value is associated to an instance of the enumeration. Therefore, to access it without a switch, you need to make a getter and make it available explicitly. Something like below:

enum Number {
case int(Int)
case float(Float)

func get() -> NSNumber {
switch self {
case .int(let num):
return num
case .float(let num):
return num
}
}
}

var vInteger = Number.int(10)
var vFloat = Number.float(10.5)

println(vInteger.get())
println(vFloat.get())

Maybe in the future something like that may be automatically created or a shorter convenience could be added to the language.

Change Value of associated enum

I think I finally figured out how to do this with a set for the computed property

var info: TextFieldInfo { 
get {
switch self {
case .generic(let type, let title):
return (type, title, "")
case .entry(let type, let title, let entry):
return (type, title, entry)
}
}
set {
switch self {
case .generic:
self = TextFieldModelEnum.generic(type: newValue.type, title: newValue.title)
case .entry:
self = TextFieldModelEnum.entry(type: newValue.type, title: newValue.title, entry: newValue.entry)
}
}
}

Example

var tf = TextFieldModelEnum.entry(type: .text, title: "A Title", entry: "An Entry")
print(tf.info.entry)
print(tf.info.title)
tf.info.entry = "new entry"
print(tf.info.entry)
tf.info.title = "new title"
print(tf.info.title)

Output

An Entry

A Title

new entry

new title

Binding to an associated value of an enum

You can extend your enumeration and add a text property with a getter and a setter:

extension Choice {
var text: String {
get {
switch self {
case let .one(string): return string
case let .two(string): return string
}
}
set {
switch self {
case .one: self = .one(newValue)
case .two: self = .two(newValue)
}
}
}
}

Then you can simply pass the text property:

TextField("", text: $choice.text) 

Mutate a value-type associated value in-place

It's not possible to change an enum associated value, without overwriting the enum itself; if instead S is declared as class, it's a quite trivial task.

Anyway I did some experiments that I report here:

1) based on withUnsafeMutablePointer:

var e = E.s(S(a: 1))
withUnsafeMutablePointer(to: &e, {
$0.pointee = E.s(S(a: 2))
})
print(e)

2) declaring a mutating func for overwriting implicitly the enum

struct S {
var a = 1
}

enum E {
case s(S)
mutating func setS(val:S) { self = E.s(val) }
}

var e = E.s(S(a: 1))
e.setS(val: S(a: 2))
print(2)

3) if S is a class:

class S:CustomDebugStringConvertible {
var a = 1
var debugDescription: String { return "\(a)" }
}

enum E {
case s(S)
}

let e = E.s(S())
if case let .s(s) = e {
s.a = 2
}
print(e)

Binding to an associated value of an enum that has different types of associated values

Indeed, having several very similar properties is a code smell, as well as the fact that those properties were added just because they are needed by the UI layer.

However, since you already have the switch over the value type, you can push the logic for the binding to that (UI) layer. Here's a possible implementation:

struct ValueView: View {
let name: String
@Binding var value: Value

var body: some View {
HStack {
switch value {
case .none:
Spacer()
case let .bool(bool):
Toggle(name, isOn: Binding(get: { bool }, set: { value = .bool($0) } ))
case let .int(int):
Stepper("\(int)", value: Binding(get: { int }, set: { value = .int($0) }))
case let .float(float):
Text(float.description)
case let .string(string):
Text(string)
}
}
}
}

I also took the liberty of extracting the code to a dedicated view and decoupling that view from the Node type, this is more idiomatic in SwiftUI and makes your code more readable and easier to maintain.

With the above in mind, ContentView simply becomes:

Usage:

struct ContentView: View {
@StateObject private var model = Model()

var body: some View {
VStack {
ScrollView {
Text(model.jsonString)
}

List($model.data, id: \.name, children: \.children) { $node in
ValueView(name: node.name, value: $node.value)
}
}
}
}

, and you can safely delete the "duplicated" properties from the Value enum.



Related Topics



Leave a reply



Submit