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

I'm playing around with Swift enums and wondering if there is a way to change the assoicated value of an enum such as the code below attempts but fails.
enum myEnum {
case SomeCase(Int?)
mutating func someFunc() {
switch self {
case .SomeCase(let a):
if a != nil {
// a = 5 can't do this of course as assigning to let
var temp = a!; // but this generates a warning
temp = 5;
}
}
}
}
If this isn't currently possible, will it be when mutating enums come to XCode?

The whole enum is a single value, so you have to replace it entirely, not field by field. But you can certainly do that by replacing self.
enum MyEnum {
case SomeCase(Int?)
mutating func someFunc() {
switch self {
case .SomeCase(_):
self = .SomeCase(5)
}
}
}
// You have to use `var` here of course or you can't mutate it.
var x = MyEnum.SomeCase(1) // .SomeCase(Optional(1))
x.someFunc() // .SomeCase(Optional(5))

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

Related

Accessing Associated Values in an Array of Swift Enum in an Opaque Fashion

I haven't really found what I need in the associated questions. That may be because I'm a bit thick, and didn't see it, but here's my quandary:
I have a computed property that returns an Array of enums with associated values.
The values are all the same types (in my use case, the type is a "flag"), but the enums are different.
I'd like to see if there was some way to cycle through the Array, looking at only the associated values, regardless of the enum type. I am attaching a small playground that shows what I mean:
enum A {
case valueA(Int)
case valueB(Int)
case valueC(Int)
}
let a: [A] = [.valueA(0), .valueB(1), .valueC(2)]
for each in a {
print(String(describing: each))
}
print();
// This works:
for each in a {
if case let .valueA(value) = each {
print(value)
}
if case let .valueB(value) = each {
print(value)
}
if case let .valueC(value) = each {
print(value)
}
}
print();
// So does this:
for each in a {
var value: Int = 0
switch each {
case .valueA(let val):
value = val
case .valueB(let val):
value = val
case .valueC(let val):
value = val
}
print(value)
}
// What I want:
//for each in a {
// if case let A(value) = each {
// print(value)
// }
//}
I want to be able to treat each member of the collection, and extract its flag, and then make a decision, based on that flag.
I know that I could do it with a big ol' switch statement, with all the enum values (second go), but it would be nice if there were some generic way to just access all the values.
You would still have to test against the each case, but you could do
enum A {
case valueA(Int)
case valueB(Int)
case valueC(Int)
}
let a: [A] = [.valueA(0), .valueB(1), .valueC(2)]
for each in a {
switch each {
case .valueA(let val), .valueB(let val), .valueC(let val):
print(val)
}
}
This would still apply if you have cases with associated values of different types:
enum Cases {
case one(Int)
case two(Int)
case three(Int, String)
case four(String)
case five(String)
}
let testArray = [Cases.one(1), .two(2), .three(3, "three"),
.four("four"), .five("five")]
// Matching associated Ints
for value in testArray {
switch value {
case .one(let intVal), .two(let intVal), .three(let intVal, _):
print(intVal)
case .four(let strVal), .five(let strVal):
print(strVal)
}
}
// Matching associated Strings
for value in testArray {
switch value {
case .one(let intVal), .two(let intVal):
print(intVal)
case .three(_, let strVal), .four(let strVal), .five(let strVal):
print(strVal)
}
}
// Matching any type, cast to Any
for value in testArray {
switch value {
case .one(let anyVal as Any), .five(let anyVal as Any):
print(anyVal)
default:
continue // skipping these cases
}
}
The important takeaway is that you are binding the same var names in every statement whose value you are trying to match, meaning you can't match values that aren't available in ever statement:
switch ... {
// This won't work, because myInt is not defined in case .two
// and myOtherInt isn't defined in case .one
case .one(let myInt), .two(let myOtherInt):
...
}
To add to #Kiril's answer, if you wanted computed values for cases with different types of associated values, you can define optional computed vars to return those values:
extension Cases {
var intValue: Int? {
switch self {
case .one(let myInt), .two(let myInt), .three(let myInt, _):
return myInt
default:
return nil
}
}
var strValue: String? {
switch self {
case .three(_, let myStr), .four(let myStr), .five(let myStr):
return myStr
default:
return nil
}
}
}
I'd suggest a slight modification on your enum:
enum A {
case valueA(Int)
case valueB(Int)
case valueC(Int)
var flag: Int {
switch self {
case .valueA(let flag),
.valueB(let flag),
.valueC(let flag):
return flag
}
}
}
That makes any loop that just needs a flag trivial:
for each in a {
print(each.flag)
}

