1

I am wondering if the following use of Enum, static var, computed property and whatnot are good practices to manage states in Swift and SwiftUI.

I'm learning Swift by myself and don't have anyone to review my code or senior to learn from.

enum ARSessionState {
    case selfie
    case world
    
    mutating func toggle() {
        self = (self == .selfie) ? .world : .selfie
    }
    
    private static var selfieConfiguration: ARFaceTrackingConfiguration = {
        let configuration = ARFaceTrackingConfiguration()
        configuration.isWorldTrackingEnabled = true
        return configuration
    }()
    
    private static var worldConfiguration: ARWorldTrackingConfiguration = {
        let configuration = ARWorldTrackingConfiguration()
        configuration.planeDetection = [.horizontal]
        configuration.userFaceTrackingEnabled = true
        return configuration
    }()

    var configuration: ARConfiguration {
        switch self {
        case .selfie:
            return ARSessionState.selfieConfiguration
        case .world:
            return ARSessionState.worldConfiguration
        }
    }
}

and I use this in a SwiftUI view like this:

struct ContentView : View {
    @State private var session: ARSessionState = .world

    ...
    
    var body: some View {
        
        ZStack {
            
            ...
            
            Button(action: {session.toggle()} ) {

                Image(systemName: "arrow.triangle.2.circlepath.camera")
                    .resizable()
                    .scaledToFit()
                    .frame(width: 50, height: 50)
            
                }.onChange(of: session) { newValue in
                let configuration = session.configuration
                
                ...

            }
        }
    }
}

Rather than using a simple Bool, I wanted to define two states and toggle between them from a Button action because selfie/world made more sense conceptually than true/false. So I defined ARSessionState and added two cases selfie and world.

Now, when the user changes the state, I wanted to have different ARConfigurations to kick-in based on the changed state, namely ARWorldTrackingConfiguration and ARFaceTrackingConfiguration. So inside .onchange {...} I call session.configuration, which should give me corresponding ARConfiguration based on the session's state.

So my questions are, is it a good practice to

  1. define an Enum with two cases like this to replace true/false Bool?

  2. define a mutating func to change the state? I'm not confident how state change will play out in SwiftUI. Specifically with @State property-wrapper.

  3. define private static var properties within Enum? I did this because I could not have stored properties in Enum.

  4. call the static properties from a computed property? (in this case, var configuration: ARConfiguration)

Any other comments related to good Swift programming practices are greatly appreciated too!

D.Park
  • 53
  • 5

3 Answers3

2

define an Enum with two cases like this to replace true/false Bool?

No problem.

define a mutating func to change the state?

It surprised me to find that it even compiles. Given that it does, it is probably OK. Personally, I'd be happier with a non mutating function that returns the new value

enum ARSessionState {
    case selfie
    case world
    
    func toggled() -> ARSessionState {
        return (self == .selfie) ? .world : .selfie
    }
}

This gives you more flexibility and is closer in semantics to the ! operator for booleans.

define private static var properties within Enum? I did this because I could not have stored properties in Enum.

I don't see a problem with that at all.

call the static properties from a computed property? (in this case, var configuration: ARConfiguration)

Or that.

JeremyP
  • 84,577
  • 15
  • 123
  • 161
1

I am no expert myself (hopefully one day) but I would like to share my thoughts for the sake of information exchange.

Is it a good practice to define an Enum with two cases like this to replace true/false Bool?

  • I think it's pretty reasonable. Using an enum with two cases (.selfie and .world) provides more clarity and makes your code more readable.

Is it a good practice to define a mutating func to change the state?

  • I don't see a reason not to use a mutating func, but why? I think the better approach is to use a normal func.

Is it a good practice to define private static var properties within an enum?

  • If you need to encapsulate certain configurations or values related to the enum, then why not? I think it's reasonable.

Is it okay to call the static properties from a computed property?

  • Good practice? Maybe. Bad practice? I don't think so. By calling the appropriate static property based on the enum's current state, you can conveniently provide the desired configuration.

Overall your code looks really cool to me, however if you need more tips and ways to write better Swift code, I can suggest you this article which I find really good. There's tons of articles about becoming a better coder and you probably know that but in case you don't, this could be a good starting point.

leopanic13
  • 62
  • 5
1

You're asking for opinions so here's my tuppence:

  1. define an Enum with two cases like this to replace true/false Bool?

Yes fine. In fact, I would say it's a good solution because if you later have more than two cases then you can add them to the enum.

  1. define a mutating func to change the state?

As someone else commented, I'm surprised it compiles. I think I would go for a non-mutating computed property that delivers the toggled state, something like:

var toggledState: ARSessionState {
    self == selfie ? world : selfie
}
  1. define private static var properties within Enum?

I think I would keep the enum as a low level data type that does not have knowledge of other classes. This keeps the dependency 1-way. At a more general level, I don't see a problem with defining static constants (using let) for other low-level types, such as strings.

  1. call the static properties from a computed property?

So following from 3, I would not include factory functions for classes. If you need a factory for ARFaceTrackingConfiguration, create a factory class instead. This would probably take an enum value as parameter. However, simple functions or computed properties that return low-level data types (such as strings) would seem fine to me.

Benzy Neez
  • 1,546
  • 2
  • 3
  • 10