If-Let Any to Rawrepresentable<String>

if-let Any to RawRepresentable String

Ok, so this is basically not doable currently out of the box, as you can't as?-cast to RawRepresentable, and Mirror does not provide rawValue for enums.

I'd say the best bet is to make own protocol, provide default implementation for String-based RawRepresentable and conform all enums manually like so:

Assuming these are the enums:

enum E1: String { case one }
enum E2: String { case two }
enum E3: String { case three }

StringRawRepresentable protocol and default implementation:

protocol StringRawRepresentable {
var stringRawValue: String { get }
}

extension StringRawRepresentable
where Self: RawRepresentable, Self.RawValue == String {
var stringRawValue: String { return rawValue }
}

Conform all needed existing enums to the protocol:

extension E1: StringRawRepresentable {}
extension E2: StringRawRepresentable {}
extension E3: StringRawRepresentable {}

And now we can cast to StringRawRepresentable:

func stringFromPossibleEnum(possibleEnum: Any) -> String? {
if let e = possibleEnum as? StringRawRepresentable { return e.stringRawValue }
return nil
}

stringFromPossibleEnum(possibleEnum: E2.two as Any)

How to specify the type of a function parameter with combined type CustomStringConvertible and RawRepresentable?

The issue is that T.RawValue can be something else than Int with your current type constraints. You need to specify that T.RawValue == Int in order to pass your rawValue: Int input parameter to init(rawValue:).

func myFunc<T: RawRepresentable & CustomStringConvertible>(rawValue: Int, skipList: [T]) where T.RawValue == Int {
let thing = T.init(rawValue: rawValue)
}

Testing for compliance with and casting to RawRepresentable protocol

As @vadian said, all those type checks can be replaced be a single call to UserDefaults.standard.object() and conditional casting. Also the type of the value property needs to be an optional to handle the case where the property is not set (or not of the correct type):

struct DefaultKey<T> {
let storageKey: String

var value: T? {
get {
return UserDefaults.standard.object(forKey: storageKey) as? T
}
nonmutating set {
UserDefaults.standard.set(newValue, forKey: storageKey)
}
}
}

And then you can define a constrained extension method where you specialize the computed property for the case of RawRepresentable types:

extension DefaultKey where T: RawRepresentable {
var value: T? {
get {
if let rawValue = UserDefaults.standard.object(forKey: storageKey) as? T.RawValue {
return T(rawValue: rawValue)
}
return nil
}
nonmutating set {
UserDefaults.standard.set(newValue?.rawValue, forKey: storageKey)
}
}
}

Example usage:

enum Direction : Int {
case left = 0
case right = 1
}

let key1 = DefaultKey<Int>(storageKey: "foo")
key1.value = 123
let key2 = DefaultKey<Direction>(storageKey: "bar")
key2.value = .right

print(key1.value as Any) // Optional(123)
print(key2.value as Any) // Optional(Direction.right)

Note that this can still crash if used with non-property-list types. To be on the safe side, you would have to restrict the extensions to types which are known to be user defaults storable (integers, floats, strings, ...):

protocol UserDefaultsStorable {}
extension Int: UserDefaultsStorable {}
extension Float: UserDefaultsStorable {}
// ...

struct DefaultKey<T> {
let storageKey: String
}

extension DefaultKey where T: UserDefaultsStorable { .. }

extension DefaultKey where T: RawRepresentable, T.RawValue: UserDefaultsStorable { ... }

enum conformance to RawRepresentable

Apparently your definition of init?(rawValue: Int) prevents the compiler from inferring the RawValue type automatically. Adding a type alias helps:

enum WeekDay: String {
typealias RawValue = String

case monday, tuesday, wednesday, thursday, friday

init?(rawValue: Int){
switch rawValue {
case 0 : self = .monday
case 1 : self = .tuesday
case 2 : self = .wednesday
case 3 : self = .thursday
case 4 : self = .friday
default : return nil
}
}
}

Alternatively define your custom init function with a different parameter name:

enum WeekDay: String {

case monday, tuesday, wednesday, thursday, friday

init?(rawInt: Int){
switch rawInt {
case 0 : self = .monday
case 1 : self = .tuesday
case 2 : self = .wednesday
case 3 : self = .thursday
case 4 : self = .friday
default : return nil
}
}
}


Related Topics



Leave a reply



Submit