How to capture value of optionals in a switch statement in Swift? - swift

I have a function where I parse a String into a Bool, Double or Int before falling back to a String. Currently I use a if-else conditions like this
func parse(stringValue: String) {
if let value = Bool(stringValue) {
// do something with a Bool
print(value)
} else if let value = Int(stringValue) {
// do something with a Int
print("\(stringValue) is Int: \(value)")
} else if let value = Double(stringValue) {
// do something with a Double
print("\(stringValue) is Double: \(value)")
} else {
// do something with a String
print("String: \(stringValue)")
}
}
This is ok but my personal preference is to use switch statements in Swift but I don't know how to do so without force unwrapping:
func parse(stringValue: String) {
switch stringValue {
case _ where Bool(stringValue) != nil:
let value = Bool(stringValue)!
// do something with Bool
case _ where Int(stringValue) != nil:
let value = Int(stringValue)!
// do something with Int
case _ where Double(stringValue) != nil:
let value = Double(stringValue)!
// do something with Double
default:
// do something with String
}
}
How can I capture the result of the where so that I can use it in the case scope?

Related

Swift abstraction for enums with associated value

I have an enum with associated value ConfigurationValue that gets stored in a map. I have type specific getters for each possible enum value.
How do I minimize the code duplication between these getters
enum ConfigurationValue {
case bool(Bool)
case int(Int)
case string(String)
}
var map: [T:ConfigurationValue] = [:]
public mutating func set(key: T, _ value: Bool) {
map[key] = .bool(value)
}
public mutating func set(key: T, _ value: Int) {
map[key] = .int(value)
}
public mutating func set(key: T, _ value: String) {
map[key] = .string(value)
}
private func getValue(key: T) -> ConfigurationValue? {
return map[key]
}
public func get(key: T) -> Bool? {
guard let value = getValue(key: key),
case .bool(let innerValue) = value else {
return nil
}
return innerValue
}
public func get(key: T) -> Int? {
guard let value = getValue(key: key),
case .int(let innerValue) = value else {
return nil
}
return innerValue
}
public func get(key: T) -> String? {
guard let value = getValue(key: key),
case .string(let innerValue) = value else {
return nil
}
return innerValue
}
I would not use protocols but instead I would add two methods in ConfigurationValue the first to create a configuration value and the other to get the inner value of a case.
enum ConfigurationValue {
case bool(Bool)
case int(Int)
case string(String)
static func with<Value>(value: Value?) -> ConfigurationValue? {
if let value = value as? Bool {
return .bool(value)
}
if let value = value as? Int {
return .int(value)
}
if let value = value as? String {
return .string(value)
}
return nil
}
func get<Value>(ofType type: Value.Type = Value.self) -> Value? {
switch self {
case .bool(let bool):
return bool as? Value
case .int(let int):
return int as? Value
case .string(let string):
return string as? Value
}
}
}
let boolValue: Bool? = ConfigurationValue.bool(false).get() // Optional(false)
let intValue: Int? = ConfigurationValue.bool(false).get() // nil
let stringValue: String? = ConfigurationValue.bool(false).get() // nil
let boolConfiguration: ConfigurationValue? = .with(value: true) // bool(true)
let intConfiguration: ConfigurationValue? = .with(value: 42) // int(42)
let stringConfiguration: ConfigurationValue? = .with(value: "42") // string("42")
let doubleConfiguration: ConfigurationValue? = .with(value: 42.0) // nil
Now you can avoid boilerplate, let's say you have a struct Foo:
struct Foo<T: Hashable> {
var map: [T:ConfigurationValue] = [:]
mutating func set<Value>(_ value: Value?, forKey key: T) {
map[key] = .with(value: value)
}
func get<Value>(key: T) -> Value? {
guard let value = map[key] else {
return nil
}
return value.get()
}
}
var map: [String:ConfigurationValue] = [
"bool": .bool(false),
"int": .int(0),
"string": .string("string")
]
var bar = Foo(map: map)
var bool: Bool? = bar.get(key: "bool") // Optional(false)
var int: Int? = bar.get(key: "int") // 0
var string: String? = bar.get(key: "string") // string
bar.set(true, forKey: "bool")
bool = bar.get(key: "bool") // Optional(true)
bar.set(42, forKey: "int")
int = bar.get(key: "int") // 42
bar.set("Hello World!", forKey: "string")
string = bar.get(key: "string") // Hello World!
It doesn't seem like an enum is the right tool for the job here since you can get the same usefulness out of just casting the values (though admittedly you wouldn't get the benefit of exhaustive switch errors). If you're wanting to utilize generics to cut down on some of this duplication, I'd go with a wrapper around the map like so:
protocol Configurable {}
extension Int: Configurable {}
extension Bool: Configurable {}
extension String: Configurable {}
class ConfigurationMap<Key: Hashable> {
private var map = [Key: Configurable]()
func getValue(at key: Key) -> Configurable? {
return map[key]
}
func setValue(_ value: Configurable, at key: Key) {
map[key] = value
}
}
Here Key would replace your T generic. To incorporate more data types, just extend the type that you want to use to be Configurable. The ConfigurationMap can also be a struct if you don't mind the mutating methods.
In Swift, this is not called a "map", but instead, a "dictionary". Type restriction can be achieved with a protocol, and explicit typing for retrieval.
public protocol ConfigurationValue { }
extension Int: ConfigurationValue { }
extension Bool: ConfigurationValue { }
extension String: ConfigurationValue { }
public struct ConfigurationDictionary<Key: Hashable> {
private var dictionary: [Key: ConfigurationValue] = [:]
}
public extension ConfigurationDictionary {
subscript<Value: ConfigurationValue>(key: Key) -> Value? {
get { dictionary[key] as? Value }
set { dictionary[key] = newValue }
}
}
var configurationDictionary = ConfigurationDictionary<String>()
configurationDictionary["i"] = 1
configurationDictionary["i"] as Int? // 1
configurationDictionary["i"] as Bool? // nil
configurationDictionary["b"] = true
let b: Bool? = configurationDictionary["b"] // true
let i: Int? = configurationDictionary["b"] // nil

Accessing the optional value of a PartialKeyPath in Swift 4

Using the PartialKeyPath API, how can you access a value of a key path's reference? For example, this works for non-optional values, but not with Optional values.
The issue I'm having is that self[keyPath: keyPath] returns a non-optional Any value.
struct Element {
let name: String
let mass: Double?
func stringValue(_ keyPath: PartialKeyPath<Element>) -> String {
let value = self[keyPath: keyPath]
switch value {
case let string as String:
return string.capitalized
case nil:
return "N/A"
case let value:
return String(describing: value)
}
}
}
let element = Element(name: "Helium", mass: 4.002602)
let string = element.stringValue(\Element.mass) /* Optional(4.002602) */
The result is that case nil is never executed and the last case is being printed as Optional(value).
How can I unwrap value properly to extract the optional?
The solution was to use Mirror to unwrap the optional which seems less than optimal. Looking forward to better Reflection support in Swift!
func unwrap(_ value: Any) -> Any? {
let mirror = Mirror(reflecting: value)
if mirror.displayStyle != .optional {
return value
}
if let child = mirror.children.first {
return child.value
} else {
return nil
}
}
struct Element {
let name: String
let mass: Double?
func stringValue(_ keyPath: PartialKeyPath<AtomicElement>) -> String {
guard let value = unwrap(self[keyPath: keyPath]) else {
return "N/A"
}
switch value {
case let string as String:
return string.capitalized
default:
return String(describing: value)
}
}
}
let element = Element(name: "Helium", mass: 4.002602)
let string = element.stringValue(\Element.mass) /* 4.002602 */

Extending swift optional for default values

I'm trying to extend Swift's Optional type with default values. Providing empty values in API requests should raise an exception. I've done this for the String type, but I can't achieve the same result with the Integer type:
extension Optional where Wrapped == String {
var unwrappedValue: String {
get {
switch self {
case .some(let value):
return value
case .none:
return ""
}
}
}
}
The Integer version is throwing the following Error:
Protocol 'Integer' can only be used as a generic constraint because it
has Self or associated type requirements
extension Optional where Wrapped == Integer {
var unwrappedValue: Integer {
get {
switch self {
case .some(let value):
return value
case .none:
return 0
}
}
}
}
If you use this for a lot of Types you might want to consider the following addition to the answer of Leo Dabus:
protocol Defaultable {
static var defaultValue: Self { get }
}
extension Optional where Wrapped: Defaultable {
var unwrappedValue: Wrapped { return self ?? Wrapped.defaultValue }
}
This way you can extend your types very easily:
extension Int: Defaultable {
static var defaultValue: Int { return 0 }
}
extension String: Defaultable {
static var defaultValue: String { return "" }
}
extension Array: Defaultable {
static var defaultValue: Array<Element> { return [] }
}
And usage goes like this:
let optionalInt: Int? = 10 // Optional(10)
let unwrappedInt = optionalInt.unwrappedValue // 10
let optionalString: String? = "Hello" // Optional("Hello")
let unwrappedString = optionalString.unwrappedValue // "Hello"
let optionalArray: [Int]? = nil // nil
let unwrappedArray = optionalArray.unwrappedValue // []
You just need to return Wrapped instead of Integer
extension Optional where Wrapped: Integer {
var unwrappedValue: Wrapped {
switch self {
case .some(let value):
return value
case .none:
return 0
}
}
}
or simply
extension Optional where Wrapped: Integer {
var safelyUnwrapped: Wrapped { return self ?? 0 }
}
let optionalInt = Int("10")
let unwrappedValue = optionalInt.safelyUnwrapped // 10
You can also achieve using below code:
extension Optional {
func defaultValue(_ val: Wrapped) -> Wrapped { return self ?? val }
}
var str: String?
str.defaultValue("User")
var a: Int?
a.defaultValue(2)
This will work for both data types.

