swiftmutating-function

Mutating func with external detection which variable to mutate


I have a struct with 2 variables. This struct has mutating function, but in this function I need to check, which variable to mutate. For this I use separate class' static function with complex logic. This class works with different structs, so for DRY purpose I can't represent this logic in all these structs.

The problem is, that I don't know how to receive the same struct's variable from this separate class, so no struct's variables are mutate. I think, that I miss some Swift knowledge, because I'm sure, that it is possible, without duplicating logic.

Code to represent it in Playground:

struct SomeStruct {
    var a = "a"
    var b = "b"

    mutating func mutateString(to newString: String) {
        var varToMutate = VariableDetector.whichVarToMutate(a, b)
        varToMutate = newString

        // prints to represent question
        print("varToMutate: \(varToMutate)")
        print("a: \(a)")
        print("b: \(b)")
    }
}

class VariableDetector {
    static func whichVarToMutate(_ first: String, _ second: String) -> String {
        var firstOrSecondString = ""

        // simple logic to represent question, in real case it is far more complex
        if first == "a" {
            firstOrSecondString = first
        } else {
            firstOrSecondString = second
        }

        return firstOrSecondString
    }
}

var someStruct = SomeStruct()
someStruct.mutateString(to: "c")

This code produces:

varToMutate: c
a: a
b: b

Yes, it can be solved with:

if varToMutate == a {
    a = newString
} else if varToMutate == b {
    b = newString
}

But I want to solve it in more elegant way :)

Thanks for any help!


Solution

  • In Swift 4, this could be done by returning a KeyPath from whichVarToMutate. The KeyPath could then be used to access into the instance in question, and mutate the property it represents.

    In Swift 3, there are 2 approaches I can think of:

    1. Pass a mutator closure to the decider method, which passes out the appropriate var to mutate as an inout param, and then has the closure body mutate it.
    2. Define a protocol that contains these variables (that you said are shared between multiple types), make those types conform to it, and provide an extension on the protocol that defines a method, which will apply to all of them. This is the approach I would use, even in Swift 4:

      struct SomeStruct {
          var a = "a"
          var b = "b"
      }
      
      protocol Mutable { // TODO: Rename me appropriately
          var a: String { get set }
          var b: String { get set }
      }
      
      extension SomeStruct: Mutable {}
      
      extension Mutable {
          mutating func changeAppropriateVar(to newValue: String) -> Void {
              // simple logic to represent question, in real case it is far more complex
              let someCondition = true
      
              if someCondition {
                  print("Setting `a` to \(newValue)")
                  a = newValue
              }
              else {
                  print("Setting `b` to \(newValue)")
                  b = newValue
              }
          }
      }
      
      var s = SomeStruct()
      s.changeAppropriateVar(to: "foo")