You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hi! I encountered an error while using the Yams.dump(object:) function because the represent(:) tries to cast an Any value to NodeRepresentable. And it seems to be a Swift problem.
Problem exposition
The error comes from the deserialisation of a Data object to Any. Here is the code that demonstrates that (and the attached playground).
import Foundation
protocolStubProtocol{}extensionArray:StubProtocol{}letducks=["Riri","Fifi","Loulou"]letdata=tryJSONEncoder().encode(ducks)letdeserializedDucks=tryJSONSerialization.jsonObject(with: data)letcastedDeserializedDucks= deserializedDucks as![String]print("StubProtocol conformance of 'ducks': \(ducks is StubProtocol)") // true
print("StubProtocol conformance of 'deserializedDucks': \(deserializedDucks is StubProtocol)") // false
print("StubProtocol conformance of 'castedDeserializedDucks': \(castedDeserializedDucks is StubProtocol)") // true
Only when casting the value from Any to [String] the cast to the protocol will work. The same goes for the cast in the represent(:) function:
if let representable = value as?NodeRepresentable
The casting would work only if the Any value is properly casted. But then it will fail with this value children since the call is recursive (e.g. with an array or a dictionary).
Solution
Recursive cast
A solution that the developer can implement is a recursive cast of the Any value to NodeRepresentable. For instance, here is an implementation in Scout.
func castToNodeRepresentable(_ value:Any)->NodeRepresentable{
if var value = value as?[String:Any]{
value.forEach{value[$0.key]=castToNodeRepresentable($0.value)}return value
}else if let value = value as?[Any]{return value.map(castToNodeRepresentable)}else if let value = value as?Int{return value asNodeRepresentable}else if let value = value as?Double{return value asNodeRepresentable}else if let value = value as?Date{return value asNodeRepresentable}else if let value = value as?Bool{return value asNodeRepresentable}else{returnString(describing: value)asNodeRepresentable}}
This does work, especially as the serialisation handles a limited set of types.
But then it seems strange to have to cast an Any value to a NodeRepresentable if the function dump(object:) expects Any.
One solution could be for the library to perform this recursive cast on behalf of the developer. Thus making sure the provided Any value conforms to the NodeRepresentable protocol. But this approach has some weaknesses:
performing this operation can take some time on large chunks of data
it does not allow customisation of the process. For example the developer could prefer to throw an error rather than fall back on a String.
Requiring a NodeRepresentable
Another solution could be to change the dump(object:) parameter type from Any? to NodeRepresentable
This would make the developer aware of the requirement rather than hiding it. Thus making them responsible for conforming Any to this protocol, while also allowing customisation.
Requiring a NodeRepresentable prevents a runtime error that can be misleading. After all, if the represent(:) function has to work with a NodeRepresentable object, why wouldn't it ensure it rather than handling an Any value? In fact, I first thought that the problem came from the YAMS library since I was passing it an Any value and there were no requirements on this value. Then I realised the problem came from the deserialisation process of Foundation.
Please let me know if this solution seems valid. If so, I would open a pull request with the proposed modifications.
The text was updated successfully, but these errors were encountered:
ABridoux
changed the title
Yams.dump function Any object parameter casted as NodeRepresentable fails
Yams.dump function Any object parameter casted as NodeRepresentable fails
Jan 24, 2021
Thank you! Someone answered to my issue. That's not a bug in fact.
The thing is, with deserialisation, the Any value is a NSArray and not an Array. Which is why I think it will not conform to NodeRepresentable since the conformance is only declared on the Array type in the library.
A quick fix would be to also add the conformance of NSArray to NodeRepresentable but I would say it's clearer to require a NodeRepresentable value in the function as mentioned.
Hi! I encountered an error while using the
Yams.dump(object:)
function because therepresent(:)
tries to cast anAny
value toNodeRepresentable
. And it seems to be a Swift problem.Problem exposition
The error comes from the deserialisation of a
Data
object toAny
. Here is the code that demonstrates that (and the attached playground).Only when casting the value from
Any
to[String]
the cast to the protocol will work. The same goes for the cast in therepresent(:)
function:The casting would work only if the
Any
value is properly casted. But then it will fail with this value children since the call is recursive (e.g. with an array or a dictionary).Solution
Recursive cast
A solution that the developer can implement is a recursive cast of the
Any
value toNodeRepresentable
. For instance, here is an implementation in Scout.This does work, especially as the serialisation handles a limited set of types.
But then it seems strange to have to cast an
Any
value to aNodeRepresentable
if the functiondump(object:)
expectsAny
.One solution could be for the library to perform this recursive cast on behalf of the developer. Thus making sure the provided
Any
value conforms to theNodeRepresentable
protocol. But this approach has some weaknesses:String
.Requiring a
NodeRepresentable
Another solution could be to change the
dump(object:)
parameter type fromAny?
toNodeRepresentable
Any
to this protocol, while also allowing customisation.NodeRepresentable
prevents a runtime error that can be misleading. After all, if therepresent(:)
function has to work with aNodeRepresentable
object, why wouldn't it ensure it rather than handling anAny
value? In fact, I first thought that the problem came from the YAMS library since I was passing it anAny
value and there were no requirements on this value. Then I realised the problem came from the deserialisation process of Foundation.Please let me know if this solution seems valid. If so, I would open a pull request with the proposed modifications.
The text was updated successfully, but these errors were encountered: