Casting to generic optional in Swift - swift

I'm fiddling around with generics in Swift and hit something I can't figure out: If I cast a value into the type of a generic parameter, the cast is not performed. If I try the same with static types, it works.
class SomeClass<T> {
init?() {
if let _ = 4 as? T {
println("should work")
} else {
return nil
}
}
}
if let _ = SomeClass<Int?>() {
println("not called")
}
if let _ = 4 as? Int? {
println("works")
}
Can anybody explain this behavior? Shouldn't be both cases equivalent?
Update
The above example is simplified to the max. The following example illustrates the need for a cast a little better
class SomeClass<T> {
init?(v: [String: AnyObject]) {
if let _ = v["k"] as? T? {
print("should work")
} else {
print("does not")
return nil
}
}
}
if let _ = SomeClass<Int?>(v: ["k": 4]) {
print("not called")
}
if let _ = SomeClass<Int>(v: ["k": 4]) {
print("called")
}
2nd Update
After #matt made me learn about AnyObject and Any and #Darko pointed out in his comments how dictionaries make my example too complicated, here's my next refinement
class SomeClass<T> {
private var value: T!
init?<U>(param: U) {
if let casted = param as? T {
value = casted
} else {
return nil
}
}
}
if let _ = SomeClass<Int?>(param: Int(4)) {
println("not called")
}
if let _ = SomeClass<Int>(param: Int(4)) {
println("called")
}
if let _ = Int(4) as? Int? {
println("works")
}
if let _ = (Int(4) as Any) as? Int? {
println("Cannot downcast from Any to a more optional type 'Int?'")
}
I tried using init?(param: Any) before, but that yields the same problem illustrated in the last if which is discussed elsewhere.
So all it comes down to: Has anyone really been far as to ever cast anything to a generic optional type? In any case? I'm happy to accept any working example.

This is really not about generics at all; it's about AnyObject (and how casting works). Consider:
let d = ["k":1]
let ok = d["k"] is Int?
print (ok) // true
// but:
let d2 = d as [String:AnyObject]
let ok2 = d2["k"] is Int?
print (ok2) // false, though "is Int" succeeds
Since your initializer casts the dictionary up to [String:AnyObject] you are in this same boat.

This works as expected now - in Playgrounds as well as in projects. Retested in Swift 5.4:
class SomeClass<T> {
private var value: T
init?<U>(param: U) {
if let casted = param as? T {
value = casted
} else {
return nil
}
}
}
if let _ = SomeClass<Int?>(param: Int(4)) {
print("called")
}
if let _ = SomeClass<Int>(param: Int(4)) {
print("called")
}
which prints called two times as expected.

As far I can see the goal of your updated code is to find out if the passed parameter (the value of the dict) is of type T. So you are mis-using the as? cast to check the type. What you actually want is the "is" operator.
class SomeClass<T> {
init?(v: [String: AnyObject]) {
if v["k"] is T {
print("called if AnyObject is of type T")
} else {
print("called if AnyObject is not of type T")
return nil
}
}
}
if let _ = SomeClass<Int>(v: ["k": 4]) {
print("called")
}
if let _ = SomeClass<Int?>(v: ["k": 4]) {
print("not called")
}

Related

How to render resursively with SwiftUI?

