Understanding CaseIterable in swift - swift

I've read the documentation, and seen perhaps only a part of what the protocol is. I just am not following the logic. Can someone help me understand this?
What I see in xcode when I examine the protocol
/// Conforming to the CaseIterable Protocol
/// =======================================
///
/// The compiler can automatically provide an implementation of the
/// `CaseIterable` requirements for any enumeration without associated values
/// or `#available` attributes on its cases. The synthesized `allCases`
/// collection provides the cases in order of their declaration.
///
/// You can take advantage of this compiler support when defining your own
/// custom enumeration by declaring conformance to `CaseIterable` in the
/// enumeration's original declaration. The `CompassDirection` example above
/// demonstrates this automatic implementation.
public protocol CaseIterable {
/// A type that can represent a collection of all values of this type.
associatedtype AllCases : Collection = [Self] where Self == Self.AllCases.Element
/// A collection of all values of this type.
static var allCases: Self.AllCases { get }
}
I'm struggling to follow what is happening here and why. Can someone walk me through the logic of this please?
One of the other big struggles I'm having because of this is if I conform a protocol to be CaseIterable.
protocol Foo: CaseIterable {}
I can't use it as a variable anymore.
struct Bar {
var foo: Foo
}
I get this error
Protocol 'Foo' can only be used as a generic constraint because it has Self or associated type requirements.
It does have Self requirements but I can't figure out how to get around this problem. If someone could help me understand why this happens and how to fix it too, I'd be very grateful.
Edit: - This is the playground code copied directly. I've updated it to use the some, but I'm not quite sure how to proceed past this error.
import Foundation
protocol Zot: CaseIterable {
var prop: Data { get }
}
enum Bar: Zot {
case thing3
case thing4
var prop: Data {
switch self {
case .thing3, .thing4: return Data()
}
}
init() {}
}
enum Baz: Zot {
case thing1
case thing2
var prop: Data {
switch self {
case .thing1, .thing2: return Data()
}
}
init() {}
}
enum Foo {
case bar
case baz
var otherValues: some Zot {
switch self {
case .bar:
return Bar
case .baz:
return Baz
}
}
}

CaseIterable exists to allow you to programmatically walk through all the possible cases of an enum, allowing you use the enum type as a Collection of its cases:
enum CardinalDirection: CaseIterable { case north, south, east, west }
for direction in CardinalDirection.allCases {
// Do something with direction which is one of north, south, east, west
print("\(direction)")
}
This prints
north
south
east
west
There is nothing that prevents you from making other kinds of types conform to CaseIterable; however, the compiler will only synthesize conformance for enum types. It's not useful for most other kinds of types; however, I have occasionally found it useful for types that conform to OptionSet. In that case you have to manually implement conformance.
struct AssetFlags: OptionSet, CaseIterable
{
typealias RawValue = UInt8
typealias AllCases = [AssetFlags]
let rawValue: RawValue
static let shouldPreload = AssetFlags(rawValue: 0x01)
static let isPurgeable = AssetFlags(rawValue: 0x02)
static let isLocked = AssetFlags(rawValue: 0x04)
static let isCached = AssetFlags(rawValue: 0x08)
static var allCases: AllCases = [shouldPreload, isPurgeable, isLocked, isCached]
}
Note that OptionSet is conceptually similar to an enum. They both define a small set of distinct values they can have. With one they are mutually exclusive, while for the other they may be combined. But the key thing for CaseIterable to be useful is the finite nature of the set of possible distinct values. If your type has that characteristic, conforming to CaseIterable could be useful, otherwise, it wouldn't make sense. Int or String, for example, are not good candidates for CaseIterable.
In my own code, I don't bother conforming to CaseIterable, even for enum types, until a specific need arises that requires it. Actually I take that approach to all protocol conformance. It's a specific case of the more general YAGNI rule of thumb: "You ain't gonna need it."
Regarding your Bar struct, the problem is not specifically related to CaseIterable, but rather to using a protocol with Self or associated type requirements, which for Foo happens to be inherited from CaseIterable.
Swift 5.7 relaxed the rules concerning Self and associated type requirements a bit, allowing you to use the any keyword to tell the compiler you want to use an existential Foo instead of a concrete Foo to write
struct Bar {
var foo: any Foo
}
If you want a concrete Foo you could use some. The original way to do it though, which still works, is to make Bar explicitly generic
struct Bar<T: Foo> {
var foo: T
}
Update based on revised question code
The way you're using enums is... well, let's say it's out of the ordinary. There are two problems. The first is that you're returning types not values:
enum Foo {
case bar
case baz
// Will return a *value* of a type that conforms to Zot
var otherValues: some Zot
{
switch self {
case .bar:
return Bar // Bar is a *type* not a value
case .baz:
return Baz // Baz is a *type* not a value
}
}
}
I'll fix this is in a way that is almost certainly wrong for what you want to do, but allows moving forward to the other problem. We need to return values, so I'll just pick the first of the corresponding cases of Bar and Baz, and that will expose the other problem.
enum Foo {
case bar
case baz
var otherValues: some Zot
{
switch self {
case .bar: return Bar.thing3
case .baz: return Baz.thing1
}
}
}
The problem here is that some means that there will be one specific concrete type that conforms to Zot, so the compiler will be able to access its properties and methods directly rather than via its protocol witness table... it's basically a way to have the efficiency of having the calling code use the concrete types without having to tie to calling code to the concrete type at the source code level. otherValues, however, returns a value of either of two types, so the return type would have to be an existential type rather than a concrete one. You could do this if you return any Zot instead of some Zot.
Of course even using any, this version is wrong, because it doesn't take into account half of the cases of Bar and Baz. I assume that you want to be able to construct a Foo from a Bar or Baz while preserving its original value somehow, and I guess retrieve it later.
Before I present solutions, I want to mention that without knowing exactly what you are trying to accomplish, your code feels like it took a wrong design turn at some point. It would probably be better to rethink how you're doing what you want to do to see if there is a better way.
If I understand what your trying to do, I can think of at least three of ways, none of which requires CaseIterable, but maybe that's needed for other reasons.
Option 1
The first is to define Foo so that it explicitly contains all of the cases of Bar and Baz:
enum Foo: Zot
{
case thing1, thing2, thing3, thing4
init(_ value: Bar)
{
switch value
{
case .thing3: self = .thing3
case .thing4: self = .thing4
}
}
init(_ value: Baz)
{
switch value
{
case .thing1: self = .thing1
case .thing2: self = .thing2
}
}
var prop: Data
{
switch self
{
case .thing1: return Baz.thing1.prop
case .thing2: return Baz.thing2.prop
case .thing3: return Bar.thing3.prop
case .thing4: return Bar.thing4.prop
}
}
var bazValue: Baz?
{
switch self
{
case .thing1: return .thing1
case .thing2: return .thing2
default: return nil
}
}
var barValue: Bar?
{
switch self
{
case .thing3: return .thing3
case .thing4: return .thing4
default: return nil
}
}
}
This has the advantage of being straight-forward, but will require more maintenance if you add/remove cases from Bar or Baz - or even add a whole other enum that conforms to Zot.
Option 2
The second way is to define Foo so that it uses associated values:
enum Foo
{
case bar(value: Bar)
case baz(value: Baz)
init(_ value: Bar) { self = .bar(value: value) }
init(_ value: Baz) { self = .baz(value: value) }
}
I think this second case is cleaner, and you don't need otherValues because usage code can do:
switch foo
{
case let .bar(value: bar):
// do whatever with bar
case let .baz(value: baz):
// do whatever with baz
}
Still assuming second version of Foo, maybe an even cleaner way is:
extension Foo
{
func withZotValue<R>(_ code: (any Zot) throws -> R) rethrows -> R
{
switch self
{
case let .bar(value: value): return try code(value)
case let .baz(value: value): return try code(value)
}
}
}
That allows you to eliminate a lot of switch statements in usage code. To use it:
foo.withZotValue { zotValue in
// Do something with zotValue that the Zot protocol supports.
}
If you need this second version of Foo to conform to CaseIterable, Swift won't synthesize conformance for you because of the associated values, but you can write the conformance yourself.
extension Foo: CaseIterable
{
typealias AllCases = [Self]
static var allCases: AllCases {
[
Baz.allCases.map { .baz(value: $0) },
Bar.allCases.map { .bar(value: $0) },
].joined()
}
}
Option 3:
The last possible solution, which actually should probably be the first, if it applies, would be to define whatever you're trying to do that's common to both Bar and Baz in Zot. Whether that's a good idea or not depends on what you're trying to do, but let's assume it does make sense. For example, I notice that both Bar and Baz support a prop property, but the Zot protocol doesn't list prop. Why not? Is it unrelated to "Zotness"? If anything that conforms to Zot should have a prop property, then add it to the protocol:
protocol Zot: CaseIterable {
var prop: Data { get }
}
You still provide implementations of prop in Bar and Baz - that's kind of like overriding a base class method in subclasses.
Let's say the Data returned from prop is encoded JSON and you want be able to decode a Codable thing from a Zot. You can now do that without caring if its a Bar or a Baz (or any other new Zot-conforming type you might add later):
func decode<T: Codable>(_ type: T.Type, from src: some Zot) throws -> T {
return try JSONDecoder().decode(T.self, from: src.prop)
}

Related

XCT assert case of enum

I'd like to assert whether a value is a specific enum case.
For example, if I have the following enum class, and a variable let value: MyEnum:
enum MyEnum {
case firstCase(value: Int)
case secondCase
}
I'd like to check whether value is an instance of firstCase.
In essence, I'd like to be able to write the following or something equivalent:
let value: MyEnum = .firstCase(value: 3)
XCTAssertEnumCase(value, .firstCase)
How can I achieve this? I'm looking for an already existing XCT function, or for instructions how to write XCTAssertEnumCase myself.
You can easily create a function that works for specific enums, however, creating a generic assert function that works for any enums will be quite hard to achieve, because there's no protocol/type constraint that could represent any enum. You can use RawRepresentable for enums with raw values, but that won't cover all enums, such as the one in your question.
This is the function for your specific enum.
func XCTAssertEnumCase(_ testValue: MyEnum, _ expectedValue: MyEnum) -> Bool {
switch (testValue, expectedValue) {
case (.firstCase, .firstCase):
return true
case (.secondCase, .secondCase):
return true
default:
return false
}
}
Alternatively, you can make your enum conform to Equatable in your test target (if it doesn't already conform in your actual production target) and only check case equality in your Equatable conformance, but then you won't be able to easily test "full equality" (including the associated values). Also, the solution will require you to manually implement Equatable conformance for all protocols that you are testing.
You cannot instantiate an enum case that has an associated value without actually supplying an associated value. So XCTAssertEnumCase(value, .firstCase) cannot be achieved.
You can do XCTAssertEnumCase(testValue, .firstCase(value: 3313) where you can pass in any Int to the associated value of firstCase and as long as testValue is also firstCase, the func will return true, regardless of the associated values.
Alternatively, you could create separate functions for asserting each case of your enum.
extension MyEnum {
func assertFirstCase() -> Bool {
switch self {
case .firstCase:
return true
default:
return false
}
}
func assertSecondCase() -> Bool {
switch self {
case .secondCase:
return true
default:
return false
}
}
}
And then use it like this:
let value: MyEnum = .firstCase(value: 3)
value.assertFirstCase() // returns true
value.assertSecondCase() // returns false

Swift enum case access level

I know, that Swift does not allow redefinition of the access level of a case in a enum, meaning the following is not possible
public enum Foo {
private case Bar
private indirect case Ind(Foo)
public init() {
self = Bar
}
}
Can anyone tell me why this is not allowed? There are some scenarios, where it is practical to hide cases of a enum and providing a initializer that initiates to such a case, therefore I don't really see the motivation to disallow this feature.
Edit:
Consider the following example of a purely functional tree structure:
public enum Tree<T: Comparable> {
case Leaf
indirect case Node(T, Tree, Tree, Int)
public init() {
self = .Leaf
}
public func height() -> Int {
switch self {
case .Leaf:
return 0
case let .Node(_, _, _, h):
return h
}
}
// functions for add and remove, etc.
}
For better runtime (which is desirable when implementing self-balancing binary trees), one might want to include the height of the tree as an associated value in the Node case. But this gives encapsulation problems, as one might now construct Node cases with illegal heights. This problem would be solved if the access level of the case could be overwritten, or if stored constants in enums would be allowed.
Swift switch statements must be exhaustive. Suppose a consumer of your public enum Foo tries to use it in a switch. If the .bar case is private, how is the switch supposed to handle it?
Cases are part of the public API of an enum. If you wish to make them private, wrap them in a struct that exposes only public operations.
Update: With the implementation of SE-0192 in Swift 5, the #unknown default: syntax was introduced. This is useful in situations where you have a enum for which you've handled all currently existing cases, but want to protect yourself from future cases being added, by specifying the default behaviour.
You can 'implement' that behaviour by adding type with private constructor.
Here is an example:
public enum Foo {
case a(Barrier)
case b(Int, Int, Barrier)
public init(x: X?) {
if let x: X = x {
self = .b(x.y, x.z, Barrier())
} else {
self = .a(Barrer())
}
}
/// Use Foo constructor!
public struct Barrier {
fileprivate init() { }
}
}
Someone still can create instance that doesn't make any sense but will look tedious:
func createB() -> Foo {
switch Foo(x: nil) {
case .a(let barrier): return Foo.b(1, 2, barrier)
case .b(_, _, let barrier): return Foo.b(1, 2, barrier)
}
Also make sure that enums are really better than structs or objects.

Is there a Swift type for a string-based enum?

I have a function that takes a String tag argument:
func findFooByTag(_ tag: String) -> Foo
Now I would like to make the code shorter and safer by introducing an enum for the valid tag values:
enum Tags: String {
case one
case two
case three
}
But I still have to call the function with a String:
let foo = findFooByTag(Tags.one.rawValue)
Is there a way to say “findFooByTag takes any string-based enum”? I have found this:
func findFooByTag<T>(_ tag: T) -> Foo where T: RawRepresentable, T.RawValue == String
But that’s quite a mouthful. Is it at least possible to sweep that under the rug with a type alias somehow?
What you have found looks awesome, but still I would suggest something like the following:
protocol Taggable {
var raw: String { get }
}
extension String: Taggable {
var raw: String { return self }
}
enum Tag: String, Taggable {
var raw: String {
return self.rawValue
}
case one = "aa"
case two = "bb"
case three = "cc"
}
func findByTag(_ tag: Taggable) {
if tag.raw == "aa" { ... }
// do something...
}
findByTag(Tag.one) // works
findByTag("abc") // also works
As there is nothing in common between enum's having a String RawValue, there is no common type for these or no protocol to which all would conform.
However, Swift 4 introduces type constraints on associated types using where clauses as described in SE-0142. Using this new capability, you can define a protocol with an associated type the type constraints describing an enum with a String rawValue, then you only need to make your Tags enum conform to this protocol and you won't need the type constraint in your function definition anymore.
class Foo {}
protocol StringEnum {
associatedtype EnumType: RawRepresentable = Self where EnumType.RawValue == String
static func findFooByTag<EnumType>(_ tag: EnumType) -> Foo
}
extension StringEnum where EnumType == Self {
static func findFooByTag<EnumType>(_ tag: EnumType) -> Foo {
return Foo()
}
}
enum Tags: String, StringEnum {
case one
case two
case three
}
let foo = Tags.findFooByTag(Tags.one)
This is implementation of course could be improved, but this is just an example showing how you can use a where clause to implement the type constraint using a protocol and its associatedType.
Due to the default implementation fo the findFooByTag func in the protocol extension, you don't need to implement the function for all of your custom enum types having a String rawValue, you only need to declare them as conforming to the StringEnum protocol.
If you don't have Xcode9 installed, you can play around with this code in the IBM Swift Sandbox using this link.
Maybe you can try do that with CustomStringConvertible?
enum Tags: String, CustomStringConvertible {
case one
case two
case three
var description: String {
return self.rawValue
}
}
func findFooByTag<T>(_ tag: T) -> Foo where T: CustomStringConvertible
looks more better
or just
func findFooByTag<T>(_ tag: CustomStringConvertible) -> Foo
For this purpose you can use any kind of wrapper object. For example:
enum TypeNotSpecifiedTag {
case one
}
enum StringTag: String {
case one
}
enum IntTag: Int {
case one = 1
}
enum Wrapper<T>: RawRepresentable {
typealias RawValue = T
case value(T)
init?(rawValue: RawValue) {
self = .value(rawValue)
}
var rawValue: RawValue {
switch self {
case let .value(item):
return item
}
}
}
print(Wrapper.value(TypeNotSpecifiedTag.one).rawValue) // "one"
print(Wrapper.value(StringTag.one.rawValue).rawValue) // "one"
print(Wrapper.value(IntTag.one.rawValue).rawValue) // 1
Notice, that according to documentation about RawValue, you doesn't always need to specify RawValue, that's why first example also compile.

Generic constraints specific to an enum member

I have a protocol with an associated type:
protocol MyProtocol {
associatedtype Q
}
Now I'd like to have an enum like
enum MyEnum<Q> {
case zero
case one(MyProtocol)
case two(MyProtocol, MyProtocol)
}
where each associated value has Q as its associated type. This doesn't work:
enum MyEnum<Q> {
case zero
case one<P: MyProtocol where P.Q == Q>(P)
case two<P1: MyProtocol, P2: MyProtocol where P1.Q == Q, P2.Q == Q>(P1, P2)
}
Apparently, individual enum members can't have their own generic constraints.
The only thing I can think of is to move those constraints to the enum declaration, but this fixates the associated types. To demonstrate why that's not what I want, this is what I'd like to be able to do:
struct StructA: MyProtocol {
typealias Q = Int
}
struct StructB: MyProtocol {
typealias Q = Int
}
var enumValue = MyEnum.one(StructA())
enumValue = .two(StructB(), StructA())
enumValue = .two(StructA(), StructB())
Is there a way around this limitation?
Type erasure. The answer is always type erasure.
What you need is an AnyProtocol type:
struct AnyProtocol<Element>: MyProtocol {
typealias Q = Element
// and the rest of the type-erasure forwarding, based on actual protocol
}
Now you can create an enum that uses them
enum MyEnum<Q> {
case zero
case one(AnyProtocol<Q>)
case two(AnyProtocol<Q>, AnyProtocol<Q>)
}
For a deeper discussion of how to build the type erasers see A Little Respect for AnySequence.
Swift cannot discuss PATs (protocols with associated types) as real types or even abstract types. They can only be constraints. In order to use it as even an abstract type, you have to distill it into a type eraser. Luckily this is quite mechanical and in most cases not difficult. It's so mechanical that eventually the compiler will hopefully do the work for you. But someone has to build the box, and today that's you.

Adding a case to an existing enum with a protocol

I want to create a protocol that enforces a certain case on all enums conforming to this protocol.
For example, if I have a enum like this:
enum Foo{
case bar(baz: String)
case baz(bar: String)
}
I want to extend it with a protocol that adds another case:
case Fuzz(Int)
Is this possible?
Design
The work around is to use a struct with static variables.
Note: This is what is done in Swift 3 for Notification.Name
Below is an implementation on Swift 3
Struct:
struct Car : RawRepresentable, Equatable, Hashable, Comparable {
typealias RawValue = String
var rawValue: String
static let Red = Car(rawValue: "Red")
static let Blue = Car(rawValue: "Blue")
//MARK: Hashable
var hashValue: Int {
return rawValue.hashValue
}
//MARK: Comparable
public static func <(lhs: Car, rhs: Car) -> Bool {
return lhs.rawValue < rhs.rawValue
}
}
Protocol
protocol CoolCar {
}
extension CoolCar {
static var Yellow : Car {
return Car(rawValue: "Yellow")
}
}
extension Car : CoolCar {
}
Invoking
let c1 = Car.Red
switch c1 {
case Car.Red:
print("Car is red")
case Car.Blue:
print("Car is blue")
case Car.Yellow:
print("Car is yellow")
default:
print("Car is some other color")
}
if c1 == Car.Red {
print("Equal")
}
if Car.Red > Car.Blue {
print("Red is greater than Blue")
}
Note:
Please note this approach is not a substitute for enum, use this only when the values are not known at compile time.
no, since you can't declare a case outside of an enum.
An extension can add a nested enum, like so:
enum Plants {
enum Fruit {
case banana
}
}
extension Plants {
enum Vegetables {
case potato
}
}
Here are a couple additional takes that may help somebody out there:
Using your example:
enum Foo {
case bar(baz: String)
case baz(bar: String)
}
You can consider to "nest" it in a case of your own enum:
enum FooExtended {
case foo(Foo) // <-- Here will live your instances of `Foo`
case fuzz(Int)
}
With this solution, it becomes more laborious to access the "hidden" cases associated type. But this simplification could actually be beneficial in certain applications.
Another alternative passes by just recreate and extend it while having a way to convert Foo into the extended enum FooExtended (eg. with a custom init):
enum FooExtended {
case bar(baz: String)
case baz(bar: String)
case fuzz(Int)
init(withFoo foo: Foo) {
switch foo {
case .bar(let baz):
self = .bar(baz: baz)
case .baz(let bar):
self = .baz(bar: bar)
}
}
}
There may be many places where one, the other, or both of these solutions make absolutely no sense, but I'm pretty sure they may be handy to somebody out there (even if only as an exercise).