I have two almost exactly identical assertions for a generic method on Swift's Dictionary structure, but one succeeds and the other fails. I assume that it's how XCTAssert works, but can't understand why. Does anyone have an idea why?
If the method is not generic and, for example, T is String, then both tests succeed.
extension Dictionary {
func safeElement<T>(key: Key, fallback: T) -> T {
if let value = self[key] as? T {
return value
}
return fallback
}
}
class DictionaryTests: XCTestCase {
let dict = ["foo": "bar"]
func testSafeElement() {
// This succeeds
let bar = dict.safeElement("foo", fallback: "")
XCTAssertEqual(bar, "bar")
// This fails
XCTAssertEqual(dict.safeElement("foo", fallback: ""), "bar")
}
}
Update
I was tinkering with it a little bit more, and it turns out that if you pass a type as a parameter, then both cases succeed. But I imagine this type verbosity is not that one would want.
extension Dictionary {
func safeElement<T>(key: Key, fallback: T, type: T.Type) -> T {
if let value = self[key] as? T {
return value
}
return fallback
}
}
class DictionaryTests: XCTestCase {
let dict = ["foo": "bar"]
func testSafeElement() {
// This succeeds
let bar = dict.safeElement("foo", fallback: "", type: String.self)
XCTAssertEqual(bar, "bar")
// This also succeeds
XCTAssertEqual(dict.safeElement("foo", fallback: "", type: String.self), "bar")
}
}
If you add a print statement:
func safeElement<T>(key: Key, fallback: T) -> T {
print("calling for \(T.self)")
You can see the difference in output between the two tests:
calling for String
calling for Optional<String>
This is probably because XCTAssertEqual's argument is declared as #autoclosure expression1: () -> T?, so the compiler tries to pick a version of safeElement that returns an optional, which it can do easily by making T==String?. But then your as? T does the wrong thing, since the dictionary's Value type is a non-optional String.
Sounds like a bug.
Related
I have a struct with several properties. How can I write a function that takes a dictionary of type [String: Any] and creates another dictionary of type [String: Any] that contains only keys and values where the input dictionary's value was different from the struct property value with the same name?
A struct:
struct MyStruct {
var a: Bool = false
var b: String = "random"
var c: Int = 2
}
Desired function call:
let myStruct = MyStruct()
let input: [String: Any] = ["b": "test", "c": 2]
let result: [String: Any] = myStruct.getDiff(input)
Desired result for the example input:
result = ["b": "test"]
Besides a struct, how would this be done for comparing the [String: Any] to a class?
The specific syntax you've provided is probably not possible. Packing things into a [String: Any] likely loses too much information to be recovered. But your overall goal is certainly possible.
The important piece is input. Rather than [String: Any], we're going to use an explicit type, ValueChange:
let input = [
ValueChange(key: "b", changedTo: "test"),
ValueChange(key: "c", changedTo: 2),
]
Creating a new type like this allows us to capture all the types, and enforce certain rules, particularly that the values are Equatable:
struct ValueChange {
init<Value: Equatable>(key: String, changedTo newValue: Value) {...}
}
I'll come back to ValueChange in a moment, but first I want to go back to how it'll be used. Since you want a .getDiff(...) syntax, it'll be best to create an extension using a protocol:
protocol DictionaryDiffComputing {}
extension DictionaryDiffComputing {
func getDiff(_ changes: [ValueChange]) -> [String: Any] {
Dictionary(uniqueKeysWithValues:
changes.compactMap { $0.changedValue(self) })
}
}
The protocol has no requirements. It just exists to say "these types have the getDiff method." This method needs ValueChange to provide us a (String, Any) tuple if the value has changed.
This is where the problem gets interesting, I'll just show the answer and then discuss it.
struct ValueChange {
let changedValue: (_ object: Any) -> (String, Any)? // (key, newValue)
init<Value: Equatable>(key: String, changedTo newValue: Value) {
self.changedValue = { object in
// Get the old value as an Any using Mirror
guard let oldAnyValue: Any = Mirror(reflecting: object)
.children
.first(where: { $0.label == key })?
.value
else {
assertionFailure("Unknown key: \(key)")
return nil
}
// Make sure it's the correct type
guard let oldValue = oldAnyValue as? Value else {
assertionFailure("Bad type for values (\(oldAnyValue)). Expected: \(Value.self)")
return nil
}
// Compare the values
return newValue != oldValue ? (key, newValue) : nil
}
}
}
This uses Mirror to pull out the old value to compare as an Any type, then it converts it to the correct Value type. This is the power of the generic init. Since we know the type a compile time, we can capture it inside this closure, erasing that type from the outside world, but being able to work with it at runtime.
extension MyStruct: DictionaryDiffComputing {}
let myStruct = MyStruct()
myStruct.getDiff(input) // ["b": "test"]
What I really don't like about this answer is that it's very unsafe. Note the two calls to assertionFailure. There is nothing about ValueChange that ensures that the key exists or that the value is the correct type. If you change the name or type a property, your program will either crash or behave incorrectly, and there's nothing the compiler can do to help you.
You can make this a lot more type-safe and the code much simpler at the cost of a slightly more verbose calling syntax:
protocol DictionaryDiffComputing {}
struct ValueChange<Root> {
let changedValue: (_ object: Root) -> (String, Any)? // (key, newValue)
init<Value: Equatable>(key: String, keyPath: KeyPath<Root, Value>, changedTo newValue: Value) {
self.changedValue = { newValue != $0[keyPath: keyPath] ? (key, newValue) : nil }
}
}
extension DictionaryDiffComputing {
func getDiff(_ changes: [ValueChange<Self>]) -> [String: Any] {
Dictionary(uniqueKeysWithValues:
changes.compactMap { $0.changedValue(self) })
}
}
let myStruct = MyStruct()
let input: [ValueChange<MyStruct>] = [
ValueChange(key: "b", keyPath: \.b, changedTo: "test"),
ValueChange(key: "c", keyPath: \.c, changedTo: 2),
]
myStruct.getDiff(input)
If you use this approach, you know that the property exists on this type, and that the value is the correct type for that property. You also get some extra power, since you can use any key path you like starting at this root type. That means you can do things like:
ValueChange(key: "b_length", keyPath: \.b.count, changedTo: 4),
You could cleanup the requirement for key in ValueChange by adding some mapping dictionary of key path to key name (a static var protocol requirement for example), but I don't know of a way to generate this automatically, and I don't know any good way to convert a key path into an appropriate string.
I'm trying to write a function to unwrap optionals with an arbitrary number of levels of nesting. Here's the test I'm using:
let a: Int??? = 1
let b: Int??? = nil
print(a.unwrap(0), b.unwrap(0)) // should print 1, 0
I can get the correct output with a basic generic function:
extension Optional {
func unwrap<T> (_ defaultValue: T) -> T {
return (self as? T) ?? defaultValue
}
}
print(a.unwrap(0), b.unwrap(0)) // 1, 0
But this doesn't prevent the function from being called with a different type than the optional. For instance, I could call a.unwrap("foo") and it would print "foo" instead of "1" since of course you can't cast Int??? to String.
I tried it using Wrapped instead, which semi-properly restricts the default value but doesn't give the correct output:
extension Optional {
func unwrap (_ defaultValue: Wrapped) -> Wrapped {
return (self as? Wrapped) ?? defaultValue
}
}
print(a.unwrap(0), b.unwrap(0)) // Optional(Optional(1)), nil
It only unwraps one level of the optional, instead of all three, and since nil is a valid value for Int?? it doesn't return the default.
Is there any way to safely do what I want here?
This code does what you ask for. The drawback is that you need to implement the Unwrappable protocol in every type you want to put inside optionals. Maybe Sourcery can help with that.
protocol Unwrappable {
associatedtype T
func unwrap(_ default: T) -> T
}
extension Optional {}
extension Optional: Unwrappable where Wrapped: Unwrappable {
typealias T = Wrapped.T
func unwrap(_ defaultValue: T) -> T {
if let value = self {
return value.unwrap(defaultValue)
}
return defaultValue
}
}
extension Int: Unwrappable {
typealias T = Int
func unwrap(_ default: Int) -> Int {
return self
}
}
let nestedOptionalValue: Int??? = 6
let nestedOptionalNil: Int??? = nil
let optionalValue: Int? = 6
let optionalNil: Int? = nil
print(nestedOptionalValue.unwrap(0)) // prints 6
print(nestedOptionalNil.unwrap(0)) // prints 0
print(optionalValue.unwrap(0)) // prints 6
print(optionalNil.unwrap(0)) // prints 0
The trick is that the Unwrappable protocol marks types that can be eventually unwrapped (as in after 0, 1 or more unwraps) to a certain type.
The difficulty in this problem comes from the fact that in an extension to Optional, you can get the Wrapped type, but if Wrapped is optional again, you can't access Wrapped.Wrapped (in other words, Swift doesn't support Higher Kinded Types).
Another approach, would be to try to add an extension to Optional where Wrapped == Optional. But again you'd need Higher Kinded Types support to access the Wrapped.Wrapped type. If we try to extend Optional where Wrapped == Optional<T>, we also fail, because we cant extend Optional generically over T.
Using Xcode 10, but did not migrate to Swift 4.2, so my project is still running with Swift 4.1.
Lets assume i have the following extension on Dictionary:
extension Dictionary where Key: ExpressibleByStringLiteral {
func find<T>(key: Key) -> T? {
return self[key] as? T
}
}
I use this function to access values in a hashmap in a type safe manner like:
let dict: [String: Any] = ["foo": "bar"]
let foo: String? = dict.find(key: "foo") // prints "bar"
My problem surfaces, when i would like to have Any type returned from my find function, like:
let bar: Any? = dict.find(key: "bar")
Pre Xcode 10, this function used to return to me plain and simple nil if the key was not found in the hashmap.
However, post Xcode 10, it returns Optional.some(nil).
I understand, that Any types can be initialised like the following:
let foo: Any = Optional<String>.none
I guess in my case something similar happens. Does anybody has an idea, how to work around it and still return nil from the find function?
This is due to an intentional change (#13910) where the compiler is now more conservative with unwrapping an optional value that's being cast to a generic placeholder type. Now, the results you get are more consistent with those that you would get in a non-generic context (see SR-8704 for further discussion).
For example:
// note the constraint `where Key : ExpressibleByStringLiteral` is needlessly restrictive.
extension Dictionary where Key : ExpressibleByStringLiteral {
func find<T>(key: Key) -> T? {
return self[key] as? T
}
}
let dict: [String: Any] = ["foo": "bar"]
let genericBar: Any? = dict.find(key: "bar")
print(genericBar as Any) // in Swift 4.1: nil, in Swift 4.2: Optional(nil)
// `T` in the above example is inferred to be `Any`.
// Let's therefore substitute `as? T` with `as? Any`.
let nonGenericBar = dict["bar"] as? Any
print(nonGenericBar as Any) // in both versions: Optional(nil)
As you can see, you now get Optional(nil) regardless of whether a generic placeholder was used in order to perform the cast, making the behaviour more consistent.
The reason why you end up with Optional(nil) is because you're performing a conditionally casting an optional value. The conditional cast on its own results in an optional value in order to indicate success or failure, and putting the resulting optional value in the success case gives you a doubly wrapped optional. And because you're casting to Any, which can represent an Optional value, no unwrapping needs to be done in order to "fit" the value in the resultant type.
If you wish to flatten the resulting optional into a singly wrapped optional, you can either coalesce nil:
extension Dictionary {
func find<T>(key: Key) -> T? {
return (self[key] as? T?) ?? nil
}
}
Or unwrap the value before casting, for example using a guard:
extension Dictionary {
func find<T>(key: Key) -> T? {
guard let value = self[key] else { return nil }
return value as? T
}
}
or, my preferred approach, using flatMap(_:):
extension Dictionary {
func find<T>(key: Key) -> T? {
return self[key].flatMap { $0 as? T }
}
}
That all being said, I often find the usage of [String: Any] to be a code smell, strongly indicating that a stronger type should be used instead. Unless the keys can really be arbitrary strings (rather than a fixed set of statically known keys), and the values can really be of any type for a particular key – there's almost certainly a better type you can be using to model your data with.
In C#, it's possible to call a generic method by specifying the type:
public T f<T>()
{
return something as T
}
var x = f<string>()
Swift doesn't allow you to specialize a generic method when calling it. The compiler wants to rely on type inference, so this is not possible:
func f<T>() -> T? {
return something as T?
}
let x = f<String>() // not allowed in Swift
What I need is a way to pass a type to a function and that function returning an object of that type, using generics
This works, but it's not a good fit for what I want to do:
let x = f() as String?
EDIT (CLARIFICATION)
I've probably not been very clear about what the question actually is, it's all about a simpler syntax for calling a function that returns a given type (any type).
As a simple example, let's say you have an array of Any and you create a function that returns the first element of a given type:
// returns the first element in the array of that type
func findFirst<T>(array: [Any]) -> T? {
return array.filter() { $0 is T }.first as? T
}
You can call this function like this:
let array = [something,something,something,...]
let x = findFirst(array) as String?
That's pretty simple, but what if the type returned is some protocol with a method and you want to call the method on the returned object:
(findFirst(array) as MyProtocol?)?.SomeMethodInMyProtocol()
(findFirst(array) as OtherProtocol?)?.SomeMethodInOtherProtocol()
That syntax is just awkward. In C# (which is just as strongly typed as Swift), you can do this:
findFirst<MyProtocol>(array).SomeMethodInMyProtocol();
Sadly, that's not possible in Swift.
So the question is: is there a way to accomplish this with a cleaner (less awkward) syntax.
Unfortunately, you cannot explicitly define the type of a generic function (by using the <...> syntax on it). However, you can provide a generic metatype (T.Type) as an argument to the function in order to allow Swift to infer the generic type of the function, as Roman has said.
For your specific example, you'll want your function to look something like this:
func findFirst<T>(in array: [Any], ofType _: T.Type) -> T? {
return array.lazy.compactMap { $0 as? T }.first
}
Here we're using compactMap(_:) in order to get a sequence of elements that were successfully cast to T, and then first to get the first element of that sequence. We're also using lazy so that we can stop evaluating elements after finding the first.
Example usage:
protocol SomeProtocol {
func doSomething()
}
protocol AnotherProtocol {
func somethingElse()
}
extension String : SomeProtocol {
func doSomething() {
print("success:", self)
}
}
let a: [Any] = [5, "str", 6.7]
// Outputs "success: str", as the second element is castable to SomeProtocol.
findFirst(in: a, ofType: SomeProtocol.self)?.doSomething()
// Doesn't output anything, as none of the elements conform to AnotherProtocol.
findFirst(in: a, ofType: AnotherProtocol.self)?.somethingElse()
Note that you have to use .self in order to refer to the metatype of a specific type (in this case, SomeProtocol). Perhaps not a slick as the syntax you were aiming for, but I think it's about as good as you're going to get.
Although it's worth noting in this case that the function would be better placed in an extension of Sequence:
extension Sequence {
func first<T>(ofType _: T.Type) -> T? {
// Unfortunately we can't easily use lazy.compactMap { $0 as? T }.first
// here, as LazyMapSequence doesn't have a 'first' property (we'd have to
// get the iterator and call next(), but at that point we might as well
// do a for loop)
for element in self {
if let element = element as? T {
return element
}
}
return nil
}
}
let a: [Any] = [5, "str", 6.7]
print(a.first(ofType: String.self) as Any) // Optional("str")
What you probably need to do is create a protocol that looks something like this:
protocol SomeProtocol {
init()
func someProtocolMethod()
}
And then add T.Type as a parameter in your method:
func f<T: SomeProtocol>(t: T.Type) -> T {
return T()
}
Then assuming you have a type that conforms to SomeProtocol like this:
struct MyType: SomeProtocol {
init() { }
func someProtocolMethod() { }
}
You can then call your function like this:
f(MyType.self).someProtocolMethod()
Like others have noted, this seems like a convoluted way to do what you want. If you know the type, for example, you could just write:
MyType().someProtocolMethod()
There is no need for f.
I'm using "JSON to my own class" and I have real headache with type conversions. Typical conversion is looks like:
I need: "55" (JSON) -> 55 (Int)
My path: AnyObject? -> String -> Int
Swift isn't converting AnyObject("55") to Int("55") directly, Swift thinks that "55" is always String. So I've written helper method:
internal func jsonDirectToInt(from: AnyObject?) -> Int? {
guard let string = from as? String,
let int = Int(string) else { return nil }
return int
}
My question: can I pass class type to function as argument, so I can write something like that:
internal func jsonDirectToInt(from: AnyObject?, type: <T>) -> Int? {
guard let string = from as? String,
let int = <T>(string) else { return nil }
return int
}
Possibly I'm misunderstanding what you want to achieve, but it sounds from your question and the third code block that follows that you want a "jsonDirectToAnything" generic function; attempting to convert the JSON from parameter to the type passed via type parameter, using a JSON (AnyObject) -> String -> SomeType pipeline. In your code example above (last code block) your return type is set to Int even when showing that you want some generic <T>(string) conversion (which is assigned to return type), so I will assume that using return type Int is not what you're after, but rather using a generic return type (if not; the question don't really make much sense for me).
Anyway, for the technical discussion: you can create such a function with the generic return type constrained to types that conform to a protocol---say StringInitializable---that includes a blueprint for an (failable) initializer by String instances. You extend the types you want to be able to use the "generic" jsonDirect method to StringInitializable, and, if needed, implement the String initializer blueprinted by the protocol. In the example below, I've blueprinted initializer init?(_ text: String) in StringInitializable. This failable initializer is readily natively available for e.g. Double and String types, but needs to be implemented (just as a wrapper) for e.g. extending Int to the protocol.
Finally note, before we proceed, that there exist several existing tools for handling conversion of JSON data to native Swift types, e.g.
SwiftyJSON
Example solution for your specific question:
/* Protocol with blueprint for failable initializer by String */
protocol StringInitializable {
init?(_ text: String)
}
/* Extend type types you want to use "generically" in 'jsonDirect' method */
extension Double : StringInitializable { } // already has a 'init?(_ text: String)', OK
extension String : StringInitializable { } // already has a 'init?(_ text: String)', OK
extension Int : StringInitializable { // point to 'init(_:radix:)' initializer
init?(_ text: String) {
guard let foo = Int.init(text, radix: 10) else {
return nil
}
self = foo
}
}
/* Your own class */
class MyClass: StringInitializable {
let foo : Int?
required init?(_ text: String) {
foo = Int(text)
}
}
/* jsonDirect for (attempted) type conversion from AnyObject to
generic type, where the latter is constrained to types conforming
to protocol 'StringInitializable' */
func jsonDirect<T: StringInitializable>(from: AnyObject?, toType _ : T.Type) -> T? {
guard let foo = from as? String, let bar = T(foo) else {
return nil
}
return bar
}
Example usage for JSON conversion into Int, Double and String as well as a the custom class MyClass:
/* Example usage */
var myJSONInt : AnyObject = "55"
var myJSONInvalidInt : AnyObject = "foo"
var myJSONDouble : AnyObject = "55.3"
var myJSONString : AnyObject = "Foo"
/* Attempt json -> specified type conversions */
let fooInt = jsonDirect(myJSONInt, toType: Int.self)
let fooMyClass = jsonDirect(myJSONInt, toType: MyClass.self)
let fooInvalidInt = jsonDirect(myJSONInvalidInt, toType: Int.self)
let fooDouble = jsonDirect(myJSONDouble, toType: Double.self)
let fooIntString = jsonDirect(myJSONInt, toType: String.self)
/* Results */
print(fooInt.dynamicType, ": value =", fooInt ?? "nil")
// Optional<Int> : value = 55
print(fooMyClass.dynamicType, ": value =", fooMyClass?.foo ?? "nil")
// Optional<MyClass> : value = 55
print(fooInvalidInt.dynamicType, ": value =", fooInvalidInt ?? "nil")
// Optional<Int> : value = nil
print(fooDouble.dynamicType, ": value =", fooDouble ?? "nil")
// Optional<Double> : value = 55.3
print(fooIntString.dynamicType, ": value =", fooIntString ?? "nil")
// Optional<String> : value = 55