Como fazer um enum estar em conformidade com um protocolo em Swift?

93

A documentação do Swift diz que classes , structs e enums podem estar em conformidade com os protocolos e posso chegar a um ponto em que todos eles estão em conformidade. Mas não consigo fazer com que o enum se comporte exatamente como os exemplos de classe e estrutura :

protocol ExampleProtocol {
    var simpleDescription: String { get set }
    mutating func adjust()
}

class SimpleClass: ExampleProtocol {
    var simpleDescription: String = "A very simple class."
    var anotherProperty: Int = 69105

    func adjust() {
        simpleDescription += " Now 100% adjusted."
    }
}

var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription

struct SimpleStructure: ExampleProtocol {
    var simpleDescription: String = "A simple structure"

    mutating func adjust() {
        simpleDescription += " (adjusted)"
    }
}

var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription

enum SimpleEnum: ExampleProtocol {
    case Base

    var simpleDescription: String {
        get {
            return "A Simple Enum"
        }
        set {
            newValue
        }
    }

    mutating func adjust() {
        self.simpleDescription += ", adjusted"
    }
}

var c = SimpleEnum.Base
c.adjust()
let cDescription = c.simpleDescription

Não descobri como fazer simpleDescriptionpara mudar como resultado de uma ligação adjust(). Meu exemplo obviamente não fará isso porque o getter tem um valor embutido no código, mas como posso definir um valor para o simpleDescriptionenquanto ainda estou em conformidade com o ExampleProtocol?

Adrian Harris Crowne
fonte

Respostas:

155

Esta é minha tentativa:

protocol ExampleProtocol {
    var simpleDescription: String { get }
    mutating func adjust()
}

enum ExampleEnum : ExampleProtocol {
    case Base, Adjusted

    var simpleDescription: String {
        return self.getDescription()
    }

    func getDescription() -> String {
        switch self {
        case .Base:
            return "A simple description of enum"
        case .Adjusted:
            return "Adjusted description of enum"
        }
    }

    mutating func adjust() {
        self = ExampleEnum.Adjusted
    }
}

var c = ExampleEnum.Base
c.adjust()
let cDescription = c.simpleDescription
Hu Qiang
fonte
Isso satisfaz o protocolo, mas ainda faz sentido como enum. Bom trabalho!
David James
1
Impressionante! Tive a ideia de criar um estado ajustado, mas não me ocorreu que poderia mudar para .Adjustado no método de ajuste. Obrigado!
Adrian Harris Crowne
Excelente ponteiro. Fiquei um pouco preso neste. Porém, uma pergunta: algum motivo pelo qual você adicionou o valor de retorno de Void à função de ajuste?
jpittman de
@jpittman porque a adjustfunção retorna Voidno ExampleProtocol, é o mesmo que usar mutating func adjust(). Se desejar adjustter um tipo de retorno, você pode alterar o protocolo para: gist.github.com/anjerodesu/e1bf640576a3b6fa415f
Angelo
1
Não foi possível editar a resposta para corrigir o erro de sintaxe, está faltando um ponto, deveria sercase .Base:
John Doe
44

Aqui está minha opinião sobre isso.

Como se trata de um enume não de um class, você deve pensar diferente (TM) : é a sua descrição que deve mudar quando o "estado" das suas enummudanças (como apontado por @ hu-qiang).

enum SimpleEnumeration: ExampleProtocol {
  case Basic, Adjusted

  var description: String {
    switch self {
    case .Basic:
      return "A simple Enumeration"
    case .Adjusted:
      return "A simple Enumeration [adjusted]"
    }
  }

  mutating func adjust()  {
    self = .Adjusted
  }
}

var c = SimpleEnumeration.Basic
c.description
c.adjust()
c.description

Espero que ajude.

Zedenem
fonte
concordo com sua opinião sobre a enum em si e com o código que você forneceu. legais.
4
Esta resposta é mais agradável e sucinta do que a aceita.
Ricardo Sanchez-Saez
2
Apenas uma nota lateral que você pode remover o SimpleEnumeration.Adjusted e substituir por apenas ".Adjusted". Se o nome da enumeração mudar, é uma coisa a menos para refatorar.
Shaolo
Sim, está melhor. Obrigado.
Arjun Kalidas
No entanto, isso não está de acordo com o protocolo fornecido
barry
11

Aqui está outra abordagem, usando apenas o conhecimento adquirido com o passeio até aquele ponto *

enum SimpleEnumeration: String, ExampleProtocol {
    case Basic = "A simple enumeration", Adjusted = "A simple enumeration (adjusted)"

    var simpleDescription: String {
        get {
            return self.toRaw()
        }
    }

    mutating func adjust() {
        self = .Adjusted
    }
}

var c = SimpleEnumeration.Basic
c.adjust()
let cDescription = c.simpleDescription

