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
How to Add Documentation to Enum Associated Values in Swift
Swift Package Manager Unable to Compile Ncurses Installed Through Homebrew
How to Rotate an Object Around Only One Axis in Realitykit
Nstableview Get Indexpath Having the Cell
Swift Generics Error: Cannot Convert Value of Type 'Type<T>' to Expected Argument Type 'Type<_>'
Swift 2, Protocol Extensions & Respondstoselector
Swift Error Comparing Two Arrays of Optionals
How to Delete Item from Collection View
Name Convention for Unwrapped Value in Swift
Extending Typed Array by Conforming to a Protocol in Swift 2
How to Save a Custom Class as an Attribute of a Coredata Entity in Swift 3
Get the First Day of Week Without Weekcalendarunit
Crop/Mask Circular Image Node in Sprite Kit Gives Jagged Edges
How Have Multiple Init() with Swift
Exc_Bad_Instruction Happens When Using Dispatch_Get_Global_Queue on iOS 7(Swift)