[Features] Supports dynamic @Option values that may change over time

Uses @autoclosure to keep call site the same, but allows a single picker @Option to show different values depending on other factors (e.g. other @Options).
This commit is contained in:
Riley Testut 2023-04-27 18:04:42 -05:00
parent 6fd7f9e1d5
commit 2ead48ad40
2 changed files with 19 additions and 11 deletions

View File

@ -16,7 +16,7 @@ public class Option<Value: OptionValue, DetailView: View>: _AnyOption
public let name: LocalizedStringKey? public let name: LocalizedStringKey?
public let description: LocalizedStringKey? public let description: LocalizedStringKey?
public let values: [Value]? public let values: (() -> [Value])?
public private(set) var detailView: () -> DetailView? = { nil } public private(set) var detailView: () -> DetailView? = { nil }
// Assigned to property name. // Assigned to property name.
@ -76,20 +76,28 @@ public class Option<Value: OptionValue, DetailView: View>: _AnyOption
} }
} }
private init(defaultValue: Value, name: LocalizedStringKey?, description: LocalizedStringKey?, values: (some Collection<Value>)?) private init(defaultValue: Value, name: LocalizedStringKey?, description: LocalizedStringKey?, values: (() -> some Collection<Value>)?)
{ {
self.defaultValue = defaultValue self.defaultValue = defaultValue
self.name = name self.name = name
self.description = description self.description = description
self.values = values.map { Array($0) }
if let values
{
self.values = { Array(values()) }
}
else
{
self.values = nil
}
self.detailView = { nil } self.detailView = { nil }
} }
private convenience init(defaultValue: Value, name: LocalizedStringKey?, description: LocalizedStringKey?) private convenience init(defaultValue: Value, name: LocalizedStringKey?, description: LocalizedStringKey?)
{ {
self.init(defaultValue: defaultValue, name: name, description: description, values: [Value]?.none) self.init(defaultValue: defaultValue, name: name, description: description, values: (() -> [Value])?.none)
} }
} }
@ -134,35 +142,35 @@ public extension Option where Value == Bool, DetailView == OptionToggleView
public extension Option where Value: LocalizedOptionValue, DetailView == OptionPickerView<Value> public extension Option where Value: LocalizedOptionValue, DetailView == OptionPickerView<Value>
{ {
// Non-Optional // Non-Optional
convenience init(wrappedValue: Value, name: LocalizedStringKey, description: LocalizedStringKey? = nil, values: some Collection<Value>) convenience init(wrappedValue: Value, name: LocalizedStringKey, description: LocalizedStringKey? = nil, values: @autoclosure @escaping () -> some Collection<Value>)
{ {
self.init(defaultValue: wrappedValue, name: name, description: description, values: values) self.init(defaultValue: wrappedValue, name: name, description: description, values: values)
self.detailView = { [weak self] () -> DetailView? in self.detailView = { [weak self] () -> DetailView? in
guard let self else { return nil } guard let self else { return nil }
return OptionPickerView(name: name, options: Array(values), selectedValue: self.valueBinding) return OptionPickerView(name: name, options: Array(values()), selectedValue: self.valueBinding)
} }
} }
// Optional, default = nil // Optional, default = nil
convenience init(name: LocalizedStringKey, description: LocalizedStringKey? = nil, values: some Collection<Value>) where Value: OptionalProtocol, Value.Wrapped: LocalizedOptionValue convenience init(name: LocalizedStringKey, description: LocalizedStringKey? = nil, values: @autoclosure @escaping () -> some Collection<Value>) where Value: OptionalProtocol, Value.Wrapped: LocalizedOptionValue
{ {
self.init(defaultValue: Value.none, name: name, description: description, values: values) self.init(defaultValue: Value.none, name: name, description: description, values: values)
self.detailView = { [weak self] () -> DetailView? in self.detailView = { [weak self] () -> DetailView? in
guard let self else { return nil } guard let self else { return nil }
return OptionPickerView(name: name, options: values.appendingNil(), selectedValue: self.valueBinding) return OptionPickerView(name: name, options: values().appendingNil(), selectedValue: self.valueBinding)
} }
} }
// Optional, default = non-nil // Optional, default = non-nil
convenience init(wrappedValue: Value, name: LocalizedStringKey, description: LocalizedStringKey? = nil, values: some Collection<Value>) where Value: OptionalProtocol, Value.Wrapped: LocalizedOptionValue convenience init(wrappedValue: Value, name: LocalizedStringKey, description: LocalizedStringKey? = nil, values: @autoclosure @escaping () -> some Collection<Value>) where Value: OptionalProtocol, Value.Wrapped: LocalizedOptionValue
{ {
self.init(defaultValue: wrappedValue, name: name, description: description, values: values) self.init(defaultValue: wrappedValue, name: name, description: description, values: values)
self.detailView = { [weak self] () -> DetailView? in self.detailView = { [weak self] () -> DetailView? in
guard let self else { return nil } guard let self else { return nil }
return OptionPickerView(name: name, options: values.appendingNil(), selectedValue: self.valueBinding) return OptionPickerView(name: name, options: values().appendingNil(), selectedValue: self.valueBinding)
} }
} }
} }

View File

@ -19,7 +19,7 @@ public protocol AnyOption<Value>: AnyObject, Identifiable
var key: String { get } var key: String { get }
var settingsKey: SettingsName { get } var settingsKey: SettingsName { get }
var values: [Value]? { get } var values: (() -> [Value])? { get }
var detailView: () -> DetailView? { get } var detailView: () -> DetailView? { get }
var value: Value { get set } var value: Value { get set }