Jordi Daiku Serra Jordi Daiku Serra - 1 month ago 8
iOS Question

Swift Mutation inside switch with associated values

I have these structs I want to mutate:

public struct CheckoutViewModel {
var sections: [Section]
var total: String

public struct Section {
var title: String
var description: String
var kind: Kind
var expandState: ExpandState

enum Kind {
case products([ProductOrderViewModel])
case shippingMode(SelectableArray<ShippingMode>)
case shippingTarget(SelectableArray<ShippingKind>)
case billingAddress(SelectableArray<Address>)
case payment(SelectableArray<PaymentProvider>)
case promoCode(String?)
case legalAdvice(userAccepted: Bool)
}
}
}

struct SelectableArray<T> {
var selectedIndex: Int?
let options: [T]

init(options: [T]) {
self.options = options
self.selectedIndex = nil
}

mutating func select(atIndex: Int) throws -> T {
guard atIndex < options.count else {
throw SelectableArrayError.outOfBoundsIndex
}
selectedIndex = atIndex
return options[atIndex]
}

var selectedElement: T? {
guard let selectedIndex = selectedIndex else { return nil }
return options[selectedIndex]
}
}


I want to use this mutating func select() method inside SelectableArray, I am calling it from a chain of mutating functions (because Sections is nested inside a struct)

extension CheckoutViewModel {
mutating func select(atIndexPath indexPath: IndexPath) {
sections[indexPath.section].select(atIndex: indexPath.row)
}
}

extension CheckoutViewModel.Section {
mutating func select(atIndex idx: Int) {
switch kind {
case .shippingMode(var modes):
do { _ = try modes.select(atIndex: idx) } catch { return }
default:
return nil
}
dump(self) // Here self hasn't changed
}
}


The problem is that the CheckoutViewModel struct is never mutated. I am guessing that switch is not a mutating function, so
var modes
inside that switch is non mutable and then it does not matter if the following functions mutate anything. The workaround I managed to do is this:

mutating func select(atIndex idx: Int) {
switch kind {
case .shippingMode(var modes):
do {
_ = try modes.select(atIndex: idx)
self.kind = .shippingMode(modes)
} catch { return }
default:
return
}
}


Do you have any oher solution to this problem? Is there any sort of
mutating switch
function that I can use?

Answer

According to The Swift Programming Language:

A switch case can bind the value or values it matches to temporary constants or variables, for use in the body of the case. This behavior is known as value binding, because the values are bound to temporary constants or variables within the case’s body.

Changes to such a temporary variable (e.g. the modes variable) do not affect the contents of the enum that is being switched on (e.g. kind).

For your first approach to work you would indeed need a different sort of switch statement that creates a reference to the enum's associated value, allowing you to modify that value in place. Such a statement doesn't exist in Swift 3.0.1.

Comments