Many Cocoa and Cocoa Touch calls are boilerplate. They use common argument values with nearly every call. Why not take advantage of Swift's default system instead? We'd like to simplify calls like this:
dismissViewControllerAnimated(flag: true, completion: nil)
to calls like this:
dismiss()
Joe Groff:
Note that framework owners really don’t have the tools to do this right now: one cannot effectively replace an Objective-C method with a method in an overlay without breaking overriding, so framework owners who wanted to add defaults for specific APIs would likely need a way to spell the default argument in Objective-C that would work through the Clang importer.
Dave A:
I don't think we can add anything to SE-0005 now that it's accepted, but the "parameter patterns" part of this gist looks like it could be a proposal on swift-evolution for the Objective-C importer. We'd need to make sure the framework owners or their representatives participate in that review, though. The rest are still radar fodder, I think.
SE-0005 introduced a number of ways in which ObjC APIs will gain default values through automatic translation. Under this accepted proposal, any method that matches one of the following patterns will now provide default values:
- Trailing closures: nullable closure parameters will default to
nil
. - NSZones: nullable zones also default to
nil
. (The proposal notes that they should always benil
to begin with as they are unused in Swift.) - Option sets: Any type name containing the word
Options
defaults to[]
, the empty option set. - Dictionaries:
NSDictionary
parameters with names includingoptions
,attributes
, andinfo
default to[:]
, the empty dictionary.
Daniel Steinberg and I love this defaulting behavior and approached the Swift Evolution list to see whether we could expand defaults for Cocoa and Cocoa Touch APIs even further. David Abrahams replied that we should best proceed by putting together specific requests and file them as radars for the UIKit/AppKit teams, who would then presumably provide extensions that pass the default values.
For example, dismissViewControllerAnimated(flag: true, completion: nil)
could become dismissAnimated()
or better yet dismiss()
when default values
are available for flag
and completion
. There's a large-ish class of these boilerplate
defaults.
Here's our preliminary list of updates that we'd like to see, and we're requesting community input to help us expand that list, so when we approach our bug reports that we've already thought through and prioritized the kinds of changes we're invested in.
Here's our current list:
-
Set a default value for any case where an imported API uses an enum parameter that offers a case named
.Normal
,.Default
, or.Plain
. For example, the action style forUIAlertAction
, the view style forUIAlertViewStyleDefault
, the table row action style forUITableViewRowActionStyleDefault
all provide.Default
options.UIBarButtonItem
offers a.Plain
style. Other.Plain
style enums includeUITableViewStyle
,PKPaymentButtonType
, andWKTextInputMode
. -
In any case where an imported API uses more than one parameter and one of those parameters is
bundle:
, default that parameter to nil. For example, forUIStoryboard
'sstoryboardWithName:bundle:
andUINib
'snibWithName:bundle:
, thebundle:
should default to nil.
UIControl
states should always default to.Normal
, e.g.button.setTitle("Hello")
. (This recommendation overlaps with the parameter name section from earlier in this write-up.)UIButton
actions should always default to.TouchUpInside
, e.g.button.addTarget(target, action: action)
(although, honestly,button.add(target: target, action: action)
would be preferable)UISlider
andUISwitch
(and probablyUIStepper
andUISegmentedControl
) actions should always default to.ValueChanged
, e.g.slider.addTarget(target, action: action)
- CG construction should always default to
0.0
values (e.g.x: 0.0, y:0.0, dX: 0.0, dY: 0.0, width: 0.0, height: 0.0
). This courtesy should probably extend as well toUIEdgeInsets
,UIOffsetMake
, and possiblyCGAffineTransform
(this using identity transform defaults).
UIGraphicsBeginImageContextWithOptions
almost universally usesscale
of0.0
. (This follows the screen scale of the current device.)animated:
should always default totrue
for UIKit controller calls. We note that callingsuper.viewWillAppear()
is foolish. You should always pass on the argument that was passed to you. A full list of UIKitanimated:
references including controller and non-controller APIs is here.
UIColor
,SKColor
, andNSColor
's constructors should default toalpha
=1.0
NSNotificationCenter
theobject:
parameter should default to nil.
NSLayoutConstraint
can default to relation (relatedBy
).Equal
, multiplier1.0
, constant0.0
, secondItem (toItem
)nil
, second attribute (attribute
).NotAnAttribute
. One can argue that defaults should be limited to multiplier and constant as readability suffers by defaulting relation, secondItem, and secondAttribute and single-item constraints represent the minority of use-cases. However, there's also an argument for enhanced readabilityNSLayoutConstraint(item: view, attribute: .Width, constant: 400.0)
and the defaulted arguments can still be used.
To simplify record fetches:
* CKModifyRecordsOperation
recordIDsToDelete:
could default to nil.* CKQuery could introduce a default TruePredicate
.
Please suggest additional defaults to help grow this list
I agree with most of this, but not all.
Regarding
animated:
, this makes sense for things likedismissViewControllerAnimated(_:completion:)
, but it doesn't make sense for all UIKit calls withanimated
. For example,super.viewWillAppear()
should not be allowed. Similarly any control with asetFoo(_:animated:)
call shouldn't default theanimated
parameter, both because these are usually paired with a non-animatedfoo
property (so it would be kind of weird to have bothx.foo = bar
andx.setFoo(bar)
), and because there's no sensible default foranimated
(I don't think it's appropriate to assume that these calls should default to being animated, and defaulting to non-animated doesn't make sense either).Regarding
NSLayoutConstraint
, if you make the relation, secondItem, and secondAttribute optional, that hurts the readability of the constructor. TheNSLayoutConstraint
init API is designed to be basically a readable sentence, e.g.NSLayoutConstraint(item: subview, attribute: .Leading, relatedBy: .Equal, toItem: view, attribute: .Leading, multiplier: 1, constant: 0)
is "subview's attribute leading is related by equality to view's attribute leading". Yeah it's not perfect but it's close. Removing items makes this confusing:NSLayoutConstraint(item: subview, attribute: .Leading, toItem: view, attribute: .Leading)
doesn't say how they're related. Not only that, butnil
and.NotAnAttribute
are not reasonable values for secondItem / secondAttribute in most cases, so having them as defaults doesn't seem like a good idea. Defaultingmultiplier
andconstant
is acceptable. However, it's worth keeping in mind that as of iOS 9 there's not much call to use this API anyway because layout anchors allow you to construct constraints in a much simpler fashion, so I'm not convinced it's worth trying to modify this API at all.