enum method returning a dynamic type

I have an enum and I'd like to create a method to return a different type for every case.
For example, I have a dictionary [String: Any]. To process the values I'm using the enum to create an array of keys:
enum Foo {
case option1
case option2
func createKey() -> [String] {
switch self {
case .option1: return ["scenario1"]
case .option2: return ["scenario2"]
}
}
}
Once I have the values, I need to cast them to a the proper type to be able to use them. Right now I'm doing it manually using if-statements but it would reduce a lot of code if I can somehow create a method in the enum to return the proper type. My current code:
let origin: [String: Any] = ["scenario2": "someText"]
let option: Foo = .option2
option.createKey().forEach {
guard let rawValue = origin[$0] else { return }
switch option {
case .option1:
guard let value = rawValue as? Int else { return }
print("Value is an Int:", value)
case .option2:
guard let value = rawValue as? String else { return }
print("Value is a String:", value)
}
}
What I would like to achieve is something like:
option.createKey().forEach {
guard let rawValue = origin[$0] as? option.getType() else { return }
}
Is this possible?
I think the core of the problem here is that Swift has strict typing. That means types must be known at compile time. This, obviously, is legal:
let s : Any = "howdy"
if let ss = s as? String {
print(ss)
}
But this is not legal:
let s : Any = "howdy"
let someType = String.self
if let ss = s as? someType { // *
print(ss)
}
someType must be a type; it cannot be a variable hiding a type inside itself. But that is precisely what, in effect, you are asking to do.

How to unwrap an optional value from Any type?

