How does typecasting/polymorphism work with this nested, closure type in Swift?
It's all about variance and Swift closures.
Swift is covariant in respect to closure return type, and contra-variant in respect to its arguments. This makes closures having the same return type or a more specific one, and same arguments or less specific, to be compatible.
Thus (Arg1) -> Res1
can be assigned to (Arg2) -> Res2
if Res1: Res2
and Arg2: Arg1
.
To express this, let's tweak a little bit the first closure:
import Foundation
let nsErrorHandler: (CustomStringConvertible) -> NSError = { _ in
return NSError(domain: "", code: 0, userInfo: nil)
}
var anyHandler: (Int) -> Error = nsErrorHandler
The above code works because Int
conforms to CustomStringConvertible
, while NSError
conforms to Error
. Any
would've also work instead of Error
as it's even more generic.
Now that we established that, let's see what happens in your two blocks of code.
The first block tries to assign a more specific argument closure to a less specific one, and this doesn't follow the variance rules, thus it doesn't compile.
How about the second block of code? We are in a similar scenario as in the first block: closures with one argument.
- we know that
String
, orVoid
, is more specific thatAny
, so we can use it as return value (Int) -> Void
is more specific than(Any) -> Void
(closure variance rules), so we can use it as argument
The closure variance is respected, thus intResolver
and stringResolver
are a compatible match for anyResolver
. This sounds a little bit counter-intuitive, but still the compile rules are followed, and this allows the assignment.
Things complicate however if we want to use closures as generic arguments, the variance rules no longer apply, and this due to the fact that Swift generics (with few exceptions) are invariant in respect to their type: MyGenericType<B>
can't be assigned to MyGenericType<A>
even if B: A
. The exceptions are standard library structs, like Optional
and Array
.
Swift generic closures sum
You are running into the issue of Swift generic coercion misunderstanding. Generic types in Swift are invariant, which means that Styler<A>
and Styler<B>
are completely unrelated types even if A
and B
are related (subclasses for instance).
This is why Style<UILabel>
and Styler<UIView>
are unrelated. However, closures (and hence functions) are variant (as explained here) - covariant on the return type, and contravariant on the parameter types, this is why your first example works.
Because of this, you can pass a UILabel
to a Styler<UIView>.apply
, since that is a simple function call, which accepts subclasses of the declared input argument type.
let styler1: Styler<UIView> = Styler { (label: UIView) -> Void in
label.backgroundColor = UIColor.red
}
let styler2: Styler<UIView> = Styler { (view: UIView) -> Void in
view.backgroundColor = UIColor.green
}
let styler3 = styler1 + styler2
styler1.apply(UILabel())
Swift optional promotion vs generic overload resolution
Specificity seems to always trump variance conversions, according to my experiments. For example:
func bar<T>(_ x: [Int], _ y: T) { print("A") }
func bar<T: P>(_ x: [Any], _ y: T) { print("B") }
bar([1], Y()) // A
bar
is more specific, but requires a variance conversion from [Int]
to [Any]
.
For why you can convert from (Y?) -> Void
to (P) -> Void
, see this. Note that Y
is a subtype of Y?
, by compiler magic.
Since it is so consistent, this behaviour seems to be by design. Since you can't really make Y
not a subtype of Y?
, you don't have a lot of choices if you want to get the desired behaviour.
I have this work around, and I admit it's really ugly - make your own Optional
type. Let's call it Maybe<T>
:
enum Maybe<T> {
case some(T)
case none
// implement all the Optional methods if you want
}
Now, your (Maybe<Y>) -> Void
won't be converted to (P) -> Void
. Normally I wouldn't recommend this, but since you said:
in the real-world code where I encountered this, the closure has multiple params, any of them can be optional, so this would lead to a combinatorial explosion.
I thought reinventing Optional
might be worth it.
Swift: What's wrong with this math function?
The size of Int
is 32-bit on 32-bit platforms (e.g. iPhone 5 and below), but the timestamp nowadays is in the range 0x57300000
, which will certainly overflow when you multiply it by 2. In Swift arithmetic overflow will cause the program to crash.
You could force the output to be an Int64, or just return a TimeInterval
(a Double
) if you don't require the output to be an integer.
// swift 3
func Unixtime() -> TimeInterval {
return Date().timeIntervalSince1970
}
// swift 2
func Unixtime() -> NSTimeInterval {
return NSDate().timeIntervalSince1970
}
print((Unixtime() + 2) * 2)
How can I get first 10-bits of UInt16 type?
How about, as 0x3FF in binary is 0000 0011 1111 1111
x = (UInt16)(x & 0x3FF)
How can I get first 10-bits of UInt16 type?
How about, as 0x3FF in binary is 0000 0011 1111 1111
x = (UInt16)(x & 0x3FF)
Castle: using an existing (not single) instance for a lower-level dependency
The solution was to use ContextualLifestyle
coupled with a custom factory that kept a reference to the ContainerContext
, in order to use the same one when resolving another ViewModel
.
Extension that makes wrapper that dispenses self typed as self
The trick to this is to define a protocol, an extension of that protocol, and put the makeHolder
method in that extension. That way, you can use Self
as the generic type for the returned ControlHolder
.
First define a new protocol (let's call it "HoldableControl
") and require that conformers must be UIControl
s. It doesn't need any other requirements because we just care about adding the makeHolder
function to an extension.
protocol HoldableControl: UIControl {}
Then, add an extension of HoldableControl
and define makeHolder
in it, returning ControlHolder<Self>
. We are allowed to use Self
here because it is allowed in protocol extensions, unlike in an extension to UIControl
.
extension HoldableControl {
func makeHolder() -> ControlHolder<Self> {
ControlHolder(control: self)
}
}
Then, we just need to have UIControl
conform to this protocol:
extension UIControl: HoldableControl {}
And make your ControlHolder
generic, as you've already done:
struct ControlHolder<T: UIControl> {
let control: T
init(control: T) {
self.control = control
}
func retrieve() -> T {
control
}
}
And now it will work:
let holder = UISwitch().makeHolder() // type is ControlHolder<UISwitch>
let output = holder.retrieve() // type is UISwitch
Method chaining - why is it a good practice, or not?
I agree that this is subjective. For the most part I avoid method chaining, but recently I also found a case where it was just the right thing - I had a method which accepted something like 10 parameters, and needed more, but for the most time you only had to specify a few. With overrides this became very cumbersome very fast. Instead I opted for the chaining approach:
MyObject.Start()
.SpecifySomeParameter(asdasd)
.SpecifySomeOtherParameter(asdasd)
.Execute();
The method chaining approach was optional, but it made writing code easier (especially with IntelliSense). Mind you that this is one isolated case though, and is not a general practice in my code.
The point is - in 99% cases you can probably do just as well or even better without method chaining. But there is the 1% where this is the best approach.
Related Topics
How to Detect a 'Click' Gesture in Swiftui Tvos
How to Get Date and Time to Show a Clock in Uilabel
Open a Viewcontroller from Remote Notification
Reading an Inputstream into a Data Object
Uitableview - Multiple Selection and Single Selection
Simple Clickable Link in Cocoa and Swift
Input Type=File Not Working in Webview of Os X Application
Calculate Area of Mkpolygon in an Mkmapview
Translucent Status Bar with No Navigation Bar
Find Item of Specific Type in Array
App Crash on Sign in (Xcode 9.3) Exc_Bad_Access (Code=1, Address=0X1)
Wait Until an Asynchronous API Call Is Completed - Swift/Ios
Need to Check That Braces in Given Array Are Balanced or Not
How to Change Colour of the Certain Words in Label - Swift 3
How Is a Return Value of Anyobject! Different from Anyobject