How to get the underlying Value from a Swift KeyPath when its type is KeyPath<Root, Value!>? - swift

Take a look at the following code:
struct Something {
var s: String! // Implicitly Unwrapped Optional
}
func bind<T, V>(keyPath: WritableKeyPath<T, V?>) {
}
bind(\Something.s)
The code above does not compile. If we change the signature of bind to bind<T, V>(keyPath: WritableKeyPath<T, V>) then it does compile, but the problem is that the type of V is String! and I need to get the underlying type, in this case String.
We can solve the problem as follows:
func bind<T, V>(keypath: WritableKeyPath<T, ImplicitlyUnwrappedOptional<V>>) {
}
Unfortunately the documentation says that ImplicitlyUnwrappedOptional is deprecated. However, it is not marked as deprecated with the #available attribute.
I'm hesitant to use a type that the docs say are deprecated, but I can find no other way to accomplish what I need.
Is there another way to get the implicitly wrapped generic Value type from a WritableKeyPath when its type is WritableKeyPath<T, V!>?
Will ImplicitlyUnwrappedOptional be removed at some point?

Is there another way to get the implicitly wrapped generic Value type from a WritableKeyPath when its type is WritableKeyPath<T, V!>?
Not that I'm aware of. Really the problem here is that \Something.s shouldn't be a WritableKeyPath<T, V!>. That should be illegal under the rules set out by SE-0054 (IUOs are attributes on declarations; they're not actual types that can satisfy generic placeholders).
Instead, \Something.s should be a WritableKeyPath<T, V?>, so really your original code ought to compile. This issue has been filed as a bug here.
Will ImplicitlyUnwrappedOptional be removed at some point?
Yes, this is set out by SE-0054:
Because IUOs are an attribute on declarations rather than on types, the ImplicitlyUnwrappedOptional type, as well as the long form ImplicitlyUnwrappedOptional<T> syntax, is removed. Types with nested IUOs are no longer allowed. This includes types such as [Int!] and (Int!, Int!).
However the type-checker implementation for this wasn't fully implemented for Swift 4, which is why you're still able to use ImplicitlyUnwrappedOptional as a type in order to work around your problem. It has been implemented for Swift 5 though, so your workaround will no longer compile on its release.
Although hopefully the IUO key path bug will have been fixed by then.

Related

What is the point of using an "Implicitly Unwrapped Optional" as a function parameter type?

I go over some code written by other developers and now and then I encounter signatures like this one:
func didReceiveLogMessage(_ message: String!)
Would it be safe to convert this parameter type to String instead of String! ?
Basically never declare a custom function parameter as implicit unwrapped optional. Don't.
This is a pre Swift 3 legacy syntax only for Objective-C and Core Foundation compatibility.
It's absolutely safe and even highly recommended to remove the exclamation mark. However if you really need an optional use a regular optional (?)
This gives no value. It actually adds complexity as even an Inmplicitly unwrapped optional as such counts as an optional, meaning it can be nil (but will crash). a regular String can't be nil

How to observe key path on 'Any' object in Swift 4

I'm using the new Swift 4 KVO and KeyPath APIs to observe changes on an object. Specifically I'm trying to observe something on the selection object of an NSArrayController. The problem is the selection is of type Any and that seems to be at odds with generating the required keypath, since the compiler doesn't know of any properties on an object of type Any.
The property's name is assetPointHeight. And my code looks like this:
var observation: NSKeyValueObservation?
observation = arrayController.observe(
#keyPath(NSArrayController.selection.assetPointHeight),
options: [.new],
changeHandler: { [weak self] (_, _) in
self?.updateLabel()
}
)
I get two compile errors:
Generic parameter Value could not be inferred
Type 'Any' has no member 'assetPointHeight'
How can I achieve what I'm looking for here? Is there another way of generating this KeyPath?
I would not expect this to work because assetPointHeight isn’t a real property on selection (eg, it’s not defined anywhere in source code, it’s a virtual property created at runtime). I think what’s happening here is the Swift 4 version of observe(...) is trying to resolve that path to a static type and cannot, so it’s throwing an error. (Observing only works on NSObject subclasses, as well, so Any could never be observed.) So in this case you would have to use traditional string-based KVO, as “vadian” said.

Swift dynamic type checking for structs?

I'm confused about dynamic type checking in Swift.
Specifically, I have a weirdo case where I want to, essentially, write (or find) a function:
func isInstanceOf(obj: Any, type: Any.Type) -> Bool
In Objective-C, this is isKindOfClass, but that won't work because Any.Type includes Swift structs, which are not classes (much less NSObject subclasses).
I can't use Swift is here, because that requires a hardcoded type.
I can't use obj.dynamicType == type because that would ignore subclasses.
The Swift book seems to suggest that this information is lost, and not available for structs at all:
Classes have additional capabilities that structures do not:
...
Type casting enables you to check and interpret the type of a class instance at runtime.
(On the Type Casting chapter, it says "Type casting in Swift is implemented with the is and as operators", so it seems to be a broader definition of "type casting" than in other languages.)
However, it can't be true that is/as don't work with structures, since you can put Strings and Ints into an [Any], and pull them out later, and use is String or is Int to figure out what they were. The Type Casting chapter of the Swift Book does exactly this!
Is there something that's as powerful as isKindOfClass but for any Swift instances? This information still must exist at runtime, right?
Actually you can use is operator.
Use the type check operator (is) to check whether an instance is of a certain subclass type. The type check operator returns true if the instance is of that subclass type and false if it is not.
Since struct can't be subclassed, is is guaranteed to be consistent when applied to instance of struct because it will check on it static type, and for classes it will query the dynamic type in runtime.
func `is`<T>(instance: Any, of kind: T.Type) -> Bool{
return instance is T;
}
This work for both, struct and class.
As already stated, is/as should work fine with structs. Other corner cases can usually be done with generic functions, something like:
let thing: Any = "hi" as Any
func item<T: Any>(_ item: Any, isType: T.Type) -> Bool {
return (item as? T) != nil
}
print(item(thing, isType: String.self)) // prints "true"
No idea if this fits your specific use case.
More generally, keep in mind that Swift (currently) requires knowing the type of everything at compile time. If you can't define the specific type that will be passed into a function or set as a variable, neither will the compiler.

Custom literal method with LiteralConvertible

I am reading about LiteralConvertible protocols (lets take StringLiteralConvertible for example), and I found that in swift 1.2 you can use implicit typecasting from literal to your custom LiteralConvertible type. Following example taken from NSHipster blog:
struct SomeStruct: StringLiteralConvertible {
init(stringLiteral value: String) {}
init(extendedGraphemeClusterLiteral value: String) {}
init(unicodeScalarLiteral value: String) {}
func someFunc(){}
}
Somewhere in executable space:
let someStruct: SomeStruct = "😀"
someStruct.someFunc() // ok
("😃" as SomeStruct).someFunc() // ok
"😂" is SomeStruct // ok (always true warn)
"😡".someFunc() // error: value of type 'String' has no member 'someFunc'
Comments are for swift 2 compiler. Seems like there was no error in swift 1.2. Am I missing something or there is no way to achieve this behavior in swift 2?
It wouldn't make sense, w.r.t. type safety and non-ambiguity, if the last row ("😡".someFunc()) could resolve to just any kind of type that happens to conform to StringLiteralConvertible and have instance an function someFunc {}. Protocols cannot know (without explicit testing) which types conform to them, but rather, are used e.g. as type constraint to test if a given type conforms to the protocol. What if we have another type SomeStruct2: StringLiteralConvertible { ... } that also implements someFunc(), how would the compiler resolve the call "😡".someFunc() if the implicit type conversion you describe were to to be allowed?
W.r.t. to the concept working in Swift 1.2: Swift 1.2 is ancient in terms of a fast growing (up) language as Swift, so there are bound to be some peculiarities in Swift 1.2 that seems really strange in term of "modern Swift". Just as custom conversion methods (__conversion() function), implicit typecasting is a concept that is not entirely agreeable with a statically typed and safe language such as Swift.
To somewhat base this answer on official sources, we may take a look at Swift evolution proposal SE-0072:
SE-0072: Fully eliminate implicit bridging conversions from Swift
This proposal relates to the implicit bridging between Swift native and Obj-C types, but the Swift team's position w.r.t. to any implicit conversions is made quite clear in the description of the accepted proposal [emphasis mine]
In Swift 1.2, we attempted to remove all implicit bridging conversions
from the language.
...
In the interest of further simplifying our type system and our user
model, we would like to complete this work and fully remove implicit
bridging conversions from the language in Swift 3.
...
I propose that we fully eliminate implicit bridging conversions in
Swift 3. This would mean that some users might have to introduce more
explicit casts in their code, but we would remove another special case
from Swift's type system and be able to further simplify the compiler.

In Swift, what does the ! symbol mean in a function signature?

In a Swift function signature, what does the ! after an argument imply? More specifically, does it mean the argument needs to be unwrapped before it is passed in or that it gets unwrapped (automatically) as it is passed in. Here is an example:
func annotationButtonTUI(sender: UIButton!) { }
In this case the function is a target for a UIButton so whatever happens with the ! is happening automatically.
My thought is it means you can expect an unwrapped sender object so you don't need to try an unwrap it.
This isn't quite a duplicate — there's some subtlety to implicitly unwrapped optionals in function signatures beyond their usage elsewhere.
You see implicitly unwrapped optionals in API imported from ObjC because that's the closest Swift approximation of an object that's expected to be there but which can be nil. It's a compromise for imported API — you can address these variables directly like in ObjC, and you can test them for nil using Swift optional syntax. (There's more about Apple's rationale for this in the Advanced Interoperability talk from WWDC14.) This pattern also applies to the IBAction declarations inserted by Interface Builder, since those methods are in effect getting called from ObjC code, too.
As you seem to have suspected, Swift wraps the possible nil in an optional when bridging from ObjC, but the ! in your function implementation's declaration unwraps the value so you can use it directly. (At your own risk.)
Since Swift 1.2 (Xcode 6.2 in Spring 2015), ObjC APIs can be annotated with nonnull and nullable, in which case the Swift interface to those APIs uses either a non-optional type or a fully optional type. (And since Swift 2.0 / Xcode 7.0, nearly all of Apple's APIs are audited to use nullability annotations, so their Swift signatures don't use much ! anymore.)
What's less well-known about this is that you're free to change the optionality of parameters when you implement your own Swift functions that get called by ObjC. If you want the compiler to enforce that sender in your action method can never be nil, you can take the ! off the parameter type. If you want the compiler to make sure you always test the parameter, change the ! to a ?.
The exclamation point after type declaration in the Swift method signatures means the parameter is an Implicitly Unwrapped Optional. That means it is an Optional type (that would be normally denoted with ? after the type) that gets unwrapped every time you access it in the method body. Not as it is passed in. It is as if you used forced unwrapping — sender!.titleLabel — each time you use it, but you do not have to type the exclamation point every time — hence implicitly unwrapped optional.
From Using Swift with Cocoa and Objective-C, section Working with nil:
Because Objective-C does not make any guarantees that an object is non-nil, Swift makes all classes in argument types and return types optional in imported Objective-C APIs. Before you use an Objective-C object, you should check to ensure that it is not missing.
Implicitly unwrapped optional allows you to treat it in the Swift code like a normal value type with the caveat that accessing it when it is nil will interrupt your program with runtime error. You’d guard against that using if statements, optional binding
or optional chaining.
Implicitly unwrapped optionals are pragmatic compromise to make the work in hybrid environment that has to interoperate with existing Cocoa frameworks and their conventions more pleasant, while also allowing for stepwise migration into safer programing paradigm — without null pointers — enforced by the Swift compiler. You’ll meet them all over Cocoa APIs, but there are also some use cases for them in pure Swift as discussed in Why create "Implicitly Unwrapped Optionals"?