Skip to content

Instantly share code, notes, and snippets.

@erica
Last active Nov 1, 2017
Embed
What would you like to do?

Requesting Default Values

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.

Background

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 be nil 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 including options, attributes, and info 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.

Requested Defaults

Here's our current list:

Parameter patterns

  • 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 for UIAlertAction, the view style for UIAlertViewStyleDefault, the table row action style for UITableViewRowActionStyleDefault all provide .Default options. UIBarButtonItem offers a .Plain style. Other .Plain style enums include UITableViewStyle, PKPaymentButtonType, and WKTextInputMode.

  • 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, for UIStoryboard's storyboardWithName:bundle: and UINib's nibWithName:bundle:, the bundle: should default to nil.

Controls

  • 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 and UISwitch (and probably UIStepper and UISegmentedControl) actions should always default to .ValueChanged, e.g. slider.addTarget(target, action: action)

Core Graphics

  • 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 to UIEdgeInsets, UIOffsetMake, and possibly CGAffineTransform (this using identity transform defaults).

UIKit

  • UIGraphicsBeginImageContextWithOptions almost universally uses scale of 0.0. (This follows the screen scale of the current device.)
  • animated: should always default to true for UIKit controller calls. We note that calling super.viewWillAppear() is foolish. You should always pass on the argument that was passed to you. A full list of UIKit animated: references including controller and non-controller APIs is here.

Colors

  • UIColor, SKColor, and NSColor's constructors should default to alpha = 1.0

Foundation

  • NSNotificationCenter the object: parameter should default to nil.

Somewhat Controversial

  • NSLayoutConstraint can default to relation (relatedBy) .Equal, multiplier 1.0, constant 0.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 readability NSLayoutConstraint(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

@lilyball

This comment has been minimized.

Copy link

@lilyball lilyball commented Apr 12, 2016

I agree with most of this, but not all.

Regarding animated:, this makes sense for things like dismissViewControllerAnimated(_:completion:), but it doesn't make sense for all UIKit calls with animated. For example, super.viewWillAppear() should not be allowed. Similarly any control with a setFoo(_:animated:) call shouldn't default the animated parameter, both because these are usually paired with a non-animated foo property (so it would be kind of weird to have both x.foo = bar and x.setFoo(bar)), and because there's no sensible default for animated (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. The NSLayoutConstraint 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, but nil and .NotAnAttribute are not reasonable values for secondItem / secondAttribute in most cases, so having them as defaults doesn't seem like a good idea. Defaulting multiplier and constant 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.

@KiGi

This comment has been minimized.

Copy link

@KiGi KiGi commented Apr 14, 2016

Some other items:
UIAlertAction style: parameter default to .Default
UIStoryboard storyboardWithName:bundle: - bundle: should default to nil (same for UINib nibWithName:bundle:)
NSNotificationCenter various subscribe/unsubscribe calls should default object: parameter to nil

@watling

This comment has been minimized.

Copy link

@watling watling commented Apr 18, 2016

there are quite a few calls that are ( or should be ) optional and should default to nil too.
Like presentViewController , and several FileManager ones like createDirectoryAtPath where you often have attributes = nil
dateByAddingUnit could have [] as default for options
When creating an NSError, the dict is optional, but could default to nil
same with calendar.dateByAddingUnit
NSSortDescriptor could default to ascending ( perhaps )
CKModifyRecordsOperation could default to nil for the recordsIDsToDelete at least
Maybe CKQuery could have a default TruePredicate to make getting all records simpler.

@erica

This comment has been minimized.

Copy link
Owner Author

@erica erica commented Apr 18, 2016

@watling: attributes are already defaulted to [] for SE-0005, as are any dictionary with the name options, attributes, and info. Added the other suggestions.

@kemchenj

This comment has been minimized.

Copy link

@kemchenj kemchenj commented Jun 30, 2016

Some nilable parameter in method default to "nil"

sender

performSegueWithIdentifier(_:sender:)

showViewController(_:sender:)
showDetailViewController(_:sender:)

In many time, may be 50%? I don't really have to know which instance is the sender

bundle

// UIImage
init(named:inBundle:compatibleWithTraitCollection:)

// UINib
init(nibName:bundle:)

// NSManagedObjectManager 
mergedModelFromBundles(_:)

Most of time we specify nil to search the app’s main bundle

completion

animateWithDuration(_:animations:)
animateWithDuration(_:animations:completion:)

animateWithDuration(_:delay:options:animations:completion:)
animateWithDuration(_:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion:)

"completion" means nilable, optional

"completionHandler" means required

Use "completion" and "completionHandler" to draw the line.

btw, delay could default to 0

Others

// NSFileManager
URLsForDirectory(_:inDomains:)

"inDomains" default to ".userDomainMask"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment