0

I am wondering if using static structs to mutate data in Swift is not a proper practice. I had a coding challenge for an interview and in the feedback for my work they said that "We didn't understand why you had static structs that mutated data.".

For a little background into my implementation of the coding challenge, the project was to create a clone of the iOS Calculator app. At a high level I had 3 major pieces:

  1. ViewController to handle button clicks and UI formatting
  2. struct handler for doing the computations
  3. Custom UILabel that formatted the number for the output

Based off of posts I have read online, I always thought a struct was preferred over a class when possible because copying data is safer than having multiple reference points to a single instance.

I also decided to make the properties and functions in the class static so that I did not have to instantiate and pass around the handler in the view controller to access them. There would only ever be one handler that needed to retain data throughout the session based on the user's input.

Below is a snippet of how this handler was structured.

struct CalculationHandler {

    /// Will be the first number included in the operation
    static var firstEntry:Double?
    /// Will be the second number included in the operation
    static var secondEntry: Double?
    /// Operation that is next to be executed
    static var currentCalculation:CalculationType = .none

    /// Returns calculated value based on firstEntry, secondEntry, and CalculationType
    static func calculate(_ calculationType:CalculationType) -> Double? { ... }

This CalculationHandler was called and used in the button clicks of the ViewController, like below:

if let value = CalculationHandler.calculate(.addition) {
        outputLabel.calculatedValue = "\(value)"
}

Can anyone explain why implementing a struct with static properties/functions is not a good practice for this use-case?

bmjohns
  • 6,344
  • 1
  • 33
  • 39
  • 1
    You should look into the singleton pattern. When you want to create a type, which you'll only need a single instance of, the singleton pattern is the preferred method. [This Q&A](https://stackoverflow.com/a/24147830/4667835) is a good starting point, but there are several good tutorials on the singleton pattern in Swift. – Dávid Pásztor Jan 17 '18 at 16:54
  • @DávidPásztor, Playing devil's advocate, what optimizations do you get from a class using singleton pattern vs a struct with static functions/variables? I see static vs singleton is debated [here](https://stackoverflow.com/questions/43905986/whats-is-different-between-struct-with-static-vars-and-a-singleton-in-swift?noredirect=1&lq=1), but I can't see a performance gain, and the struct with static looks like a cleaner implementation to me? – bmjohns Jan 17 '18 at 19:26
  • I don't really see any direct performance difference between using a singleton instance or a utility class (all methods are static as in your example). However, the main difference lies in code maintenance costs, readability and access control. While maintenance costs and readability can be subjective, singleton has a clear advantage when it comes to access control. If you implement a utility class, you cannot hide any of its methods/variables using `private`/`fileprivate` access modifiers due to the fact that all of its methods need to be static. – Dávid Pásztor Jan 17 '18 at 22:19
  • On the other hand, if you implement a singleton instance, you can hide as many fields/methods as you'd like, since you actually have an instance of that class. This can come extremely useful if you are designing libraries/APIs that will be made public, since it will prevent end users from using methods/accessing properties they shouldn't be able to. When using a utility class, you won't be able to use access control to your advantage, so this might be a reason why people prefer the singleton pattern over a utility class. – Dávid Pásztor Jan 17 '18 at 22:23
  • @DávidPásztor Unless I am missing something, I am able to declare static methods/variables as `private/fileprivate` and not have them accessible outside of that struct/file in Swift 4. Have you tested this recently? – bmjohns Jan 17 '18 at 22:55
  • My bad, haven't actually tested it since Swift 4, seems like you're right, this doesn't apply anymore. – Dávid Pásztor Jan 17 '18 at 22:58

2 Answers2

1

There seems to be a major misunderstanding of why one would use a struct, a singleton, etc.

These two statements from your post contradict each other:

  • First you say that you use structs "because copying data is safer than having multiple reference points to a single instance".

  • Then, however, you say that you "decided to make the properties and functions in the class static so that I did not have to instantiate and pass around".

So you chose a struct to make passing data around safer and then decided to avoid passing things around altogether. Maybe that's what confused them: you used a language feature in a very untypical way.

There is not difference inbetweenusing a class or a struct when all all members are static. (Forget performance considerations, that's totally negligible in such a simple example.)


Coming back to your question, what they wanted to see is probably this:

struct Calculation {
  var firstNumber: Double? = nil
  var secondNumber: Double? = nil
  var type: CalculationType = .none
}

extension Calculation {
  var value: Double? {
    // ... calculate it
  }
}

That's a simple struct, just holding all the data that belongs to a calculation, plus a var to perform the calculation.

In your view controller, you would then define a didSet handler in order to update the visualization:

var calculation = Calculation() {
  didSet {
    outputLabel.calculatedValue = calculation.value
  }
}

The Swift magic that comes into play here is that whenever you reassign a member of self.calculation the didSet handler will automatically be executed (behind the scenes, a new Calculation instance is created and assigned); so in your UI handlers you only need to forward the entered data (e.g. self.calculation.firstNumber = number), which will in turn cause the output to get updated.

As I said, it's just a guess that that's what they meant with their critique. You can only know for sure when you ask ;)

dr_barto
  • 5,723
  • 3
  • 26
  • 47
0

There are plenty of reasons this isn't a great practice, but here is one main one people love to test in interviews: Does not promote composition.

Regarding composition, check out the following example:

//We could make this even more composable using generics,
//but for now this will do.
struct DoubleTuple {
   let firstNum: Double
   let secondNum: Double
}

//Again, a type alias for the return type here would make this even more composable
protocol Calculation {
   func calculate(doubleTuple: DoubleTuple) -> Double?
}

struct AdditonCalculation: Calculation {
   func calculate(doubleTuple: DoubleTuple) -> Double? {
      return doubleTuple.firstNum + doubleTuple.secondNum
   }
}

struct SubtractionCalculation: Calculation {
   func calculate(doubleTuple: DoubleTuple) -> Double? {
      let subtractTuple = DoubleTuple(firstNum: doubleTuple.firstNum, secondNum: -doubleTuple.secondNum)
      return AdditonCalculation()
                .calculate(doubleTuple: subtractTuple)
   }
}

Here, we are able to define what Subtraction means in terms of our previously defined addition struct. In other words, subtraction is composed of addition. In this example, this seems like way more work than just subtracting the two, but in more complicated calculations, it helps if you can break them down into previously-defined, composable, more simple calculations. With your example, you would presumably have a giant switch statement inside your calculate function, where the calculations could get bloated quickly, it would be harder to re-use previous calculations, and it would be harder to test each one in isolation.

Michael Curtis
  • 564
  • 3
  • 9
  • You make a good point, and I agree that the actual calculation portion could be improved with your suggestion. However I am not sure it addresses the exact comment that I was confused by, `"We didn't understand why you had static structs that mutated data.".` Because in a calculator you need to store the first entry and second entry at different times, I would still need some singleton or static variables to retain that info until i'm ready to calculate. And I liked the idea of having that logic outside of the View Controller to add some separation of concerns. – bmjohns Jan 17 '18 at 21:47