I got this long error:
Function opaque return type was inferred as '_ConditionalContent<_ConditionalContent<some View, VStack<ForEach<[Dictionary<String, JSON>.Keys.Element], Dictionary<String, JSON>.Keys.Element, TupleView<(some View, some View)>?>>>, some View>' (aka '_ConditionalContent<_ConditionalContent<some View, VStack<ForEach<Array<String>, String, Optional<TupleView<(some View, some View)>>>>>, some View>'), which defines the opaque type in terms of itself
I think the issue is the "resursive" character of the rendering as the error log itself says also:
which defines the opaque type in terms of itself
If I replace in the second if-else-statement, as in comment you see, and return only a text, then I got no error.
#ViewBuilder
func showNode(json: JSON) -> some View {
if let v = json.get() as? String {
Text("\(v)").padding()
} else if let d = json.get() as? [String: JSON] {
VStack {
ForEach(d.keys.sorted(), id: \.self) { k in
if let v = d[k] {
Text("\(k)").padding()
showNode(json: v)
}
}
}
// Text("").padding()
} else {
Text("").padding()
}
}
Method is a recursive method, which should render a json tree.
And the JSON
public enum JSON {
case string(String)
case number(Float)
case object([String:JSON])
case array([JSON])
case bool(Bool)
func get() -> Any {
switch self {
case .string(let v):
return v
case .number(let v):
return v
case .object(let v):
return v
case .array(let v):
return v
case .bool(let v):
return v
}
}
}
And all I do render it on screen:
struct ContentView: View {
var data: JSON? = nil
init() {
updateValue()
return
}
mutating func updateValue() {
do {
if let jsonURL = Bundle.main.url(forResource: "user", withExtension: "json") {
let jsonData = try Data(contentsOf: jsonURL)
guard let d2 = try JSONSerialization.jsonObject(with: jsonData, options: .mutableLeaves) as? JSON else {
print("Can not convert to d2")
return
}
data = d2
}
} catch {
}
}
var body: some View {
VStack {
if let data = self.data {
showNode(json: data)
}
}
}
some View is just a language feature that allows you to not write out the whole return type of the method.
If you actually try to write out the whole return type of the method by meticulously following all the language rules, you will find that you run into a problem. Because you need to know what the return type of showNode is, in order to know the return type of showNode! This is partly due to its recursive nature, and partly due to #ViewBuilder.
If you are wondering what on earth is _ConditionalContent, those come from buildEither, which is what your if statements translate into when put inside a #ViewBuilder. TupleView comes from buildBlock, and all their type parameters are determined by the types of the expressions you put inside, one of them being the showNode call, whose type we are in the middle of figuring out.
You can fix this by either using AnyView, the type-erased view:
func showNode(json: JSON, depth: Int = 1) -> AnyView {
if let v = json.get() as? String {
return AnyView(Text("\(v)").padding())
} else if let d = json.get() as? [String: JSON] {
return AnyView(VStack {
ForEach(d.keys.sorted(), id: \.self) { k in
if let v = d[k] {
Text("\(k)").padding()
showNode(json: v)
}
}
})
} else {
return AnyView(Text("").padding())
}
}
Or make a new View type to stop the infinite recursion:
struct NodeView: View {
let json: JSON
var body: some View {
if let v = json.get() as? String {
Text("\(v)").padding()
} else if let d = json.get() as? [String: JSON] {
VStack {
ForEach(d.keys.sorted(), id: \.self) { k in
if let v = d[k] {
Text("\(k)").padding()
NodeView(json: v)
}
}
}
} else {
Text("").padding()
}
}
}
A few additional notes:
The way that you are parsing the JSON right now is incorrect. JSONSerialization won't give you your own JSON type. You should probably use a custom Codable implementation instead, but exactly how to do that belongs to another question.
The view that this code draws doesn't actually show the "levels" of the JSON. Not sure if that is intended or not
if let v = json.get() as? String { doesn't handle Bools or Floats or arrays. If you want to handle those, you need to write checks for them as well.

Testing for compliance with and casting to RawRepresentable protocol

I have some generic code that allows me to read and write various types to the defaults system, e.g. value getters and setters:
var value : T {
get {
if T.self == Int.self {
return UserDefaults.standard.integer(forKey: storageKey) as! T
} else if T.self == Double.self {
return UserDefaults.standard.double(forKey: storageKey) as! T
} else if T.self == Float.self {
return UserDefaults.standard.float(forKey: storageKey) as! T
} else if T.self == Bool.self {
return UserDefaults.standard.bool(forKey: storageKey) as! T
} else if T.self == String.self {
return UserDefaults.standard.string(forKey: storageKey) as! T
} else {
return UserDefaults.standard.value(forKey: self.storageKey) as! T
}
}
set(value) {
UserDefaults.standard.set(value, forKey: storageKey)
UserDefaults.standard.synchronize()
}
}
Now I want to add my own enum types to this mechanism by making them RawRepresentable<Int>, e.g.
enum Direction : Int, RawRepresentable {
case left = 0
case right = 1
}
Unfortunately, I can neither find the magic incantation to test whether T conforms to the RawRepresentable protocol, nor can I cast T to the RawRepresentable protocol, because no matter what I try, I always end up with a Protocol 'RawRepresentable' can only be used as a generic constraint because it has Self or associated type requirements.
I have tried every where and as incantation until I have started doubting that it can be done at all!?
I'm in Swift 5 and the goal is to create new instance by invoking CustomType(rawValue:) and getting the Int value by calling myValue.rawValue.
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 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 produce a nil of a generic type

I would like to declare a generic class which holds/obtains a variable of type Any?, but casts this variable to a given type, when requested. Something like this
class A<T> {
var o: NSObject!
var k: String!
var v: T {
get { return o.value(forKeyPath: k) as! T }
set { o.setValue(newValue, forKeyPath: k) }
}
}
I would like to this to work so that when o.value(forKeyPath: k) is nil and T can hold a nil (it is ExpressibleByNilLiteral), v returns nil (of type T). As it is, it crashes because of the as!. Is it possible to do this?
An attempted crack at this looks like this (suggested by
How to make a computed property of a generic class depend on the class constraints)
class A<T> {
var o: NSObject!
var k: String!
var v: T {
get {
let x = o.value(forKeyPath: k)
if let E = T.self as? ExpressibleByNilLiteral.Type {
if let x = x { return x as! T }
let n: T = <nil of type T> <---- not sure what to put here
return n
} else {
return x as! T
}
}
set { o.setValue(newValue, forKeyPath: k) }
}
}
but I am not sure how to make it work.
Not sure why, but this actually works
func cast<T>(_ v: Any?)->T {
if let E = T.self as? ExpressibleByNilLiteral.Type {
if let v = v {
return v as! T
} else {
return E.init(nilLiteral: ()) as! T
}
} else {
return v as! T
}
}
I thought I already tried this and it did not work... Anyway, now we can substitute all as! calls with cast when we do not want to crash on nil, if the type we are casting to can take it, e.g. in the getter of my question.

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