Swift "Enum" base object - swift

If I want to add an extension to every object I can do the following:
extension AnyObject
{
func myFunc() { ... }
}
Is there something similar where I can add a function to every Enum? In other words, what is the base "class" for every enum?

First of all, note that you cannot do an extension to AnyObject as above, as AnyObject is a protected protocol (to which all classes implicitly conform) rather than a type. See e.g.
Is there a way to add an extension to AnyObject?
Now, you could, however, extend any specific type as you show above, e.g., extension Int { ... }. However enum is not a type; rather a "container" of ordered raw representable values. So a more valid comparison could be "If I want to add an extension to every class, by extension class ...", which is, naturally, trickier.
Now, all enumerations with a rawValue and an initializer by rawValue conforms to protocol RawRepresentable, so you could extend this protocol for specific types of raw values:
extension RawRepresentable where RawValue == Int {
var sqrt: Double {
return pow(Double(rawValue),(1/2))
}
}
And this extension property would be available to all enumerations that explicitly use the same raw value type, in this case, Int:
enum MyIntegerLiteralEnum : Int {
case One = 1
case Two
case Three
case Four
}
print(MyIntegerLiteralEnum.One.sqrt)
print(MyIntegerLiteralEnum.Two.sqrt)
print(MyIntegerLiteralEnum.Four.sqrt)
/* 1.0
1.4142135623731
2.0 */
As a disclaimer, note that this extension will be made available to all types that conforms to RawRepresentable with a rawValue of type Int, not only enum types. E.g.:
struct Foo : RawRepresentable {
typealias RawValue = Int
var bar : Int
var rawValue: RawValue {
get {
return bar
}
}
init(rawValue bar: Int) {
self.bar = bar
}
}
var a = Foo(rawValue: 16)
print(a.sqrt) // 4.0

Related

Swift - Why String doesn't conform to RawRepresentable?

I'm learning Swift and cannot realize why this code is correct:
enum Test1: String {
case value
}
let test1 = Test1.value.rawValue
but this one is incorrect and shows me errors
struct MyStruct {
}
extension MyStruct: Equatable {
static func == (lhs: MyStruct, rhs: MyStruct) -> Bool {
return true
}
}
enum Test2: MyStruct {
case value
}
I browsed thru Swift.String sources and didn't find rawValue declaration. How does it work in Swift? Is String a built-in type that "automatically" conforms to RawRepresentable, but all other types have to explicitly declare its conformance?
Notice that Test.value has type Test1, not String.
There is special treatment (implicit conformance to RawRepresentable), but it applies to string-valued enums, not String itself.
Raw values can be strings, characters, or any of the integer or
floating-point number types. Each raw value must be unique within its
enumeration declaration.
https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html
But that's only for using the colon-based shortcut syntax. It's no problem to manually conform.
enum Test2 {
case value
}
extension Test2: RawRepresentable {
init?(rawValue: MyStruct) {
self = .value
}
var rawValue: MyStruct { .init() }
}

Swift: Index of an element in enum (CaseIterable)

The following code (compiles without errors) retrieves index of an element
in a particular CaseIterable enum type
public enum MyEnum : CaseIterable {
case ONE, TWO, THREE
public func ordinal() -> Int? {
return MyEnum.allCases.firstIndex(of: self)
}
}
I want to make a generic function to work with all CaseIterable enums.
If I try:
public extension CaseIterable {
public func ordinal() -> Int? {
return CaseIterable.allCases.firstIndex(of: self)
}
}
I get a compiler error "Member 'allCases' cannot be used on value of protocol type 'CaseIterable'; use a generic constraint instead" which is quite logical, as the actual enum type is unknown".
When I try CaseIterable<T>, I get another error, as CaseIterable is not declared as generic type.
Is there a way?
Couple of changes are necessary:
The return type needs to be Self.AllCases.Index? rather than Int?. In practice, these types will be equivalent, as seen below.
You also need to constrain any types to Equatable, because you need to be equatable in order to use firstIndex(of:). Again, in practice, any CaseIterable will usually be an enum without associated values, meaning it will be equatable automatically.
(Optional change) This function will never return nil, because you're finding one case in a CaseIterable. So you can remove the optionality on the return type (Self.AllCases.Index) and force unwrap.
Example:
public extension CaseIterable where Self: Equatable {
public func ordinal() -> Self.AllCases.Index {
return Self.allCases.firstIndex(of: self)!
}
}
enum Example: CaseIterable {
case x
case y
}
Example.y.ordinal() // 1
type(of: Example.y.ordinal()) // Int
Personally, I'd add that "Ordinal" usually means something different than what you're doing, and I'd recommend changing the function name to elementIndex() or something. But that's an aside.

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.

