Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Using OptionSet to simplify your view state. Unify your states into 1
//
// Created by Daniel Tavares on 07/05/2021.
//
import SwiftUI
// MARK: - OptionsSet
// Blog post https://dev.to/vibrazy/easy-swiftui-view-bindings-using-optionset-and-sourcery-4edb
protocol OptionsBinding {}
enum MyEnum: OptionsBinding {
case showBackground
case showBorder
case reduceMotionEnabled
case largeFont
case receiveEmailNotifications
}
extension MyEnum {
struct Options: OptionSet {
var rawValue: UInt
static let none = Self([])
static let showBackground = Self(rawValue: 1 << 0)
static let showBorder = Self(rawValue: 1 << 1)
static let reduceMotionEnabled = Self(rawValue: 1 << 2)
static let largeFont = Self(rawValue: 1 << 3)
static let receiveEmailNotifications = Self(rawValue: 1 << 4)
}
}
extension Binding where Value: OptionSet, Value == Value.Element {
func bindedValue(_ options: Value) -> Bool {
return wrappedValue.contains(options)
}
func bind(
_ options: Value,
animate: Bool = false
) -> Binding<Bool> {
return .init { () -> Bool in
self.wrappedValue.contains(options)
} set: { newValue in
let body = {
if newValue {
self.wrappedValue.insert(options)
} else {
self.wrappedValue.remove(options)
}
}
guard animate else {
body()
return
}
withAnimation {
body()
}
}
}
}
struct LessStateVariablesView: View {
@State var viewOptions = MyEnum.Options.none
var body: some View {
ZStack {
if $viewOptions.bindedValue(.showBackground) {
Color.red.edgesIgnoringSafeArea(.all)
}
VStack {
Toggle(isOn: $viewOptions.bind(.showBackground)) {
Text(LocalizedStringKey("Show Background"))
}
Toggle(isOn: $viewOptions.bind(.showBorder)) {
Text(LocalizedStringKey("Show Border"))
}
Toggle(isOn: $viewOptions.bind(.reduceMotionEnabled)) {
Text(LocalizedStringKey("Reduce Motion Enabled"))
}
Toggle(isOn: $viewOptions.bind(.largeFont)) {
Text(LocalizedStringKey("Large Font"))
}
Toggle(isOn: $viewOptions.bind(.receiveEmailNotifications)) {
Text(LocalizedStringKey("Receive Notifications"))
}
}
.padding()
.font(.system(size: 22, weight: .light, design: .rounded))
}
}
}
/*
https://github.com/krzysztofzablocki/Sourcery
Stencil Template
{% for type in types.enums where type.cases.count > 0 and type.based.OptionsBinding or type|annotated:"OptionsBinding" %}
extension {{ type.name }} {
struct Options: OptionSet {
var rawValue: UInt
static let none = Self([])
{% for i in 0...type.cases.count %}
{% if not forloop.last %}
static let {{type.cases[i].name}} = Self(rawValue: 1 << {{i}})
{% endif %}
{% endfor %}
}
}
{% endfor %}
extension Binding where Value: OptionSet, Value == Value.Element {
func bindValue(_ options: Value) -> Bool {
return wrappedValue.contains(options)
}
func bind(
_ options: Value,
animate: Bool = false
) -> Binding<Bool> {
return .init { () -> Bool in
self.wrappedValue.contains(options)
} set: { newValue in
let body = {
if newValue {
self.wrappedValue.insert(options)
} else {
self.wrappedValue.remove(options)
}
}
guard animate else {
body()
return
}
withAnimation {
body()
}
}
}
}
*/
@startover205
Copy link

startover205 commented May 5, 2021

Great extension!

Edit the constraint as below so you don't have to repeat the declaration for other structs conforming to OptionSet:

extension Binding where Value: OptionSet, Value == Value.Element {
        // ...
}

@vibrazy
Copy link
Author

vibrazy commented May 7, 2021

Good shout thank you, updated it :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment