Issue #1044
Good naming is one of the most underrated forms of documentation. A well-chosen name removes the need for a comment. A poor one forces every reader to mentally re-derive what the code does. Apple’s own frameworks are a rich source of patterns worth studying closely. The naming of clipsToBounds, tableView(_:didSelectRowAt:), and Equatable did not happen by accident. Each follows a set of rules that, once internalized, makes your own API feel natural to anyone who has written Swift.
Boolean properties
Apple names most boolean properties using the third-person singular form of a verb. The value reads as an assertion about the object it belongs to. “This view clips to bounds.” “This layer masks to bounds.” “This scroll view scrolls to top.”
UIView.clipsToBounds
CALayer.masksToBounds
UIScrollView.scrollsToTop
UIScrollView.bounces
UIView.translatesAutoresizingMaskIntoConstraints
NSManagedObjectContext.automaticallyMergesChangesFromParent
The verb conjugation is the signal. clips implies the view is the actor. The property is not describing a state; it is describing an ongoing behavior that the object performs. Naming it isBoundClipped would flip the meaning toward a condition, and boundClipping would sound like a noun rather than a flag.
When the value describes a current state rather than a behavior, the is prefix is appropriate.
UIView.isHidden
UIView.isOpaque
UIControl.isEnabled
UIScrollView.isScrollEnabled
UIView.isUserInteractionEnabled
isHidden does not mean the view hides itself. It means the view is currently hidden. The distinction matters when you are naming your own booleans. Ask whether the property controls an action the object takes (clips, bounces, scrolls) or describes a condition the object is in (isHidden, isEnabled). When neither fits neatly, is is the safer default.
Apple is not afraid of long names. translatesAutoresizingMaskIntoConstraints is verbose, but it communicates exactly what the property controls with no ambiguity. Brevity at the cost of clarity is not a good trade.
Delegate methods
Delegate methods in Apple frameworks use three prefixes that signal when and why the method is called: will, did, and should.
A will method fires just before an action. It often returns a value that can modify or cancel the action. A did method fires after the action completes and returns Void. A should method is a question asked synchronously, expecting a Bool answer that controls whether the action proceeds.
// Before selection — return a different path to redirect, nil to prevent
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath?
// After selection — observation only
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
// Permission check — return false to block editing
func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool
// After scrolling begins
func scrollViewDidScroll(_ scrollView: UIScrollView)
// After deceleration ends
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView)
Every delegate method starts with the sender as an unnamed first parameter. The label is suppressed with _, and the parameter name matches the class in lowercase: tableView, scrollView, collectionView. This convention exists because a single delegate object may serve multiple instances of the same class. The sender parameter is what lets you tell them apart.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if tableView === searchResultsTable {
showSearchResult(at: indexPath)
} else if tableView === historyTable {
showHistoryItem(at: indexPath)
}
}
Delegate protocol names follow the naming of the class they serve, with a Delegate or DataSource suffix: UITableViewDelegate, UITableViewDataSource, UIScrollViewDelegate. The suffix signals the role, not the capability. This keeps delegate protocols distinct from capability protocols like Equatable.
Closures and completion handlers
Apple’s closure parameter labels describe what triggers the closure or what it receives. Generic callbacks use completionHandler or handler when the closure fires after work finishes. The word completion is a signal to callers that the method is asynchronous and this is how they get the result.
func dataTask(
with url: URL,
completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void
) -> URLSessionDataTask
The parameter order inside the closure follows a consistent rule: result first, context second, error last. Callers typically check the result immediately, consult response metadata only if needed, and handle the error as a fallback. The order reflects that reading priority.
When writing your own async methods, apply the same ordering.
func fetchUser(
id: String,
completion: @escaping (User?, URLResponse?, Error?) -> Void
)
SwiftUI uses a different style for closures attached to view modifiers. Event-driven modifiers use an on prefix: .onAppear, .onDisappear, .onChange(of:), .onReceive(_:). The on prefix reads as “when this event happens, run this block”, which is more accurate than a generic handler label because the event is named explicitly in the modifier itself.
Text("Hello")
.onAppear { loadData() }
.onChange(of: query) { newValue in filterResults(for: newValue) }
When naming closure parameters in your own APIs, the label should describe the event or the moment, not the expected action. Callers decide what to do; the API names when.
Protocol names
Protocols fall into two categories in Swift, and each category uses a different naming style.
Protocols that describe an identity or a concept use plain nouns. A type that conforms to Collection is a collection. Sequence, Iterator, RandomAccessCollection, and BidirectionalCollection all name what the conforming type is, not what it can do.
Protocols that describe a capability use the suffixes -able, -ible, or -ing. Equatable means the type supports equality testing. Hashable means it can produce a hash value. Codable is a type alias for Encodable & Decodable. ProgressReporting means the type exposes progress information.
// Identity
protocol Collection { ... }
protocol Sequence { ... }
// Capability
protocol Equatable { ... }
protocol Hashable: Equatable { ... }
protocol Codable: Encodable, Decodable { ... }
protocol ProgressReporting { ... }
The distinction helps readers understand a protocol from its name before reading its definition. If you see a type conforming to Hashable, you know exactly what that adds. If you see it conforming to Collection, you know what it represents.
Method labels and argument prepositions
Swift argument labels are part of the full method name. Apple designs them so that the call site reads as close to natural English as possible.
array.insert(element, at: index)
array.removeAll(where: { $0 == target })
array.sorted(by: >)
string.replacingOccurrences(of: "old", with: "new")
tableView.insertRows(at: indexPaths, with: .automatic)
tableView.moveRow(at: source, to: destination)
Each label is a preposition that signals the argument’s role. at marks a position. with provides a style or option. of identifies the subject. by provides the comparator. to marks a destination. These prepositions are not decoration; they carry structural information about what the argument does.
A method without a label should read naturally without one. array.append(element) works because the label would just add noise. array.insert(element, at: index) needs the label because the index requires context.
Apple also defines mutating and nonmutating variants as pairs. The mutating form uses an imperative verb. The nonmutating form appends -ed or -ing to return a new value.
array.sort() // mutates in place, returns Void
array.sorted() // returns a new sorted copy
array.reverse() // mutates in place
array.reversed() // returns a new reversed sequence
This pattern is worth replicating. If your type has a mutating normalize(), a nonmutating normalized() that returns a new value gives callers the choice without changing the semantics of either.
Start the conversation