tristanhimmelman / ObjectMapper

Simple JSON Object mapping written in Swift
MIT License
9.16k stars 1.03k forks source link

Parse nil value Xcode 10 #1010

Closed AlexZd closed 4 years ago

AlexZd commented 6 years ago

I'm using OM 3.3 and Xcode 10. I found issue during parsing nil value

ImmutableMappable.swift

public func value<T>(_ key: String, nested: Bool? = nil, delimiter: String = ".", file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> T {
        let currentValue = self.currentValue(for: key, nested: nested, delimiter: delimiter)
        guard let value = currentValue as? T else {
            throw MapError(key: key, currentValue: currentValue, reason: "Cannot cast to '\(T.self)'", file: file, function: function, line: line)
        }
        return value
    }

This method returns value event if currentValue = nil, but should throw an error.

public func value<T>(_ value: Any?) throws -> T {
    let currentValue = value
    guard let value = currentValue as? T else {
        throw MachError(.aborted)
    }
    return value
}

class X {
    var a: Int!
}
let xObj = X()
do {
    xObj.a = try value(nil)
} catch {
    print("error \(error)")
}

You can try this code in playground, you will see that it will not go inside catch block. Replace var a: Int! to var a: Int = 0 and now it will go to catch.

gcharita commented 6 years ago

This is probably a Swift issue.

If we change the value() function to:

public func value<T>(_ value: Any?) throws -> T {
    print(T.self as? Optional<Int>.Type) // Cast type as Int?
    guard let value: T = value as? T else {
        throw MachError(.aborted)
    }
    return value
}

in the debugger we will see this:

Optional(Swift.Optional<Swift.Int>)

Which means that the cast succeed.

But if we change the function to:

public func value<T>(_ value: Any?) throws -> T {
    print(T.self as? Int.Type) // Cast type as Int
    guard let value: T = value as? T else {
        throw MachError(.aborted)
    }
    return value
}

in the debugger we will see nil. So, the T type is not Int but Int? (since IUO are Optionals in swift 4.1+)

Probably the "optionality" of a Type is inside the actual type (T in this case) in swift 4.2 But this behaviour defeats the purpose of guard let statement.

This is interesting.