Given an array of [Any] that has a mix of optional and non optional values, e.g:
let int:Int? = 1
let str:String? = "foo"
let values:[Any] = [int,2,str,"bar"]
How can we extract the value of the Optional in the Any type (if there is one) so we can create a generic print function that only prints out the values.
E.g. this printArray function goes through and prints each element:
func printArray(values:[Any]) {
for i in 0..<values.count {
println("value[\(i)] = \(values[i])")
}
}
printArray(values)
Which will output:
value[0] = Optional(1)
value[1] = 2
value[2] = Optional("foo")
value[3] = bar
How can we change it so it only prints the underlying value so that it unwraps the value if it's Optional? e.g:
value[0] = 1
value[1] = 2
value[2] = foo
value[3] = bar
Update Progress...
It can work when changing the argument to [Any?], e.g:
let values:[Any?] = [int,2,str,"bar"]
func printArray(values:[Any?]) {
for i in 0..<values.count {
println("value[\(i)] = \(values[i]!)")
}
}
printArray(values)
Which will print the desired:
value[0] = 1
value[1] = 2
value[2] = foo
value[3] = bar
But would still like to see how we can unwrap an Optional from Any as this is what MirrorType.value returns making it difficult to extract the Optional value, e.g:
class Person {
var id:Int = 1
var name:String?
}
var person = Person()
person.name = "foo"
var mt:MirrorType = reflect(person)
for i in 0 ..< mt.count {
let (name, pt) = mt[i]
println("\(name) = \(pt.value)")
}
Prints out:
id = 1
name = Optional("foo")
When I need:
id = 1
name = foo
For Xcode 7 and Swift 2:
func unwrap(any:Any) -> Any {
let mi = Mirror(reflecting: any)
if mi.displayStyle != .Optional {
return any
}
if mi.children.count == 0 { return NSNull() }
let (_, some) = mi.children.first!
return some
}
let int:Int? = 1
let str:String? = "foo"
let null:Any? = nil
let values:[Any] = [unwrap(int),2,unwrap(str),"bar", unwrap(null)]
This will give you [1, 2, "foo", "bar", {NSObject}]
Change NSNull() to nil and the return value of unwrap func to Any? will always unwrap any type.
To maybe save somebody from cobbling it all together from the answers and comments, here is an answer including both "sane" ways and some what I consider to be improvements for Swift 3 coming with Xcode 8.2.1.
Using Reflection
func unwrap<T>(_ any: T) -> Any
{
let mirror = Mirror(reflecting: any)
guard mirror.displayStyle == .optional, let first = mirror.children.first else {
return any
}
return first.value
}
Discussion
The accepted answer from bubuxu fails to compile with Swift 3.
As walkline suggests in his comment, changing .Optional to .optional fixes this (see SE-0005 and Swift API Design Guidelines).
Reasons I thought this solution can be improved:
I find returning NSNull() weird.
I think the alternative of returning nil with return type Any? is also problematic because it turns everything (including non-optional values) into optional values
(e.g. unwrap(any: 42) returns Optional(42)).
When calling unwrap(any:) with anything but an Any value (any more any anybody?) the Swift 3 compiler warns about implicitly
coercing to Any.
Similiar thoughts apply to Sajjon's answer.
The solution I suggest addresses all those points. Be aware however that unwrap(_:) returns nil as type Any so using the nil
coalescing operator does not work anymore. This means that this just shifts around what I think is problematic about the second point. But I found this to be just the right thing to do for the (to me) more interesting use case regarding reflection.
Using an Extension on Optional
protocol OptionalProtocol {
func isSome() -> Bool
func unwrap() -> Any
}
extension Optional : OptionalProtocol {
func isSome() -> Bool {
switch self {
case .none: return false
case .some: return true
}
}
func unwrap() -> Any {
switch self {
case .none: preconditionFailure("trying to unwrap nil")
case .some(let unwrapped): return unwrapped
}
}
}
func unwrapUsingProtocol<T>(_ any: T) -> Any
{
guard let optional = any as? OptionalProtocol, optional.isSome() else {
return any
}
return optional.unwrap()
}
Discussion
This is bascially LopSae's solution updated to Swift 3. I also changed the precondition failure message and added unwrapUsingProtocol(_:).
Usage
class Person {
var id:Int = 1
var name:String?
}
var person = Person()
person.name = "foo"
let mirror = Mirror(reflecting: person)
for child in mirror.children.filter({ $0.label != nil }) {
print("\(child.label!) = \(unwrap(child.value))")
}
No matter if you're using unwrap() or unwrapUsingProtocol(), this will print
id = 1
name = foo
If you're looking for a way to neatly align the output, see Is there a way to use tabs to evenly space out description strings in Swift?
To check if a Any variable is an optional a protocol can be used as a means of a typeless Optional.
Just as its currently imposible (as of Swift 2) to check against a typeless Optional it is also not posible to cast an into a typeless optional:
let anyType: Any.Type = Optional<String>.self
let anyThing: Any = Optional.Some("string")
anyType is Optional.Type // Causes error
let maybeString = anything as? Optional // Also causes error
// Argument for generic parameter 'Wrapped' could not be inferred
However, the proposed OptionalProtocol can also be used to provide a generic-less interface to access the Optional values and even unwrap them:
protocol OptionalProtocol {
func isSome() -> Bool
func unwrap() -> Any
}
extension Optional : OptionalProtocol {
func isSome() -> Bool {
switch self {
case .None: return false
case .Some: return true
}
}
func unwrap() -> Any {
switch self {
// If a nil is unwrapped it will crash!
case .None: preconditionFailure("nill unwrap")
case .Some(let unwrapped): return unwrapped
}
}
}
// With this we can check if we have an optional
let maybeString: String? = "maybe"
let justString: String = "just"
maybeString is OptionalProtocol // true
justString is OptionalProtocol // false
With the methods provided the optionals can be checked and accessed in quite a natural way, without needing the impossible cast to Optional:
let values:[Any] = [
Optional.Some(12),
2,
Optional<String>.None, // a "wrapped" nil for completeness
Optional.Some("maybe"),
"something"
]
for any in values {
if let optional = any as? OptionalProtocol {
if optional.isSome() {
print(optional.unwrap())
} else {
// nil should not be unwrapped!
print(optional)
}
continue
}
print(any)
}
Which will print:
12
2
nil
maybe
something
Slight alteration on #thm to completely unwrap:
func unwrap<T>(_ any: T) -> Any {
let mirror = Mirror(reflecting: any)
guard mirror.displayStyle == .optional, let first = mirror.children.first else {
return any
}
return unwrap(first.value)
}
I think this is a kind of bug.
In general, to discover and extract the specific type from Any, down casting with as is the only supported method. But :
let int:Int? = 1
let any:Any = int
switch any {
case let val as Optional<Int>: // < [!] cannot downcast from 'Any' to a more optional type 'Optional<Int>'
print(val)
default:
break
}
This means, there is no supported way to do that.
Anyway, apparently you can do that with reflect:
func printArray(values:[Any]) {
for i in 0..<values.count {
var val = values[i]
var ref = reflect(val)
// while `val` is Optional and has `Some` value
while ref.disposition == .Optional && ref.count > 0 && ref[0].0 == "Some" {
// replace `val` with unwrapped value
val = ref[0].1.value;
ref = reflect(val)
}
println("value[\(i)] = \(val)")
}
}
let int:Int? = 1
let str:String? = "foo"
let values:[Any] = [int,2,str,"bar"]
printArray(values)
outputs:
value[0] = 1
value[1] = 2
value[2] = foo
value[3] = bar
ADDED: minor tweaked version
func printArray(values:[Any]) {
for i in 0..<values.count {
var ref = reflect(values[i])
// while `val` is Optional and has `Some` value
while ref.disposition == .Optional && ref.count > 0 && ref[0].0 == "Some" {
// Drill down to the Mirror of unwrapped value
ref = ref[0].1
}
let val = ref.value
println("value[\(i)] = \(val)")
}
}
Factoring out into a function:
func unwrapAny(val:Any) -> Any {
var ref = reflect(val)
while ref.disposition == .Optional && ref.count > 0 && ref[0].0 == "Some" {
ref = ref[0].1
}
return ref.value
}
func printArray(values:[Any]) {
for i in 0..<values.count {
println("value[\(i)] = \(unwrapAny(values[i]))")
}
}
Not a complete answer. It boils down to this:
let int:Int? = 1
let str:String? = "foo"
let values:[Any] = [int,2,str,"bar"]
func printArray(values:[Any]) {
for i in 0..<values.count {
let v = values[i]
if _stdlib_demangleName(_stdlib_getTypeName(v)) == "Swift.Optional" {
println("value[\(i)] = "it's optional: \(v)") // here I'm stuck
}else {
println("value[\(i)] = \(values[i])")
}
}
}
printArray(values)
how about this solution, I made a generic version of previous answer.
fileprivate func unwrap<T>(value: Any)
-> (unwraped:T?, isOriginalType:Bool) {
let mirror = Mirror(reflecting: value)
let isOrgType = mirror.subjectType == Optional<T>.self
if mirror.displayStyle != .optional {
return (value as? T, isOrgType)
}
guard let firstChild = mirror.children.first else {
return (nil, isOrgType)
}
return (firstChild.value as? T, isOrgType)
}
let value: [Int]? = [0]
let value2: [Int]? = nil
let anyValue: Any = value
let anyValue2: Any = value2
let unwrappedResult:([Int]?, Bool)
= unwrap(value: anyValue) // ({[0]}, .1 true)
let unwrappedResult2:([Int]?, Bool)
= unwrap(value: anyValue2) // (nil, .1 true)
let unwrappedResult3:([UInt]?, Bool)
= unwrap(value: anyValue) // (nil, .1 false)
let unwrappedResult4:([NSNumber]?, Bool)
= unwrap(value: anyValue) ({[0]}, .1 false)
The following is code on Playground.
Based on the solution by #bubuxu, one can also:
func unwrap<T: Any>(any: T) -> T? {
let mirror = Mirror(reflecting: any)
guard mirror.displayStyle == .optional else { return any }
guard let child = mirror.children.first else { return nil }
return unwrap(any: child.value) as? T
}
But you need to check against nil using ?? nil when using unwrap, as done in foo
func foo<T>(_ maybeValue: T?) {
if let value: T = unwrap(any: maybeValue) ?? nil {
print(value)
}
}
Still neat though!
(Anyone got a solution for the ?? nil check?)
Without making it too complicated, why not:
let int:Int? = 1
let str:String? = "foo"
let values:[Any?] = [int,2,str,"bar"]
for var i:Int = 0; i < values.count; i++
{
println("\(values[i]!)")
}
This prints:
1
2
foo
bar
According to Using Enumeration case patterns in Swift 2.0
those might be look like this:
let pattern :[Int?] = [nil, 332, 232,nil,55]
for case let number? in pattern {
print(number)
}
Output:
332,
232,
55