I would like something along these lines. I would suggest going farther and do
something like this if possible to avoid repeating the type information all
over the place:
enum OneOnOneField: Int {
let title: String
let placeholder: String
let image: UIImage
case agenda {
title: NSLocalizedString("Agenda", comment: "One on one field
header")
placeholder: NSLocalizedString("Add an agenda", comment: "One
on one field placeholder”)
image: #imageLiteral(resourceName: "Agenda-Small”)
}
// etc
}
l8r
Sean
> On Jan 9, 2017, at 11:54 AM, Tim Shadel via swift-evolution
> <[email protected]> wrote:
>
> It seems like my original (simplified) examples didn't clearly highlight what
> the problem I'm focused on solving.
>
> Enums get large, and they get complicated because the code for each case gets
> sliced up and scattered across many functions. It becomes a "one of these
> things is not like the other" situation because writing functions inside
> enums is unlike writing functions in any other part of Swift code.
>
> Also, to be clear, my goal is _not_ code brevity. It is coherence, the
> property where related code is located together. Some increase in code
> verbosity is acceptable to make code more coherent, since that leads to
> long-term maintainability.
>
> I've pulled out a few of the larger enums I've seen in code to try to
> illustrate this. Along the way, I've made a few alterations based on the
> comments I've seen come through. (Rien & Robert: I've pushed the definitions
> inside the declaration, to ensure they don't end up in other files since that
> was never my intention; Rien & Daniel: I've altered the syntax to open a
> brace right after the case declaration making everything outside it the
> default, and I like that better).
>
> Here's an enum used to consolidate the descriptions of UI elements for a
> screen, allowing the datasource to alter their order, presentation, and
> visibility while keeping the set of possible fields clean and finite. These
> enum values are used much like singletons.
>
> enum OneOnOneField: Int {
>
>
> case agenda
> case summary
> case date
> case notes
>
> struct Info {
>
> var title
> : String
>
> var placeholder
> : String
>
> var image
> :
> UIImage
>
> }
>
>
> var info
> : Info {
>
>
> switch self {
>
>
> case .agenda:
>
>
> return Info(
>
> title
> : NSLocalizedString("Agenda", comment: "One on one field header"),
>
> placeholder
> : NSLocalizedString("Add an agenda", comment: "One on one field placeholder"),
>
> image
> : #imageLiteral(resourceName: "Agenda-Small"))
>
>
> case .summary:
>
>
> return Info(
>
> title
> : NSLocalizedString("Summary", comment: "One on one field header"),
>
> placeholder
> : NSLocalizedString("Add a summary", comment: "One on one field placeholder"),
>
> image
> : #imageLiteral(resourceName: "Summary-Small"))
>
>
> case .date:
>
>
> return Info(title: "", placeholder: "", image: UIImage())
>
>
> case .notes:
>
>
> return Info(title: NSLocalizedString("Personal Notes", comment: "Title for
> personal notes screen"), placeholder: "", image: UIImage())
>
>
> }
>
>
> }
> }
> Consolidating them could instead look something like this:
>
> enum OneOnOneField: Int {
>
>
> var title
> : String { return "" }
>
> var placeholder
> : String { return "" }
>
> var image
> : UIImage { return UIImage() }
>
>
>
> case agenda {
> var title: String { return NSLocalizedString("Agenda", comment: "One
> on one field header") }
>
> var placeholder
> : String { return NSLocalizedString("Add an agenda", comment: "One on one
> field placeholder") }
>
> var image
> : UIImage { return #imageLiteral(resourceName: "Agenda-Small") }
>
>
> }
>
>
>
> case summary {
> var title: String { return NSLocalizedString("Summary", comment: "One
> on one field header") }
>
> var placeholder
> : String { return NSLocalizedString("Add a summary", comment: "One on one
> field placeholder") }
>
> var image
> : UIImage { return #imageLiteral(resourceName: "Summary-Small") }
>
>
> }
>
>
>
> case date
>
> case notes {
> var title: String { return NSLocalizedString("Personal Notes",
> comment: "Title for personal notes screen") }
>
>
> }
>
>
>
> }
> Here's an enum that implements the basics of a state machine for OAuth 2
> token use, refreshing, and login. Some of its cases have associated values
> and some don't.
>
> enum TokenState: State {
>
>
>
> case loading
> case none
> case expired(Date)
> case untested(token: String)
> case validated(token: String)
>
> var description: String {
>
>
> switch self {
>
>
> case .loading:
>
>
> return "Loading token from disk"
>
>
> case .none:
>
>
> return "No token found"
>
>
> case let .expired(at):
>
>
> return "Expired at \(at)"
>
>
> case let .untested(token):
>
>
> return "Received token \(token), but it hasn't been tested."
>
>
> case let .validated(token):
>
>
> return "Token \(token) has been validated."
>
>
> }
>
>
> }
>
>
> mutating func react
> (to event: Event) {
>
>
> switch self {
>
>
> case .loading:
>
>
> switch event {
>
>
> case _ as TokenNotFound:
>
> self
> = .
> none
>
> case let expired as TokenExpired:
>
> self
> = .expired(expired.at)
>
>
> case let loaded as TokenLoaded:
>
> self
> = .untested(token: loaded.token)
> default:
>
>
> break
>
>
> }
>
>
> case .none:
>
>
> switch event {
>
>
> case let loggedIn as UserLoggedIn:
>
> self
> = .untested(token: loggedIn.token)
> default:
>
>
> break
>
>
> }
>
>
> case .expired:
>
>
> switch event {
>
>
> case let refreshed as TokenRefreshed:
>
> self
> = .untested(token: refreshed.token)
>
>
> case _ as TokenRefreshErrored:
>
> self
> = .
> none
>
> default:
>
>
> break
>
>
> }
>
>
> case let .untested(token):
>
>
> switch event {
>
>
> case _ as UserLoaded:
>
> self
> = .validated(token: token)
>
>
> case _ as TokenRejected:
>
> self
> = .expired(at: Date())
> default:
>
>
> break
>
>
> }
>
>
> case .validated:
>
>
> switch event {
>
>
> case _ as TokenRejected:
>
> self
> = .expired(Date())
>
>
> case _ as UserLoggedOut:
>
> self
> = .
> none
>
> default:
>
>
> break
>
>
> }
>
>
> }
>
>
> }
>
>
>
> static var initialState: TokenState {
>
>
> return .
> loading
>
> }
>
>
>
> }
> After consolidation, this becomes:
>
> enum TokenState: State {
>
>
>
> static var initialState: TokenState {
>
>
> return .
> loading
>
> }
>
>
>
> case loading {
> var description: String {
>
>
> return "Loading token from disk"
>
>
> }
>
>
> mutating func react
> (to event: Event) {
>
>
> switch event {
>
>
> case _ as TokenNotFound:
>
> self
> = .
> none
>
> case let expired as TokenExpired:
>
> self
> = .expired(at: expired.at)
>
>
> case let loaded as TokenLoaded:
>
> self
> = .untested(token: loaded.token)
> default:
>
>
> break
>
>
> }
>
>
> }
>
>
> }
>
>
>
> case none {
> var description: String {
>
>
> return "No token found"
>
>
> }
>
>
> mutating func react
> (to event: Event) {
>
>
> switch event {
>
>
> case let loggedIn as UserLoggedIn:
>
> self
> = .untested(token: loggedIn.token)
> default:
>
>
> break
>
>
> }
>
>
> }
>
>
> }
>
>
>
> case expired(at: Date) {
> var description: String {
>
>
> return "Expired at \(at)"
>
>
> }
>
>
> mutating func react
> (to event: Event) {
>
>
> switch event {
>
>
> case let refreshed as TokenRefreshed:
>
> self
> = .untested(token: refreshed.token)
>
>
> case _ as TokenRefreshErrored:
>
> self
> = .
> none
>
> default:
>
>
> break
>
>
> }
>
>
> }
>
>
> }
>
>
>
> case untested(token: String) {
> var description: String {
>
>
> return "Received token \(token), but it hasn't been tested."
>
>
> }
>
>
> mutating func react
> (to event: Event) {
>
>
> switch event {
>
>
> case _ as UserLoaded:
>
> self
> = .validated(token: token)
>
>
> case _ as TokenRejected:
>
> self
> = .expired(at: Date())
> default:
>
>
> break
>
>
> }
>
>
> }
>
>
> }
>
>
>
> case validated(token: String) {
> var description: String {
>
>
> return "Token \(token) has been validated."
>
>
> }
>
>
> mutating func react
> (to event: Event) {
>
>
> switch event {
>
>
> case _ as TokenRejected:
>
> self
> = .expired(at: Date())
>
>
> case _ as UserLoggedOut:
>
> self
> = .
> none
>
> default:
>
>
> break
>
>
> }
>
>
> }
>
>
> }
>
>
>
> }
>
>
>
>> On Jan 8, 2017, at 12:22 PM, Derrick Ho via swift-evolution
>> <[email protected]> wrote:
>>
>> Currently we can write a helper method to aid in getting the values inside
>> the enum associated value. Below is a fully working implementation:
>>
>> ```
>> enum Package {
>> case box(String, Int)
>> case circular(String)
>>
>> var associated: Associated {
>> return Associated(package: self)
>> }
>>
>> struct Associated {
>> let box: (String, Int)?
>> let circular: (String)?
>> init(package: Package) {
>> switch package {
>> case .box(let b):
>> box = b
>> circular = nil
>> case .circular(let b):
>> box = nil
>> circular = b
>> }
>> }
>> }
>> }
>>
>> let b = Package.box("square", 5)
>> b.associated.box?.0 // Optional("square")
>> b.associated.box?.1 // Optional(5)
>> b.associated.circular // nil
>>
>> let c = Package.circular("round")
>> c.associated.box?.0 // nil
>> c.associated.box?.1 // nil
>> c.associated.circular // Optional("round")
>> ```
>>
>> I had to wedge in a special type called "Associated" and had to write some
>> boiler-plate code to get this effect. It is quite predictable and can
>> probably be done under the hood. I would of course prefer syntactic sugar
>> to simplify it and turn
>> ```
>> b.associated.box?.0
>> ```
>> into
>> ```
>> b.box?.0
>> ```
>>
>> On Sun, Jan 8, 2017 at 1:05 PM David Sweeris via swift-evolution
>> <[email protected]> wrote:
>>
>> On Jan 8, 2017, at 06:53, Karim Nassar via swift-evolution
>> <[email protected]> wrote:
>>
>>> One area of enums that I’d love to see some sugar wrapped around (and
>>> perhaps this has already been discussed previously?) is extracting
>>> associated values.
>>>
>>> There are many times where, given an enum like:
>>>
>>> enum Feedback {
>>> case ok
>>> case info(String)
>>> case warning(String, Location)
>>> case error(String, Location)
>>> }
>>>
>>> I’d love it if we could tag the associated values with some semantic
>>> accessor, perhaps borrowed from tuples:
>>>
>>> enum Feedback {
>>> case ok
>>> case info(msg: String)
>>> case warning(msg: String, loc: Location)
>>> case error(msg: String, loc: Location)
>>> }
>>>
>>> then:
>>>
>>> let foo = self.getSomeFeedback() // -> Feedback
>>> if let msg = foo.msg { // since not all cases can hold a ‘msg’ .msg is an
>>> Optional
>>> print(foo)
>>> }
>>
>> Can't remember if it's come up before, but +1. I can't count how many times
>> I've written something like:
>> enum Foo : CustomStringConvertible {
>> case c1(T1)
>> case c2(T2)
>> ...
>> case cN(TN)
>>
>> var description: String {
>> switch self {
>> case .c1(let val): return "\(val)"
>> case .c2(let val): return "\(val)"
>> ...
>> case .cN(let val): return "\(val)"
>> }
>> }
>> }
>>
>> Being able to simplify that to:
>> var description: String {
>> let nilDesc = "some appropriate description"
>> return "\(self.0 ?? nilDesc)"
>> }
>>
>> Would be great.
>>
>> - Dave Sweeris
>> _______________________________________________
>> swift-evolution mailing list
>> [email protected]
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>> _______________________________________________
>> swift-evolution mailing list
>> [email protected]
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
> _______________________________________________
> swift-evolution mailing list
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution