Swift static var in switch case - swift

switch-case for comparing static vars isn't working as expected. However, specifying a type or comparing directly works. Please see below:
class AnimalHelper {
func loadAnimal(_ animal: Animal) {
// Doesn't compile
switch animal {
case .squirrel, .deer: loadGrass()
case .dolphin: return // not implemented
default: loadMeat()
}
// Direct comparison works
if animal == .squirrel || animal == .deer {
loadGrass()
} else if animal == .dolphin {
return // not implemented
} else {
loadMeat()
}
// Specifying the type explicitly also works
switch animal {
case Animal.squirrel, Animal.deer: loadGrass()
case Animal.dolphin: return // not implemented
default: loadMeat()
}
}
func loadGrass() {}
func loadMeat() {}
}
class Animal {
let id = 6 // Will be generated
let hasFourLegs = true
let numberOfEyes = 2
// ... //
static var squirrel: Animal { return .init() }
static var dolphin: Animal { return .init() }
static var puma: Animal { return .init() }
static var deer: Animal { return .init() }
}
extension Animal: Equatable {
public static func ==(lhs: Animal, rhs: Animal) -> Bool {
return lhs.id == rhs.id
}
}
I'm sure something about the above isn't quite right because of which I'm getting the following compilation errors:
Enum case 'squirrel' not found in type 'Animal'
Enum case 'deer' not found in type 'Animal'
Enum case 'dolphin' not found in type 'Animal'
Please let me know how is checking for equality in a switch-case is different from that in an if condition.

In Swift, switch-case may use several different rules to match the switch-value and the case labels:
enum case matching
In this case, you can use dot-leaded case labels, but unfortunately, your Animal is not enum.
(This is not the same as equality below, as enum cases may have associated values.)
pattern matching operator ~=
Swift searches an overload for the type of the switch-value and the type of case-label, if found, applies the operator and uses the the Bool result as indicating matches.
For this purpose, Swift needs to infer the types of case-labels independent of the switch-value, thus Swift cannot infer the types of case-labels with dot-leaded notation.
equality ==
When the switch-value is Equatable, Swift uses the equality operator == for matching the switch-value to the case-labels.
(There may be more which I cannot think of now.)
The detailed behavior is not well defined, but it's difficult for compilers to resolve two rules -- pattern matching and equality. (You may want to define custom matching with ~= for Equatable types.)
So, in Swift 3, dot-leaded notation in the case-labels only works for enum types.
But, as far as I checked, Swift 4 have made it. Try Xcode 9 (currently the latest is beta 3), and your code would compile. This behavior may change in the release version of Xcode 9, but you know how to work around.

Related

Swift: Same-Type requirement makes generic parameters equivalent?

I'm using swift 5 and try to compile the following code:
protocol BasicProtocol {
associatedtype T
var str: T {get set}
}
struct AItem<U>: BasicProtocol {
typealias T = U
var str: T
init<G: StringProtocol>(str: G) where G == T {
self.str = str
}
}
I got compilation error:
error: Test.playground:10:45: error: same-type requirement makes generic parameters 'G' and 'U' equivalent
init<G: StringProtocol>(str: G) where G == T {
^
How to make them equivalent? or I can't?
Thanks.
Update 1:
This is the problem I encountered: I want to declare struct "AItem", hoping it has a generic type "T". And this generic type will have some restrictions, such as: "T: StringProtocol". Then for some reason, I need to use an array to load these structs, and ensure that the generics of each structure can be set at will.
I learned that there is "type-erase" might can solve this. So I tried this way, but it seemed unsuccessful. The problems mentioned above have occurred.
Update 2:
struct AItem<T: StringProtocol> {
var aStr: T
}
var array: [AItem<Any>] = [AItem(aStr: "asdfasdf")]
Look,If you compile this code, you will get a compilation error:
error: Test.playground:5:13: error: type 'Any' does not conform to protocol 'StringProtocol'
var array: [AItem<Any>] = [AItem(aStr: "asdfasdf")]
^
If I use "var array: [AItem<String>]", I will not be able to put any other non-"String" but implemented "StringProtocol" instance in the array.
This is why I said I want "ensure that the generics of each structure can be set at will".
Update 3:
very thanks for #jweightman, now I update my question again.
protocol ConstraintProtocol {}
extension String: ConstraintProtocol{}
extension Data: ConstraintProtocol{}
extension Int: ConstraintProtocol{}
.......
struct AItem<T = which class has Implemented "ConstraintProtocol"> {
var aPara: T
init(aPara:T) {
self.aPara = aPara
}
}
// make a array to contain them
var anArray: [AItem<Any class which Implemented "ConstraintProtocol">] = [AItem(aPara: "String"), AItem(aPara: 1234), AItem(aPara: Data("a path")), …]
// then I can use any item which in anArray. Maybe I will implement a method to judge these generics and perform the appropriate action.
for curItem in anArray {
var result = handleItem(curItem)
do something...
}
func handleItem<T: ConstraintProtocol>(item: AItem<T>) -> Any? {
if (item.T is ...) {
do someThing
return ......
} else if (item.T is ...) {
do someThing
return ...
}
return nil
}
This is my whole idea, but all of which are pseudo-code.
It seems like type erasure is the answer to your problem. The key idea to the type erasure pattern is to put your strongly typed but incompatible data (like an AItem<String> and an AItem<Data>) inside of another data structure which stores them with "less precise" types (usually Any).
A major drawback of type erasure is that you're discarding type information—if you need to recover it later on to figure out what you need to do with each element in your array, you'll need to try to cast your data to each possible type, which can be messy and brittle. For this reason, I've generally tried to avoid it where possible.
Anyways, here's an example of type erasure based on your pseudo code:
protocol ConstraintProtocol {}
extension String: ConstraintProtocol{}
extension Data: ConstraintProtocol{}
extension Int: ConstraintProtocol{}
struct AItem<T: ConstraintProtocol> {
var aPara: T
init(aPara: T) {
self.aPara = aPara
}
}
struct AnyAItem {
// By construction, this is always some kind of AItem. The loss of type
// safety here is one of the costs of the type erasure pattern.
let wrapped: Any
// Note: all the constructors always initialize `wrapped` to an `AItem`.
// Since the member variable is constant, our program is "type correct"
// even though type erasure isn't "type safe."
init<T: ConstraintProtocol>(_ wrapped: AItem<T>) {
self.wrapped = wrapped
}
init<T: ConstraintProtocol>(aPara: T) {
self.wrapped = AItem(aPara: aPara);
}
// Think about why AnyAItem cannot expose any properties of `wrapped`...
}
var anArray: [AnyAItem] = [
AnyAItem(aPara: "String"),
AnyAItem(aPara: 1234),
AnyAItem(aPara: "a path".data(using: .utf8)!)
]
for curItem in anArray {
let result = handleItem(item: curItem)
print("result = \(result)")
}
// Note that this function is no longer generic. If you want to try to "recover"
// the type information you erased, you will have to do that somewhere. It's up
// to you where you want to do this.
func handleItem(item: AnyAItem) -> String {
if (item.wrapped is AItem<String>) {
return "String"
} else if (item.wrapped is AItem<Data>) {
return "Data"
} else if (item.wrapped is AItem<Int>) {
return "Int"
}
return "unknown"
}
An alternative to type erasure you could consider, which works well if there's a small, finite set of concrete types your generic could take on, would be to use an enum with associated values to define a "sum type". This might not be a good choice if the protocol you're interested in is from a library that you can't change. In practice, the sum type might look like this:
enum AItem {
case string(String)
case data(Data)
case int(Int)
}
var anArray: [AItem] = [
.string("String"),
.int(1234),
.data("a path".data(using: .utf8)!)
]
for curItem in anArray {
let result = handleItem(item: curItem)
print("result = \(result)")
}
func handleItem(item: AItem) -> String {
// Note that no casting is required, and we don't need an unknown case
// because we know all types that might occur at compile time!
switch item {
case .string: return "String"
case .data: return "Data"
case .int: return "Int"
}
}

Swift where condition to check if a property is implemented

I just found another way to make a great use of protocols and protocol extensions in Swift by extending the Optional protocol to add a function so I can provide default values.
I wrote a blog post about this here: https://janthielemann.de/random-stuff/providing-default-values-optional-string-empty-optional-string-swift-3-1/
The gist of the post is that I needed a clean and easy way to provide default values for optional String which are nil or empty. To do this, I created a Emptyable protocol end extended the Optional protocol like so:
protocol Emptyable {
var isEmpty: Bool { get }
}
extension Optional where Wrapped: Emptyable {
func orWhenNilOrEmpty<T: Emptyable>(_ defaultValue: T) -> T {
switch(self) {
case .none:
return defaultValue
case .some(let value) where value.isEmpty:
return defaultValue
case .some(let value):
return value as! T
}
}
}
extension String: Emptyable {}
Now the question is: Is there a way I can get rid of the Emptyable protocol and instead have a conditional check whether or not a property or function is implemented by the generic type so that I automatically get orWhenNilOrEmpty() for each and every type which has isEmpty?
UPDATE
As suggested by Paulo, the T generic is actually not needed and I created a operator for even quicker access and more convenient usage (at least I think so. Feel free to correct me, I'm always happy to learn new things and improve myself).
I call it the "not empty nil coalescing" operator (who can come up with a better names? I feel like I suck at naming things :/ ). Hopefully some day it helps somebody:
protocol Emptyable {
var isEmpty: Bool { get }
}
infix operator ???: NilCoalescingPrecedence
extension Optional where Wrapped: Emptyable {
func orWhenNilOrEmpty(_ defaultValue: Wrapped) -> Wrapped {
switch(self) {
case .none:
return defaultValue
case .some(let value) where value.isEmpty:
return defaultValue
case .some(let value):
return value
}
}
static func ???(left: Wrapped?, right: Wrapped) -> Wrapped {
return left.orWhenNilOrEmpty(right)
}
}
extension String: Emptyable {}
extension Array: Emptyable {}
extension MyStruct: Emptyable {
let text: String
let number: Int
var isEmpty: Bool { return text.isEmpty && number == 0 }
init(text: String, number: Int) {
self.text = text
self.number = number
}
}
let mandatoryNotEmptyString = optionalOrEmptyString ??? "Default Value"
let mandatoryNotEmptyStruct = optionalOrEmptyStruct ??? MyStruct(name: "Hello World", number: 1)
No, you cannot query if an object or value has a certain property as a constraint on an extension without using a protocol. That would require reflection in a way that is currently not implemented in Swift. Also, an isEmpty property could have different meanings for different types, so testing for the existence of a method or property instead of a protocol could lead to unexpected behaviour.
You could just write
if let unwrappedString = optionalString, !unwrappedString.isEmpty {
// Do stuff
} else {
// Use default value
}
No protocol or extension required and very readable.
In Swift 4, which is coming out this fall, String will conform to BidirectionalCollection, which inherits from Collection. The Collection protocol provides an isEmpty property, so your extension could be
extension Optional where Wrapped: Collection {
// ...
}
But even then you should consider to set empty strings to nil when storing them in the first place, because you now have two states (nil and empty) which seem to represent the exact same thing.

Using diff in an array of objects that conform to a protocol

I'm experimenting with using Composition instead of Inheritance and I wanted to use diff on an array of objects that comply with a given protocol.
To do so, I implemented a protocol and made it comply with Equatable:
// Playground - noun: a place where people can play
import XCPlayground
import Foundation
protocol Field:Equatable {
var content: String { get }
}
func ==<T: Field>(lhs: T, rhs: T) -> Bool {
return lhs.content == rhs.content
}
func ==<T: Field, U: Field>(lhs: T, rhs: U) -> Bool {
return lhs.content == rhs.content
}
struct First:Field {
let content:String
}
struct Second:Field {
let content:String
}
let items:[Field] = [First(content: "abc"), Second(content: "cxz")] // 💥 boom
But I've soon discovered that:
error: protocol 'Field' can only be used as a generic constraint because it has Self or associated type requirements
I understand why since Swift is a type-safe language that needs to be able to know the concrete type of these objects at anytime.
After tinkering around, I ended up removing Equatable from the protocol and overloading the == operator:
// Playground - noun: a place where people can play
import XCPlayground
import Foundation
protocol Field {
var content: String { get }
}
func ==(lhs: Field, rhs: Field) -> Bool {
return lhs.content == rhs.content
}
func ==(lhs: [Field], rhs: [Field]) -> Bool {
return (lhs.count == rhs.count) && (zip(lhs, rhs).map(==).reduce(true, { $0 && $1 })) // naive, but let's go with it for the sake of the argument
}
struct First:Field {
let content:String
}
struct Second:Field {
let content:String
}
// Requirement #1: direct object comparison
print(First(content: "abc") == First(content: "abc")) // true
print(First(content: "abc") == Second(content: "abc")) // false
// Requirement #2: being able to diff an array of objects complying with the Field protocol
let array1:[Field] = [First(content: "abc"), Second(content: "abc")]
let array2:[Field] = [Second(content: "abc")]
print(array1 == array2) // false
let outcome = array1.diff(array2) // 💥 boom
error: value of type '[Field]' has no member 'diff'
From here on, I'm a bit lost to be honest. I read some great posts about type erasure but even the provided examples suffered from the same issue (which I assume is the lack of conformance to Equatable).
Am I right? And if so, how can this be done?
UPDATE:
I had to stop this experiment for a while and totally forgot about a dependency, sorry! Diff is a method provided by SwiftLCS, an implementation of the longest common subsequence (LCS) algorithm.
TL;DR:
The Field protocol needs to comply with Equatable but so far I have not been able to do this. I need to be able to create an array of objects that comply to this protocol (see the error in the first code block).
Thanks again
The problem comes from a combination of the meaning of the Equatable protocol and Swift’s support for type overloaded functions.
Let’s take a look at the Equatable protocol:
protocol Equatable
{
static func ==(Self, Self) -> Bool
}
What does this mean? Well it’s important to understand what “equatable” actually means in the context of Swift. “Equatable” is a trait of a structure or class that make it so that any instance of that structure or class can be compared for equality with any other instance of that structure or class. It says nothing about comparing it for equality with an instance of a different class or structure.
Think about it. Int and String are both types that are Equatable. 13 == 13 and "meredith" == "meredith". But does 13 == "meredith"?
The Equatable protocol only cares about when both things to be compared are of the same type. It says nothing about what happens when the two things are of different types. That’s why both arguments in the definition of ==(::) are of type Self.
Let’s look at what happened in your example.
protocol Field:Equatable
{
var content:String { get }
}
func ==<T:Field>(lhs:T, rhs:T) -> Bool
{
return lhs.content == rhs.content
}
func ==<T:Field, U:Field>(lhs:T, rhs:U) -> Bool
{
return lhs.content == rhs.content
}
You provided two overloads for the == operator. But only the first one has to do with Equatable conformance. The second overload is the one that gets applied when you do
First(content: "abc") == Second(content: "abc")
which has nothing to do with the Equatable protocol.
Here’s a point of confusion. Equability across instances of the same type is a lower requirement than equability across instances of different types when we’re talking about individually bound instances of types you want to test for equality. (Since we can assume both things being tested are of the same type.)
However, when we make an array of things that conform to Equatable, this is a higher requirement than making an array of things that can be tested for equality, since what you are saying is that every item in the array can be compared as if they were both of the same type. But since your structs are of different types, you can’t guarantee this, and so the code fails to compile.
Here’s another way to think of it.
Protocols without associated type requirements, and protocols with associated type requirements are really two different animals. Protocols without Self basically look and behave like types. Protocols with Self are traits that types themselves conform to. In essence, they go “up a level”, like a type of type. (Related in concept to metatypes.)
That’s why it makes no sense to write something like this:
let array:[Equatable] = [5, "a", false]
You can write this:
let array:[Int] = [5, 6, 7]
or this:
let array:[String] = ["a", "b", "c"]
or this:
let array:[Bool] = [false, true, false]
Because Int, String, and Bool are types. Equatable isn’t a type, it’s a type of a type.
It would make “sense” to write something like this…
let array:[Equatable] = [Int.self, String.self, Bool.self]
though this is really stretching the bounds of type-safe programming and so Swift doesn’t allow this. You’d need a fully flexible metatyping system like Python’s to express an idea like that.
So how do we solve your problem? Well, first of all realize that the only reason it makes sense to apply SwiftLCS on your array is because, at some level, all of your array elements can be reduced to an array of keys that are all of the same Equatable type. In this case, it’s String, since you can get an array keys:[String] by doing [Field](...).map{ $0.content }. Perhaps if we redesigned SwiftLCS, this would make a better interface for it.
However, since we can only compare our array of Fields directly, we need to make sure they can all be upcast to the same type, and the way to do that is with inheritance.
class Field:Equatable
{
let content:String
static func == (lhs:Field, rhs:Field) -> Bool
{
return lhs.content == rhs.content
}
init(_ content:String)
{
self.content = content
}
}
class First:Field
{
init(content:String)
{
super.init(content)
}
}
class Second:Field
{
init(content:String)
{
super.init(content)
}
}
let items:[Field] = [First(content: "abc"), Second(content: "cxz")]
The array then upcasts them all to type Field which is Equatable.
By the way, ironically, the “protocol-oriented” solution to this problem actually still involves inheritance. The SwiftLCS API would provide a protocol like
protocol LCSElement
{
associatedtype Key:Equatable
var key:Key { get }
}
We would specialize it with a superclass
class Field:LCSElement
{
let key:String // <- this is what specializes Key to a concrete type
static func == (lhs:Field, rhs:Field) -> Bool
{
return lhs.key == rhs.key
}
init(_ key:String)
{
self.key = key
}
}
and the library would use it as
func LCS<T: LCSElement>(array:[T])
{
array[0].key == array[1].key
...
}
Protocols and Inheritance are not opposites or substitutes for one another. They complement each other.
I know this is probably now what you want but the only way I know how to make it work is to introduce additional wrapper class:
struct FieldEquatableWrapper: Equatable {
let wrapped: Field
public static func ==(lhs: FieldEquatableWrapper, rhs: FieldEquatableWrapper) -> Bool {
return lhs.wrapped.content == rhs.wrapped.content
}
public static func diff(_ coll: [Field], _ otherCollection: [Field]) -> Diff<Int> {
let w1 = coll.map({ FieldEquatableWrapper(wrapped: $0) })
let w2 = otherCollection.map({ FieldEquatableWrapper(wrapped: $0) })
return w1.diff(w2)
}
}
and then you can do
let outcome = FieldEquatableWrapper.diff(array1, array2)
I don't think you can make Field to conform to Equatable at all as it is designed to be "type-safe" using Self pseudo-class. And this is one reason for the wrapper class. Unfortunately there seems to be one more issue that I don't know how to fix: I can't put this "wrapped" diff into Collection or Array extension and still make it support heterogenous [Field] array without compilation error:
using 'Field' as a concrete type conforming to protocol 'Field' is not supported
If anyone knows a better solution, I'm interested as well.
P.S.
In the question you mention that
print(First(content: "abc") == Second(content: "abc")) // false
but I expect that to be true given the way you defined your == operator

In Swift, is there a way of determining whether an enum is based on a certain type (eg. String)?

In order to write generic code for an NSValueTransformer, I need to be able to check that an enum is of type String for example. Ie.:
enum TestEnum: String {
case Tall
case Short
}
I am expecially interested in a test that can be used with the guard statement. Something allong the line of:
guard let e = myEnum as <string based enum test> else {
// throw an error
}
Please note that not all enums have raw values. For eample:
enum Test2Enum {
case Fat
case Slim
}
Hence a check on the raw value type can not be used alone for this purpose.
EDIT
After some further investigation it has become clear that NSValueTransformer can not be used to transform Swift enums. Please see my second comment from matt's answer.
First, it's your enums, so you can't not know what type they are. Second, you're not going to receive an enum type, but an enum instance. Third, even if you insist on pretending not to know what type this enum is, it's easy to make a function that can be called only with an enum that has a raw value and check what type that raw value is:
enum E1 {
case One
case Two
}
enum E2 : String {
case One
case Two
}
enum E3 : Int {
case One
case Two
}
func f<T:RawRepresentable>(t:T) -> Bool {
return T.RawValue.self == String.self
}
f(E3.One) // false
f(E2.One) // true
f(E1.One) // compile error
Generics to the rescue :
func enumRawType<T>(of v:T)-> Any?
{ return nil }
func enumRawType<T:RawRepresentable>(of v:T)-> Any?
{
return type(of:v.rawValue)
}
enumRawType(of:E1.One) // nil
enumRawType(of:E2.One) // String.Type
enumRawType(of:E3.One) // Int.Type

Access an enum value by its hashvalue?

enum have a property named 'hashValue' which is its index inside the enum.
Now my question is, is it possible to access its value by using a number? Ex: let variable:AnEnum = 0
If you want to map enum values to integers, you should do so directly with raw values. For example (from the Swift Programming Language: Enumerations):
enum Planet: Int {
case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}
let possiblePlanet = Planet(rawValue: 7)
I don't believe there's any documentation promising that an enum's hashValue is anything in particular (if you have a link, I've be very interested). In the absence of that, you should be explicit in your assignment of raw values.
enum Opponent: String {
case Player
case Computer
static func fromHashValue(hashValue: Int) -> Opponent {
if hashValue == 0 {
return .Player
} else {
return .Computer
}
}
}
Explanation:
Since there is no way to get back an enum value from its hashValue, you have to do it manually. It's not pretty, but it works. You essentially create a function that allows you to pass in the index of the value you want and manually return that enum value back to the caller. This could get nasty with an enum with tons of cases, but it works like a charm for me.
Swift 4, iOS 12:
Simply make your enum with explicitly setting raw type (like Int in below example):
enum OrderStatus: Int {
case noOrder
case orderInProgress
case orderCompleted
case orderCancelled
}
Usage:
var orderStatus: OrderStatus = .noOrder // default value
print(orderStatus.rawValue) // it will print 0
orderStatus = .orderCompleted
print(orderStatus.rawValue) // it will print 2
Swift 4.2 🔸(Based on previous answer by #Stephen paul)
This answer uses switch instead of if/else clauses. And returns optional as your not garantueed that the hash provided will match.
enum CellType:String{
case primary,secondary,tierary
/**
* NOTE: Since there is no way to get back an enum value from its hashValue, you have to do it manually.
* EXAMPLE: CellType.fromHashValue(hashValue: 1)?.rawValue//👉primary
*/
static func fromHashValue(hashValue: Int) -> CellType? {
switch hashValue {
case 0:
return .primary
case 1:
return .secondary
case 2:
return .tierary
default:
return nil
}
}
}
Your requirement is to have this line of code working, where 0 is the hashValue of the enum variable (note that starting with Xcode 10, 0 is never a valid hashValue...):
let variable:AnEnum = 0
This is simply done by making your enum ExpressibleByIntegerLiteral and CaseIterable:
extension AnEnum: CaseIterable, ExpressibleByIntegerLiteral {
typealias IntegerLiteralType = Int
public init(integerLiteral value: IntegerLiteralType) {
self = AnEnum.allCases.first { $0.hashValue == value }!
}
}
The CaseIterable protocol is natively available with Swift 4.2, and you can implement it yourself for older swift versions (Swift 3.x, 4.x) using the code from https://stackoverflow.com/a/49588446/1033581.
Actually, with Swift 4.2, the hashValue is not the index inside the enum anymore.
Edit: Just found a safer way to achieve that. You can use the CaseIterable (Swift 4.2) and pick the desired case in the allCases collection.
enum Foo: CaseIterable {
case bar
case baz
init?(withIndex index: Int) {
guard Foo.allCases.indices ~= index else { return nil }
self = Foo.allCases[index]
}
}
Foo(withIndex: 0) // bar
Foo(withIndex: 1) // baz
Foo(withIndex: 2) // nil
Note: I'm leaving this little trick here, because playing with unsafe pointer is fun, but please, do not use this method to create a case with an index.
It relies on Swift memory representation which might change without notice, and is really unsafe because using a wrong index produce a runtime error.
That being said, in Swift, a case is represented using an Int in raw memory. So you can use this to build a case using unsafe pointers.
enum Foo {
case bar
case baz
init(withIndex index: UInt8) {
var temp: Foo = .bar
withUnsafeMutablePointer(to: &temp) { pointer in
let ptr = UnsafeMutableRawPointer(pointer).bindMemory(to: UInt8.self, capacity: 1)
ptr.pointee = index
}
self = temp
}
}
Foo(withIndex: 0) // bar
Foo(withIndex: 1) // baz
Foo(withIndex: 2) // runtime error !