How to unwrap an optional value from Any type?

Swift

Swift Problem Overview


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

Swift Solutions


Solution 1 - Swift

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.

Solution 2 - Swift

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?

Solution 3 - 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

Solution 4 - Swift

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

Solution 5 - Swift

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]))")
    }
}

Solution 6 - Swift

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)

Solution 7 - Swift

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.

enter image description here

Solution 8 - Swift

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?)

Solution 9 - Swift

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

Solution 10 - Swift

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmythzView Question on Stackoverflow
Solution 1 - SwiftbubuxuView Answer on Stackoverflow
Solution 2 - SwiftthmView Answer on Stackoverflow
Solution 3 - SwiftMaic López SáenzView Answer on Stackoverflow
Solution 4 - SwiftChris PrinceView Answer on Stackoverflow
Solution 5 - SwiftrintaroView Answer on Stackoverflow
Solution 6 - Swiftqwerty_soView Answer on Stackoverflow
Solution 7 - SwiftKeithView Answer on Stackoverflow
Solution 8 - SwiftSajjonView Answer on Stackoverflow
Solution 9 - SwiftjwlaughtonView Answer on Stackoverflow
Solution 10 - SwiftNarendra GView Answer on Stackoverflow