Se você deseja que adjust()atue como um alternador (embora não haja nada que sugira que este seja o caso), use:

mutating func adjust() {
    switch self {
    case .Basic:
        self = .Adjusted
    default:
        self = .Basic
    }
}

* (Embora não mencione explicitamente como especificar um tipo de retorno e um protocolo)

Jack James
fonte
2
Acho que essa abordagem é provavelmente a melhor do grupo. A atualização rápida é que o simpleDescription deve retornar self.rawValue
Justin Levi Winter
7

Aqui está uma solução que não altera o valor enum atual, mas seus valores de instância (apenas no caso de ser útil para alguém).

enum ProtoEnumeration : ExampleProtocol {
    case One(String)
    case Two(String)

    var simpleDescription: String {
        get {
            switch self {
            case let .One(desc):
                return desc
            case let .Two(desc):
                return desc
            }
        }
    }
    mutating func adjust() {
        switch self {
        case let .One(desc):
            self = .One(desc + ", adjusted 1")
        case let .Two(desc):
            self = .Two(desc + ", adjusted 2")
        }
    }
}

var p = ProtoEnumeration.One("test")
p.simpleDescription
p.adjust()
p.simpleDescription
DiogoNeves
fonte
Pontos extras para quem encontrar uma maneira de evitar todas essas mudanças. Algo nos moldes desta cópia fictíciaself = copy(self, self.desc + ", asdfasdf")
DiogoNeves
4

Não é possível definir variáveis ​​sem getter e setter em enums e, portanto, é impossível ter uma variável que você possa modificar.

Você pode estar em conformidade com o protocolo, mas não pode ter o mesmo comportamento com a mutação como nas classes.

Tomáš Linhart
fonte
2

É um link sobre enum in swift.

Estruturas e enumerações são tipos de valor. Por padrão, as propriedades de um tipo de valor não podem ser modificadas a partir de seus métodos de instância. ligação

Então, você tem que usar a função mutante.

enum ProtocolEnum: ExampleProtocol {
    case on, off
    var simpleDescription: String {
        switch self {
        case .on:
            return "Switch is ON"
        case .off:
            return "Switch is OFF"
        }
    }
    mutating func adjust() {
        switch self {
        case .on:
            self = off
        case .off:
            self = on
        }
    }
}

var c = ProtocolEnum.on
c.simpleDescription
c.adjust()
let cDescription = c.simpleDescription
Jeff Gu Kang
fonte
1

Outra opção é ajustar () para alternar entre os casos da seguinte forma:

enum SimpleEnum: ExampleProtocol {
    case Foo, Bar

    var simpleDescription: String {
    get {
        let value = self == .Foo
            ? "Foo"
            : "Bar"
        return "A simple \(value) enum."
    }
    }

    mutating func adjust() {
        self = self == .Foo
            ? .Bar
            : .Foo
    }
}
Endersstocker
fonte
1

Aqui está a construção da resposta de Jack:

protocol ICanWalk {
    var description: String { get }
    mutating func stepIt()
}

enum TwoStepsForwardThreeStepsBack: Int, ICanWalk {
    case Base = 0, Step1, Step2

    var description: String {
        return "Step \(self.rawValue)"
    }

    mutating func stepIt() {
        if let nextStep = TwoStepsForwardThreeStepsBack( rawValue: self.rawValue + 1 ) {
            // going forward.
            self = nextStep
        } else {
            // back to the base.
            self = TwoStepsForwardThreeStepsBack.Base
        }
    }
}
Alex Akhtyrskiy
fonte
1

Eu vim com isso

protocol ExampleProtocol {
    var simpleDescription: String { get }
    mutating func adjust()
}

enum Seat: ExampleProtocol {
    case WindowSeat, MiddleSeat, AisleSeat

    var simpleDescription : String {
        switch self {
        case .WindowSeat:
            return "Window Seat"
        case .MiddleSeat:
            return "Middle Seat"
        case .AisleSeat:
            return "Aisle Seat"
        }
    }

    mutating func adjust() {
        switch self {
        case .WindowSeat:
            self = .MiddleSeat
        case .MiddleSeat:
            self = . AisleSeat
        case .AisleSeat:
            self = .WindowSeat
        }
    }
}

var seat = Seat.MiddleSeat
print(seat.simpleDescription) // Middle Seat
seat.adjust()
print(seat.simpleDescription) // Aisle Seat
Groot
fonte
0

aqui está meu código

enum SimpleEnum: ExampleProtocol {
    case Base, Adjusted
    var simpleDescription: String {
        get {
            var description = "A simple enum."
            switch self {
            case .Base:
                return description
            case .Adjusted:
                return description + " - [adjusted]"
            }
        }
    }
    mutating func adjust() {
        self = SimpleEnum.Adjusted
    }
}
var simpleEnum = SimpleEnum.Base
simpleEnum.adjust()
simpleEnum.simpleDescription
CAM
fonte
0

Minha primeira contribuição aqui:

enum SimpleEnum: ExampleProtocol {
    case Basic(String), Adjusted(String)
    init() {
        self = SimpleEnum.Basic("A simple Enum")

    }

    var simpleDescription: String {
        get {
            switch self {
            case let .Basic(string):
                return string
            case let .Adjusted(string):
                return string
            }
        }
    }

    mutating func adjust() {
        self = SimpleEnum.Adjusted("full adjusted")

    }
}

var c = SimpleEnum()
c.adjust()
let cDescription = c.simpleDescription

Obrigado pelos outros!

Indra Rusmita
fonte
1
Você também poderia adicionar uma explicação?
Robert
@Robert deve ser autoexplicado como os outros, mas a diferença é que estou usando o método init no enum e tenho o enum básico padrão. então você verá isso quando criar um objeto enum como na estrutura e exemplo de classe no playground do Swift.
Indra Rusmita
0

Esse experimento também me confundiu, devido aos exemplos anteriores de SimpleClass e SimpleStructure mostrando a propriedade simpleDescription sendo modificada internamente, o que me fez pensar que precisava fazer a mesma coisa. Depois de examinar as outras respostas postadas aqui e ler a documentação oficial do Apple Swift 2.1, eu descobri o seguinte:

protocol ExampleProtocol {
     var simpleDescription: String { get }
     mutating func adjust()
}

enum SimpleEnum: ExampleProtocol {
    case Simple
    case Adjusted

    var simpleDescription: String {
        switch self {
        case .Simple:
            return "A simple enumeration"
        case .Adjusted:
            return "A simple enumeration somewhat changed."
        }
    }

    mutating func adjust() {
        self = .Adjusted
    }

    mutating func restore() {
        self = .Simple
    }
}

var d: SimpleEnum = .Simple
d.simpleDescription

d.adjust()
d.simpleDescription

d.restore()
d.simpleDescription

Observe também que nos exemplos fornecidos pela Apple para SimpleClass e SimpleStructure antes deste experimento, a descrição simples é perdida internamente - você não pode obter o valor original de volta (a menos, é claro, que o salve fora da classe / estrutura); foi isso que me levou a criar um método restore () para o exemplo SimpleEnum, que permite alternar entre os valores. Espero que isso seja útil para alguém!

William L. Marr III
fonte
0

Eu estava pensando que o objetivo é simplesmente manter o estado e usar uma descrição para tornar o estado atual mais fácil de ler:

enum SimpleEnum: ExampleProtocol {

    case Default, Adjusted

    init() {
        self = .Default
    }

    var simpleDescription: String { get { return "\(self) Value" }}

    mutating func adjust() {
        self = .Adjusted
    }
}

var simpleEnum = SimpleEnum()
simpleEnum.adjust()
let adjustedSimple = simpleEnum.simpleDescript
João
fonte
0

Outra variação: Usar valores associados para manter e exibir a opção anterior (da forma "Selecionado 1, ajustado de 2, ajustado de 1, ajustado de 2, ajustado de 1")

protocol ExampleProtocol {
     var simpleDescription: String { get }
     mutating func adjust()
}

indirect enum EnumWithDescription: ExampleProtocol {
    case option1(EnumWithDescription?)
    case option2(EnumWithDescription?)
    var simpleDescription: String {
        return "Selected " + getDescription()
    }
    internal func getDescription() -> String {
        var currentValue: String
        let previousValue : EnumWithDescription?
        switch self {
        case .option1(let previous):
            currentValue = "1"
            previousValue = previous
        case .option2(let previous):
            currentValue = "2"
            previousValue = previous
        }
        if let adjustedFrom = previousValue?.getDescription() {
            return "\(currentValue) adjusted from \(adjustedFrom)"
        }
        else {
            return "\(currentValue)"
        }
    }
    mutating func adjust() {
        switch self {
        case .option1:
            self = .option2(self)
        case .option2:
            self = .option1(self)
        }
    }
}
var d = EnumWithDescription.option1(nil)
d.simpleDescription
d.adjust()
d.adjust()
d.simpleDescription
// Output: "Selected 1, adjusted from 2, adjusted from 1, adjusted from 2, adjusted from 1"
nkalvi
fonte
-1

que tal agora

enum SimpleEnum : ExampleProtocol {
    case Desc(String)
    init() {
        self = Desc("a simple enum")
    }
    var simpleDescription:String {
        get {
            return (Mirror(reflecting: self).children.first!.value as? String)!
        }
    }
    mutating func adjust() {
        self = SimpleEnum.Desc(self.desc + " adjusted")
    }
}
var e = SimpleEnum()
e.simpleDescription    # => "a simple enum"
e.adjust()
e.simpleDescription    # => "a simple enum adjusted"
michex
fonte