Swift - Why String doesn't conform to RawRepresentable? - swift

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() }
}

Related

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.

Conversion operator in swift

Is it possible to write custom conversion (casting) operator in swift? Especially I'm looking for enums conversion, ex:
enum MyEnum : Int {
case Case1 = 0
case Case2
func __conversion() -> String { // doesn't work since Swift 1.0
switch self {
case Case1: return "Case 1"
case Case2: return "Case 2"
}
}
}
let enumStr: String = MyEnum.Case1
Of course, I can convert to String with explicit method, but I would like to have implicit mechanism.
Disclaimer/TL;DR! This answer pertains to the technical question as to whether we can possibly implement implicit bridging mechanisms between different Swift types ourself. The answer is: for some cases, yes, but only in a limited sense and by means of "hacks": do not use this is production code!
Swift internal protocol abuse: we may implement implicit mechanisms to Obj-C objects (e.g. NSNumber, NSString ...)
As MartinR writes in his comment, custom conversion methods are not present for (native) Swift.
For the technical discussion we can, however, (ab)use the internal protocol _ObjectiveCBridgeable to allow implicit bridging from your enum to Obj-C objects, in this case e.g. NSString. For a more detailed Q&A of the subject of the internal protocol _ObjectiveCBridgeable, see
Is it possible to replicate Swifts automatic numeric value bridging to Foundation (NSNumber) for (U)Int8/16/32/64 types?
Before proceeding, I'll quote a disclaimer from my answer in thread above:
... note that _ObjectiveCBridgeable is an internal/hidden protocol
(_UnderScorePreFixedProtocol), so solutions based on it might break
without warning in upcoming Swift versions.
Example #1: implementing implicit bridging of your enum to NSString
First lets add a failable initializer to your enum, allowing (attempted) initialization by String instances:
import Foundation
enum MyEnum: Int {
case Case1 = 0
case Case2
init?(string: String) {
switch string {
case "Case 1": self = .Case1
case "Case 2": self = .Case2
default: return nil
}
}
}
Next up, let MyEnum conform to _ObjectiveCBridgeable, as described in more detail in the thread linked to above
extension MyEnum: _ObjectiveCBridgeable {
typealias _ObjectiveCType = NSString
static func _isBridgedToObjectiveC() -> Bool {
return true
}
static func _getObjectiveCType() -> Any.Type {
return _ObjectiveCType.self
}
func _bridgeToObjectiveC() -> _ObjectiveCType {
return NSString(string: "Case \(self.rawValue+1)")
}
static func _forceBridgeFromObjectiveC(source: _ObjectiveCType, inout result: MyEnum?) {
result = MyEnum(string: source as String)
}
static func _conditionallyBridgeFromObjectiveC(source: _ObjectiveCType, inout result: MyEnum?) -> Bool {
self._forceBridgeFromObjectiveC(source, result: &result)
return true
}
}
With the conformance above, we can now make use of implicit bridging from MyEnum instances to NSString
/* example usage */
var myCase: MyEnum = .Case1
var enumNSstr: NSString = myCase // MyEnum -> NSString, implicit
print(enumNSstr) // Case 1
enumNSstr = "Case 2"
// NSString -> MyEnum, by type conversion (castable)
myCase = (enumNSstr as MyEnum) ?? .Case1
print(myCase) // Case 2
Example #2: implementing implicit bridging of your enum to a custom Swift native type
We may even abuse the _ObjectiveCBridgeable protocol further, using its (deep backend) mechanisms to implement implicit bridging between two native Swift types, with the limitation that the type bridged to must be a reference type (specifically: instances of the type must be representable by AnyObject, hence the reference type limitation).
Let MyEnum be as defined above, but additionally, define a reference (class) type Foo, and conform MyEnum to _ObjectiveCBridgeable with the bridged to type, _ObjectiveCType being set to Foo.
class Foo {
var bar: String
init(bar: String) { self.bar = bar }
}
extension MyEnum: _ObjectiveCBridgeable {
typealias _ObjectiveCType = Foo
static func _isBridgedToObjectiveC() -> Bool {
return true
}
static func _getObjectiveCType() -> Any.Type {
return _ObjectiveCType.self
}
func _bridgeToObjectiveC() -> _ObjectiveCType {
return Foo(bar: "Case \(self.rawValue+1)")
}
static func _forceBridgeFromObjectiveC(source: _ObjectiveCType, inout result: MyEnum?) {
result = MyEnum(string: source.bar)
}
static func _conditionallyBridgeFromObjectiveC(source: _ObjectiveCType, inout result: MyEnum?) -> Bool {
self._forceBridgeFromObjectiveC(source, result: &result)
return true
}
}
We can now make use of implicit bridging from MyEnum instances to Foo
/* example usage */
var myCase: MyEnum = .Case1
var myFoo: Foo = myCase // MyEnum -> Foo, implicit
print(myFoo.bar) // Case 1
myFoo.bar = "Case 2"
// Foo -> MyEnum, by type conversion (castable)
myCase = (myFoo as? MyEnum) ?? .Case1
print(myCase) // Case 2
Finally note that you may, for any given type (say, MyEnum), naturally only implement implicit bridging to a single other (reference) type; since you can only conform to _ObjectiveCType once (for a unique type for the typealias _ObjectiveCType), otherwise yielding a compile time error for redundant protocol conformance.
The above is tested for Swift 2.2.

Swift "Enum" base object

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

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.