jechris
(JC)
1
So I have a simple function getObject(_: String) -> T?.
Testing it with XCTAssertNil(_: Any?) used to work before but is now broken in Swift 4.2.
XCTAssertNil(getObject(forKey: .userCacheKey)) // true in Swift 4.1, false in Swift 4.2
My understanding is that T is inferred as Any?. I'm aware there's been changes on casting with optional and generics. Yet I would have expected it to be inferred as Any, just like it did in Swift 4.1
Is it really intended behaviour of the changes made in Swift 4.2?
Only way I found to fix the issue is using casting which does not satisfy me:
XCTAssertNil(getObject(forKey: .userCacheKey) as String?) // returns true
SDGGiesbrecht
(Jeremy David Giesbrecht)
2
Are you sure the value is actually nil? What does it print if you change it to the following?
if let nonNil = getObject(forKey: .userCacheKey) {
print(type(of: nonNil)) // Actual dynamic type of the instance.
print(nonNil) // The instance’s value.
} else {
print("nil")
}
As far as I'm aware, T should be inferred to be Any in both 4.1 and 4.2, here's a minimal example:
enum Key { case userCacheKey }
func getObject<T>(forKey key: Key) -> T? {
print(T.self)
return nil
}
func test() {
XCTAssertNil(getObject(forKey: .userCacheKey))
// passes and prints: Any
}
Is it possible to show us the implementation of your getObject(forKey:) function?
I would hazard a guess that the issue you're running into is a result of the change in casting behaviour from optional values to generic placeholders that you mention in your post – see swift - iOS 12 SDK Generic function returns Optional.some(nil) - Stack Overflow & SR-8704 for more info.
1 Like
jechris
(JC)
5
@hamishknight code is pretty simple and similar to what is logged into SR-8704:
func getObject<T>(forKey key: Key) -> T? {
return UserDefaults.standard.object(forKey: String(describing: key)) as? T
}
Just to understand the fix you did: what will be the inferred T in Swift 5 ? Any or Any? ?
T should still be inferred to be Any – to be clear, my patch didn't alter the way in which generic placeholders get satisfied, it only changed the behaviour of a cast from an optional value to a generic placeholder type.
And given your implementation of getObject(forKey:) involves such a cast, that's likely the issue you're running into.
jechris
(JC)
7
Perfect. I'll test with the patch when available.
Thanks