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
Pdf417 Decode and Generate The Same Barcode Using Swift
How to Get The Range of The First Line in a String
How to Hide The Top Bar (With Buttons) Usin Swift and Macos
Allow Line Editing When Reading Input from The Command Line
How to Constrain Second Nsviewcontroller Minimum Size in Os X App
Wkwebview on Macos Cuts Off Top
Qlpreviewcontroller Showing File Then Going Blank in Swiftui
How to Update User Interface on Core Data
Struct Hash for Different Types
Trying to Display Location Data from Firebase to Mapkit
Changing Associated Value of Enum Swift
Create Objects/Instances in Variables Swift
Wkwebview Problems in Macos Mojave
Msmessagelivelayout Freeze/Crash in Transcript When Info.Plist Contains Privacy Request
Realitykit Entity Synchronization Is Always Nil
Why Is a Firestore Listener Returning .Added Twice When a Single Document Is Added
Get Out of Navigation Controller and Go Back to Tab Bar View