Peter Tretiakov
Peter Tretiakov

Reputation: 3410

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!

Upvotes: 0

Views: 317

Answers (2)

Alexander
Alexander

Reputation: 63167

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

Upvotes: 1

mag_zbc
mag_zbc

Reputation: 6982

Unless a and b are instances of a class, your varToMutate will only be a copy, because everything besides a Class is a value-type in Swift. Maybe try UnsafeMutablePointer<T>?

Upvotes: 1

Related Questions