Swift 2: How to refer to a type of an element in default implementation of a function in a protocol

I need to get some objects from REST API and map them to local objects using ObjectMapper.
These objects contain a number of enumerations. All of them are coming as integers and I want to map them to locally described enums.
To do that I have to describe standard transform function that is used by ObjectMapper.
enum Types: Int {
case Uno = 1
case Dos = 2
case Tres = 3
static var transform = TransformOf<Types,Int>(
fromJSON: {
$0 != nil
? Types(rawValue:$0!)
: nil
},
toJSON: { $0?.rawValue})
}
The problem is that I have a number of these enumerations and the function is totally same in all of them except that first argument in TransformOf<..> list is specific for each enum.
What I want to do is to create a common protocol with default implementation of that function, something like
protocol Transformable {
var transform: TransformOf<self.Type,Int> {
get {
return TransformOf<self.Type,Int>(
fromJSON: {
$0 != nil
? Types(rawValue:$0!)
: nil
},
toJSON: { $0?.rawValue})
}
}
}
...and then to apply the protocol with the implementation to all of the enumerations I have.
Obviously reference of self.Type is not working there and I just can't get how to generally refer to type of specific instance that will finally use the function? Probably I'm thinking wrong way of solving that problem.
I think what you're missing is the Self identifier. When implementing Generics, the Self keyword acts as a placeholder for the type that implements your protocol. (For more information)
In other words:
protocol Transformable {
var rawValue: Int { get }
init?(rawValue: Int)
func toJSON() -> Int
static func fromJSON(rawValue: Int) -> Self?
}
Each enum that conforms to the protocol Transformable will then have a static method that returns its own type.
Secondly, since this is Swift 2, you can implement a protocol extension:
extension Transformable {
func toJSON() -> Int {
return self.rawValue
}
static func fromJSON(rawValue: Int) -> Self? {
return Self(rawValue: rawValue)
}
}
Now all enums that conform to the protocol will convert themselves to and from Int:
enum Types: Int, Transformable {
case Uno = 1
case Dos = 2
case Tres = 3
//No extra implementation
}
enum OtherTypes: Int, Transformable {
case Cuatro = 4
case Cinco = 5
case Seis = 6
//No extra implementation
}
print(Types.fromJSON(1)!) //prints Uno
print(OtherTypes.fromJSON(4)!) //prints Cuatro
print(Types.fromJSON(4)!) /* throws an error, because the Types enum
does not recognise 4 as a raw value */

Is it possible to declare swift generic for enums of particular type?

Would it be possible to have a function that allows any enum where the rawValue is of a certain type? For example, any enum that has a string rawValue.
This can be done using generics and the "where" keyword
enum EnumString: String {
case A = "test"
}
func printEnum<T: RawRepresentable where T.RawValue == String>(arg: T) {
print(arg.rawValue)
}
printEnum(EnumString.A) //Prints "test"
You can declare a generic that conforms to the type RawRepresentable, which is a protocol that all enums declaring a primitive rawValue conform to.
enum EnumA: Int {
case A = 0
}
enum EnumB {
case A
}
func doGenericSomething<T: RawRepresentable>(arg: T) {
println(arg.rawValue)
}
doGenericSomething(EnumA.A) //OK
doGenericSomething(EnumB.A) //Error! Does not conform to protocol RawRepresentable
You cannot, however, specify an enum's rawValue type in a generic. For information you can see the post here.