Swift enum's unwrapping by reference

I'm new to Swift and decided to write my own Optional enum.
enum MyOptional<Type> {
case none
case some(Type)
func get() -> Type {
switch self {
case .some(let x):
return x
case .none:
fatalError()
}
}
}
var test: MyOptional<String> = MyOptional.some("testString")
test.get().append("#")
But in case I put some struct that have mutating function and call that function - the compiler, obviously, tells me:
error: cannot use mutating member on immutable value: function call
returns immutable value test.get().append("#")
How does Swift's Optional returns struct by reference unwrapping them?
The Swift compiler has quite a bit of built-in support for Optional; including for the postfix operators ! and ? which can produce l-values (values which reside at known locations in memory; therefore allowing for mutations of that memory if the expression is mutable).
Unfortunately I don't believe it's possible to implement your own l-value returning operators (or functions in general), although constructs that allow you to define getters and setters (such as computed properties and subscripts) can be treated as l-values when they have setters:
enum MyOptional<Type> {
case none, some(Type)
var forceUnwrapped: Type {
get {
switch self {
case .some(let x):
return x
case .none:
fatalError()
}
}
set {
self = .some(newValue)
}
}
// just for demonstration; don't actually implement this as a subscript!
subscript() -> Type {
get {
switch self {
case .some(let x):
return x
case .none:
fatalError()
}
}
set {
self = .some(newValue)
}
}
}
var test = MyOptional.some("testString")
test.forceUnwrapped.append("#")
test[].append("#")
Here, test.forceUnwrapped and test[] can be treated as l-values. When mutating through them, the compiler will create a temporary variable from calling the getter, mutate this temporary, and then call the setter with the mutated value.
Although it's worth noting in both cases that when used with assignment (i.e test.forceUnwrapped = ... & test[] = ...), the getter won't be called; only the setter, which gives them slightly different semantics to Optional's postfix !, which will crash on the optional being nil even on assignment (i.e someOptional! = ...).
As an alternative, you could also define a method that takes a closure with an inout parameter, allowing the caller to mutate the force-unwrapped value:
enum MyOptional<Type> {
case none
case some(Type)
mutating func forceMutate<R>(_ body: (inout Type) throws -> R) rethrows -> R {
switch self {
case .some(var x):
defer {
self = .some(x)
}
return try body(&x)
case .none:
fatalError()
}
}
}
var test = MyOptional.some("testString")
test.forceMutate { $0.append("#") }
You can use non-mutating operations, and reassign the result back into the variable:
enum MyOptional<Type> {
case none
case some(Type)
func forceUnwrap() -> Type {
switch self {
case .some(let x):
return x
case .none:
fatalError()
}
}
static func ?? (lhs: MyOptional, rhs: #autoclosure () -> Void) {
}
}
var test: MyOptional<String> = .some("testString")
print(test)
test = .some(test.forceUnwrap() + "#")
print(test)
It might also be useful to have functions like map and flatMap:
extension MyOptional {
func map(_ transform: (Wrapped) -> Wrapped) -> MyOptional<Wrapped> {
switch self {
case .some(let x):
return .some(transform(x))
case .none:
return .none
}
}
mutating func mapInPlace(_ transform: (Wrapped) -> Wrapped) {
self = self.map(transform)
}
}
test = test.map{ $0 + "#" }
print(test)
test.mapInPlace{ $0 + "#" }
print(test)

How to compare Swift enumerations which include additional constructor values [duplicate]

class MyClass
{
enum MyEnum {
case FirstCase
case SecondCase(Int)
case ThirdCase
}
var state:MyEnum!
func myMethod ()
{
if state! == MyEnum.FirstCase {
// Do something
}
}
}
I get the compiler error pointing at the if statement::
Binary operator '==' cannot be applied to two 'MyClass.MyEnum'
operands
If instead, I use a switch statement, there is no problem:
switch state! {
// Also, why do I need `!` if state is already an
// implicitly unwrapped optional? Is it because optionals also
// are internally enums, and the compiler gets confused?
case .FirstCase:
// do something...
default:
// (do nothing)
break
}
However, the switch statement feels too verbose: I just want to do something for .FirstCase, and nothing otherwise. An if statement makes more sense.
What's going on with enums and == ?
EDIT: This is ultra-weird. After settling for the switch version and moving on to other (totally unrelated) parts of my code, and coming back, the if-statement version (comnparing force-unwrapped property against fixed enum case) is compiling with no errors.
I can only conclude that it has something to do with some corrupted cache in the parser that got cleared along the way.
EDIT 2 (Thanks #LeoDabus and #MartinR): It seems that the error appears when I set an associated value to the other enum case (not the one I am comparing against - in this case, .SecondCase). I still don't understand why that triggers this compiler error in particular ("Can't use binary operator '=='..."), or what that means.
As you said in a comment, your enumeration type actually has associated
values. In that case there is no default == operator for the enum type.
But you can use pattern matching even in an if statement (since Swift 2):
class MyClass {
enum MyEnum {
case FirstCase
case SecondCase
case ThirdCase(Int)
}
var state:MyEnum!
func myMethod () {
if case .FirstCase? = state {
}
}
}
Here .FirstCase? is a shortcut for .Some(MyEnum.FirstCase).
In your switch-statement, state is not automatically unwrapped,
even if it is an implicitly unwrapped optional (otherwise you could
not match against nil). But the same pattern can be used here:
switch state {
case .FirstCase?:
// do something...
default:
break
}
Update: As of Swift 4.1 (Xcode 9.3) the compiler can synthesize conformance to Equatable/Hashable for enums with associated values (if all their types are Equatable/Hashable). It suffices to declare the conformance:
class MyClass {
enum MyEnum: Equatable {
case firstCase
case secondCase
case thirdCase(Int)
}
var state:MyEnum!
func myMethod () {
if state == .firstCase {
// ...
}
}
}
class MyClass {
enum MyEnum {
case FirstCase
case SecondCase
case ThirdCase
}
var state: MyEnum!
func myMethod() {
guard let state = state else { return }
if state == MyEnum.FirstCase {
// Do something
print(true)
} else {
print(false)
}
}
}
let myClass = MyClass()
myClass.state = .FirstCase
myClass.myMethod()
myClass.state = .SecondCase
myClass.myMethod()

Unwrapping either one of two types in Swift

I have a method which does exactly the same thing for two types of data in Swift.
To keep things simple (and without duplicating a method) I pass AnyObject as an argument to my method which can be either of these two types. How to I unwrap it with an || (OR) statement so I can proceed? Or maybe this done otherwise?
func myFunc(data:AnyObject) {
if let data = data as? TypeOne {
// This works fine. But I need it to look something like unwrapping below
}
if let data = data as? TypeOne || let data = data as? TypeTwo { // <-- I need something like this
// Do my stuff here, but this doesn't work
}
}
I'm sure this is trivial in Swift, I just can't figure out how to make it work.
You can't unify two different casts of the same thing. You have to keep them separate because they are two different casts to two different types which the compiler needs to treat in two different ways.
var x = "howdy" as AnyObject
// x = 1 as AnyObject
// so x could have an underlying String or Int
switch x {
case let x as String:
print(x)
case let x as Int:
print(x)
default: break
}
You can call the same method from within those two different cases, if you have a way of passing a String or an Int to it; but that's the best you can do.
func printAnything(what:Any) {
print(what)
}
switch x {
case let x as String:
printAnything(x)
case let x as Int:
printAnything(x)
default: break
}
Of course you can ask
if (x is String || x is Int) {
but the problem is that you are no closer to performing an actual cast. The casts will still have to be performed separately.
Building on Clashsoft's comment, I think a protocol is the way to go here. Rather than pass in AnyObject and unwrap, you can represent the needed functionality in a protocol to which both types conform.
This ought to make the code easier to maintain, since you're coding toward specific behaviors rather then specific classes.
I mocked up some code in a playground that shows how this would work.
Hopefully it will be of some help!
protocol ObjectBehavior {
var nickname: String { get set }
}
class TypeOne: ObjectBehavior {
var nickname = "Type One"
}
class TypeTwo: ObjectBehavior {
var nickname = "Type Two"
}
func myFunc(data: ObjectBehavior) -> String {
return data.nickname
}
let object1 = TypeOne()
let object2 = TypeTwo()
println(myFunc(object1))
println(myFunc(object2))
Find if that shared code is exactly the same for both types. If yes:
protocol TypeOneOrTypeTwo {}
extension TypeOneOrTypeTwo {
func thatSharedCode() {
print("Hello, I am instance of \(self.dynamicType).")
}
}
extension TypeOne: TypeOneOrTypeTwo {}
extension TypeTwo: TypeOneOrTypeTwo {}
If not:
protocol TypeOneOrTypeTwo {
func thatSharedMethod()
}
extension TypeOne: TypeOneOrTypeTwo {
func thatSharedMethod() {
// code here:
}
}
extension TypeTwo: TypeOneOrTypeTwo {
func thatSharedMethod() {
// code here:
}
}
And here you go:
func myFunc(data: AnyObject) {
if let data = data as? TypeOneOrTypeTwo {
data.thatSharedCode() // Or `thatSharedMethod()` if your implementation differs for types.
}
}
You mean like this?
enum IntOrString {
case int(value: Int)
case string(value: String)
}
func parseInt(_ str: String) -> IntOrString {
if let intValue = Int(str) {
return IntOrString.int(value: intValue)
}
return IntOrString.string(value: str)
}
switch parseInt("123") {
case .int(let value):
print("int value \(value)")
case .string(let value):
print("string value \(value)")
}
switch parseInt("abc") {
case .int(let value):
print("int value \(value)")
case .string(let value):
print("string value \(value)")
}
output:
int value 123
string value abc

Swift enum with custom initializer loses rawValue initializer

I have tried to boil this issue down to its simplest form with the following.
Setup
Xcode Version 6.1.1 (6A2008a)
An enum defined in MyEnum.swift:
internal enum MyEnum: Int {
case Zero = 0, One, Two
}
extension MyEnum {
init?(string: String) {
switch string.lowercaseString {
case "zero": self = .Zero
case "one": self = .One
case "two": self = .Two
default: return nil
}
}
}
and code that initializes the enum in another file, MyClass.swift:
internal class MyClass {
let foo = MyEnum(rawValue: 0) // Error
let fooStr = MyEnum(string: "zero")
func testFunc() {
let bar = MyEnum(rawValue: 1) // Error
let barStr = MyEnum(string: "one")
}
}
Error
Xcode gives me the following error when attempting to initialize MyEnum with its raw-value initializer:
Cannot convert the expression's type '(rawValue: IntegerLiteralConvertible)' to type 'MyEnum?'
Notes
Per the Swift Language Guide:
If you define an enumeration with a raw-value type, the enumeration automatically receives an initializer that takes a value of the raw value’s type (as a parameter called rawValue) and returns either an enumeration member or nil.
The custom initializer for MyEnum was defined in an extension to test whether the enum's raw-value initializer was being removed because of the following case from the Language Guide. However, it achieves the same error result.
Note that if you define a custom initializer for a value type, you will no longer have access to the default initializer (or the memberwise initializer, if it is a structure) for that type. [...]
If you want your custom value type to be initializable with the default initializer and memberwise initializer, and also with your own custom initializers, write your custom initializers in an extension rather than as part of the value type’s original implementation.
Moving the enum definition to MyClass.swift resolves the error for bar but not for foo.
Removing the custom initializer resolves both errors.
One workaround is to include the following function in the enum definition and use it in place of the provided raw-value initializer. So it seems as if adding a custom initializer has a similar effect to marking the raw-value initializer private.
init?(raw: Int) {
self.init(rawValue: raw)
}
Explicitly declaring protocol conformance to RawRepresentable in MyClass.swift resolves the inline error for bar, but results in a linker error about duplicate symbols (because raw-value type enums implicitly conform to RawRepresentable).
extension MyEnum: RawRepresentable {}
Can anyone provide a little more insight into what's going on here? Why isn't the raw-value initializer accessible?
This bug is solved in Xcode 7 and Swift 2
extension TemplateSlotType {
init?(rawString: String) {
// Check if string contains 'carrousel'
if rawString.rangeOfString("carrousel") != nil {
self.init(rawValue:"carrousel")
} else {
self.init(rawValue:rawString)
}
}
}
In your case this would result in the following extension:
extension MyEnum {
init?(string: String) {
switch string.lowercaseString {
case "zero":
self.init(rawValue:0)
case "one":
self.init(rawValue:1)
case "two":
self.init(rawValue:2)
default:
return nil
}
}
}
You can even make the code simpler and useful without switch cases, this way you don't need to add more cases when you add a new type.
enum VehicleType: Int, CustomStringConvertible {
case car = 4
case moped = 2
case truck = 16
case unknown = -1
// MARK: - Helpers
public var description: String {
switch self {
case .car: return "Car"
case .truck: return "Truck"
case .moped: return "Moped"
case .unknown: return "unknown"
}
}
static let all: [VehicleType] = [car, moped, truck]
init?(rawDescription: String) {
guard let type = VehicleType.all.first(where: { description == rawDescription })
else { return nil }
self = type
}
}
Yeah this is an annoying issue. I'm currently working around it using a global-scope function that acts as a factory, i.e.
func enumFromString(string:String) -> MyEnum? {
switch string {
case "One" : MyEnum(rawValue:1)
case "Two" : MyEnum(rawValue:2)
case "Three" : MyEnum(rawValue:3)
default : return nil
}
}
This works for Swift 4 on Xcode 9.2 together with my EnumSequence:
enum Word: Int, EnumSequenceElement, CustomStringConvertible {
case apple, cat, fun
var description: String {
switch self {
case .apple:
return "Apple"
case .cat:
return "Cat"
case .fun:
return "Fun"
}
}
}
let Words: [String: Word] = [
"A": .apple,
"C": .cat,
"F": .fun
]
extension Word {
var letter: String? {
return Words.first(where: { (_, word) -> Bool in
word == self
})?.key
}
init?(_ letter: String) {
if let word = Words[letter] {
self = word
} else {
return nil
}
}
}
for word in EnumSequence<Word>() {
if let letter = word.letter, let lhs = Word(letter), let rhs = Word(letter), lhs == rhs {
print("\(letter) for \(word)")
}
}
Output
A for Apple
C for Cat
F for Fun
Add this to your code:
extension MyEnum {
init?(rawValue: Int) {
switch rawValue {
case 0: self = .Zero
case 1: self = .One
case 2: self = .Two
default: return nil
}
}
}