Simpler way to deal with hardcoded ViewModifers values in SwiftUI
Tags: swift, ios, swiftui
Published: 2021-05-10 11:04:28

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/26y6nn7djvufeogv05f8.png)

How often do you you have to deal with 2 values based on a condition in your SwiftUI views?

> .background(isShowingSomething ? Color.red : Color.blue)
> .font(isShowingSomething ? .title : .callout)`
> .padding(isShowingSomething ? 20 : 50)`

This can become cumbersome specially if you have a lot of view modifiers that rely on said values.

```swift
struct NonToggledExampleView: View {
	@State var isShowingMenu = false

	var body: some View {
		ZStack {
			if isShowingMenu {
				Color.red.edgesIgnoringSafeArea(.all)
			} else {
				Color.blue.edgesIgnoringSafeArea(.all)
			}

			VStack {
				Text("Hello from Toggled")
					.font(isShowingMenu ? .title : .callout)
				Button(action: {
					isShowingMenu.toggle()
				}, label: {
					Text("Toggle Me")
				})
			}.background(isShowingMenu ? Color.yellow : Color.green)
		}
	}
}
```

In order to update those values you will need to navigate your code, remember what they are and update accordingly. Resume preview and you are on your way.

One way of simplifying this is to extract those values into variables.

```swift
private var backgroundColorOn = Color.red
private var backgroundColorOff = Color.blue
```

This will grow exponentially the more options you have.

#Introducing Toggled

We can do better than this, so I've created Toggled. Toggled allows you to specify both values at initialization in order to make it easy to write/read/update your SwiftUI hardcoded values.

```swift
struct ToggledExampleView: View {
	@State var isShowingMenu = false
	@State var backgroundColor = Toggled<Color>(values: (on: .red, off: .blue))
	@State var font = Toggled<Font>(values: (on: .title, off: .callout))
	@State var stackBackgroundColor = Toggled<Color>(values: (on: .yellow, off: .green))
	@State var stackPadding = Toggled<CGFloat>(values: (on: 20, off: 0))

	var body: some View {
		ZStack {
			backgroundColor.value(state: isShowingMenu)
				.edgesIgnoringSafeArea(.all)
			VStack {
				Text("Hello from Toggled")
					.fixedSize()
					.font(font.value(state: isShowingMenu))
				Button(action: {
					withAnimation {
						isShowingMenu.toggle()
					}
				}, label: {
					Text("Toggle Me")
				})
			}
			.padding(stackPadding.value(state: isShowingMenu))
			.background(stackBackgroundColor.value(state: isShowingMenu))
		}
	}
}
```

#Source Code
```swift
protocol ToggleInterface {
	associatedtype ValueType
	var values: (on: ValueType, off: ValueType) { get set }
	func value(state: Bool) -> ValueType
}

struct Toggled<T>: ToggleInterface {
	typealias ValueType = T
	var values: (on: T, off: T)
	func value(state: Bool) -> T {
		guard state else {
			return values.off
		}
		return values.on
	}
}
```

Using toggled will eliminate the need for the ternary/if when you want to use the value, it makes it easier to reason with the options you have in your view and also you can make it dynamic by turning into a binding.

#Futher improvements

We could take this a step further and create some sort of utility to dynamically change those values at run time. 

> DebugToggledViewRangeValue(title: "$stackPadding", toggled: $stackPadding, range: 0...100)

```swift
struct DebugToggledViewColor: View {
	var title: String
	var toggled: Binding<Toggled<Color>>

	var body: some View {
		VStack(spacing: 0) {
			Text(title)
		 HStack {
			ColorPicker("On", selection: toggled.onValue)
			Spacer()
			ColorPicker("Off", selection: toggled.offValue)
		}
		}
	}
}

struct DebugToggledViewRangeValue<T>: View where T : BinaryFloatingPoint, T.Stride : BinaryFloatingPoint {
	var title: String
	var toggled: Binding<Toggled<T>>
	var range: ClosedRange<T>

	var body: some View {
		VStack(spacing: 0) {
			Text(title)
			HStack {
				VStack {
					Text("On")
					Slider(value: toggled.onValue, in: range)
				}
				VStack {
					Text("Off")
					Slider(value: toggled.offValue, in: range)
				}
			}
		}
	}
}

extension Binding where Value: ToggleInterface {
	var onValue: Binding<Value.ValueType> {
		return Binding<Value.ValueType> {
			wrappedValue.values.on
		} set: { newValue in
			wrappedValue.values.on = newValue
		}
	}

	var offValue: Binding<Value.ValueType> {
		return Binding<Value.ValueType> {
			wrappedValue.values.off
		} set: { newValue in
			wrappedValue.values.off = newValue
		}
	}
}
```

Love to hear your thoughts and feedback. 

[GIST](https://gist.github.com/vibrazy/16e0408b5be7be4df508e92e0cf9448b)