Bogdan P.

Hi there! I'm Bogdan's digital clone - nice to meet you! I can answer questions about Bogdan and his work, and put you in touch with him if need be. What's on your mind?

Hyper legible Swift

SwiftiOSAPI DesignClean Code

Write Swift that reads like English. Naming conventions, parameter labels, and a prompt that does it automatically.

Swift is unusually expressive. With the right names and parameter labels, call sites read like sentences that tell you exactly what the code does. I've been refining this style and wanted to share what works.

Four things make it click:

The rest is examples.

Before and after

Three real examples from a calendar timeline I'm building.

1. Tap handling

This function needs to do three things:

Before

Swift
1private func handleTap(at location: CGPoint, _: CGFloat) {
2    if titleFocused {
3        titleFocused = false
4        return
5    }
6    if let item = hitTester.findEvent(at: location, in: currentEventLayoutAttributes) {
7        setEventToolbar(for: item)
8        editingEventId = item.id
9    } else if dragState == nil {
10        editingEventId = nil
11    }
12}

After

Swift
1private func handleTap(at location: CGPoint, _: CGFloat) {
2    unfocusTitleIfEditing()
3    if let item = event(at: location) {
4        toggleEditMode(.on, for: item)
5    } else if !isDragActive {
6        editingEventId = nil
7    }
8}

Every line says what it does. unfocusTitleIfEditing() needs no comment. event(at: location) reads as a phrase. The function is a short list of intentions.

2. Drag handling

The timeline supports three kinds of drags:

Before

Swift
1private func handleDragChanged(to location: CGPoint, _: CGFloat) {
2    movedFingerToDate = yToDate(location.y)
3
4    if isCreatingEvent, let current = movedFingerToDate { /* ... */ }
5
6    if let editingEventId,
7       let event = app.schedule.itemById[editingEventId],
8       let start = touchedDownAtDate,
9       let current = movedFingerToDate,
10       let state = dragState {
11        /* compute offsets, update state, manage previews */
12    }
13}

After

Swift
1private func handleDragChanged(to location: CGPoint, _: CGFloat) {
2    date = yToDate(location.y)
3
4    switch currentlyDragging {
5    case .toCreateEvent:
6        updateDragOffset(for: .eventCreation, at: date)
7    case .toMoveEvent:
8        updateDragOffset(for: .eventDrag, at: date)
9    case .toUpdateEventHandle:
10        updateDragOffset(for: .eventHandles, at: date)
11    }
12}

The function becomes a router. Each branch names the intent and delegates to a helper. You can scan it in seconds.

3. Transformation pipeline

Before

Swift
1private func arrangeOverlappingEvents(_ events: [EventLayoutAttributes]) -> [EventLayoutAttributes] {
2    guard !events.isEmpty else { return [] }
3
4    let eventsByStartTime = sortByStartTime(events)
5    let overlappingGroups = groupOverlappingEvents(eventsByStartTime)
6
7    return overlappingGroups.flatMap { group in
8        layoutEventsInGroup(group)
9    }
10}

After

Swift
1private func arrangeOverlappingEvents(_ events: [EventLayoutAttributes]) -> [EventLayoutAttributes] {
2    guard !events.isEmpty else { return [] }
3
4    let ordered = sort(events)
5    let groups = groupOverlapping(events: ordered)
6    return groups.flatMap { layout(in: $0) }
7}

Three steps, three names: sort, group, layout. The word "events" disappears because the types already carry it.

Parameter labels are the trick

Swift's external parameter labels let you turn function calls into English phrases. That's the whole mechanism.

Reads: "start event resize at location for event"

Swift
1func startEventResize(at location: CGPoint, for event: Event) -> Bool { /* ... */ }

Reads: "toggle edit mode on for item"

Swift
1func toggleEditMode(_ state: EditState, for item: Item) { /* ... */ }

Reads: "layout in group"

Swift
1func layout(in group: [EventLayoutAttributes]) -> [EventLayoutAttributes] { /* ... */ }

The rules are simple:

Why bother

You can read the code without checking implementations. Call sites document themselves. The function name tells you what happens, the labels tell you what each argument means. Comments become optional.

It also plays well with AI tools. Clear intent and explicit naming give models more to work with. Refactors and completions are more accurate when the code already says what it means.

Checklist

The prompt

Drop this into Claude Code, Cursor, or whatever you use. One-shot refactors with this prompt have never broken anything for me.

naming-and-structure.md

Markdown
1---
2alwaysApply: true
3---
4
5We write intention‑revealing Swift that reads like English and encodes product requirements in names.
6
7## Principles
8- **Intent over mechanics.** Names state outcomes (what), not procedures (how).
9- **Sentence‑style call sites.** Verb + prepositions form readable phrases.
10- **Ubiquitous domain language.** Use the product's nouns/verbs consistently.
11- **Step‑down narrative.** Orchestrate at top, detail in helpers.
12- **Omit needless words.** Types and scope carry context.
13
14## Parameter Labels
15- Omit first external label (`_`) if verb + first arg reads clearly.
16- Use `in:`, `at:`, `for:`, `on:`, `by:`, `from:`, `to:` to complete the sentence.
17- Prefer concrete role nouns: `group`, `item`, `range`.
18
19## Verb Taxonomy
20- Lifecycle: `start`, `update`, `finish`, `cancel`
21- Visibility: `show`, `hide`, `reveal`, `dismiss`
22- State: `enable`, `disable`, `select`, `deselect`, `toggle`
23- Effects: `apply`, `commit`, `rollback`, `retry`, `refresh`
24- Safety: `ensure`, `validate`, `protect`, `require`
25
26## Structure
27- Keep orchestration funcs small; push details into well‑named helpers.
28- Favor linear pipelines for transforms: `sort → group → layout`.
29- Replace comments with named helpers; names should explain *why*.
30- Avoid `handle/process/manage` unless nothing else is truly clearer.
31
32## Examples
33
34**Before**
35```swift
36func handleResizeHandleTouch(location: CGPoint, event: Event) -> Bool { /* ... */ }
37```
38
39**After**
40```swift
41func startEventResize(at location: CGPoint, for event: Event) -> Bool { /* ... */ }
42```
43
44**Before**
45```swift
46func handleEventMoveTouch(location: CGPoint, event: Event) -> Bool { /* ... */ }
47```
48
49**After**
50```swift
51func startEventMove(at location: CGPoint, for event: Event) -> Bool { /* ... */ }
52```
53
54**Before**
55```swift
56func handleDeletionGesture(inLeftMargin locationX: CGFloat) { /* ... */ }
57```
58
59**After**
60```swift
61func previewDeleteEvent(at x: CGFloat) { /* ... */ }
62```
63
64**Pipeline**
65```swift
66let ordered = sort(events)
67let groups = groupOverlapping(events: ordered)
68let laidOut = groups.flatMap { layout(in: $0) }
69```
70
71## Renaming examples
72
73| Old (Vague) | New (Intent) |
74|-------------|--------------|
75| handleResizeHandleTouch | startEventResize |
76| handleEventMoveTouch | startEventMove |
77| handleDeletionGesture | previewDeleteEvent |
78| updateDragGestureOffset | updateDragOffset |
79| updateDeletionCloseness | updateDeletePreview |
80
81## Review Checklist
82- Does the call site read like a sentence? If not, relabel.
83- Can a new engineer infer the outcome from names alone?
84- Are there any redundant words that types/scopes already imply?
85- Are verbs reused consistently across the module?
86- Is orchestration short and linear? If not, extract helpers.
87
88## Anti‑Patterns
89- Buckets like handle/process/manage everywhere.
90- Over‑shortening that creates ambiguity (sort in global scope).
91- Comment walls explaining intent - encode it in names instead.
92
93## LLM Synergy
94- Keep behavior and requirements in names and labels so models can infer intent in limited context windows. Prefer small, composable helpers with narrow effects.

Write the call site first. Make it read like a sentence. Then write the function behind it.