1. Вступ
Майже кожен розробник хоча б раз у житті казав — або думав: «Та тут же все очевидно». А потім минав тиждень, і цей самий розробник дивився на свій код, ніби на записку з майбутнього, написану невідомим почерком. Doc comments у Swift — це спосіб зафіксувати зміст API, щоб його можна було правильно використовувати, не відкриваючи реалізацію й не влаштовуючи археологічних розкопок.
Документація розв’язує дві практичні задачі. По-перше, вона пояснює як користуватися вашим типом або функцією: які вхідні дані допустимі, що повертається, які помилки можливі. По-друге, вона зменшує зв’язність: якщо контракт задокументовано, ви можете змінювати реалізацію, не ламаючи користувачів. У цьому сенсі документація чудово доповнює доступи з минулої лекції: public без документації — як «пульт керування без підписів». Працювати може, але стрес гарантовано.
Що таке doc comments у Swift: /// і /** ... */
Якщо звичайні коментарі (// і /* ... */) — це нотатки для людей, то doc comments — це нотатки для людей, яким IDE допомагає їх читати. У Swift документаційні коментарі прив’язуються до оголошення й можуть показуватися в Quick Help, автодоповненні та під час наведення на символ. Тобто ви пишете текст один раз, а він з’являється в потрібний момент, коли мозок користувача API ще не встиг перегрітися.
У Swift є два основні варіанти doc comments:
/// Однорядкова документація (або багаторядкова через кілька ///).
public struct BookID {
public let rawValue: String
}
і блоковий варіант:
/**
Багаторядкова документація блоком.
Зазвичай зручна, якщо тексту багато.
*/
public struct Year {
public let value: Int
}
На практиці в навчальних і робочих проєктах частіше використовують ///: вона простіша, швидша й добре дисциплінує писати коротко.
Важливе правило: doc comment має стояти прямо перед тим оголошенням, яке ви документуєте. Якщо між ними затесався порожній код, інший коментар або import, IDE може «приклеїти» документацію не туди або взагалі нікуди. І так, це той самий момент, коли один зайвий пробіл псує настрій.
2. Мінімальний стандарт doc comment
Коли кажуть «задокументуй функцію», новачок часто впадає в крайність: або пише «робить щось» — і це марно, або пише роман на 40 рядків — і це ніхто не читає. Мінімальний стандарт документації — це золота середина: коротко, структуровано й по суті. У Swift для структури є усталені секції - Parameters:, - Returns:, - Throws:.
Домовімося про простий «шаблон мислення»:
| Що документуємо | Що потрібно сказати мінімум |
|---|---|
|
Що це за сутність і яку роль вона відіграє в домені або шарі |
| метод/функція без throws | Що робить + важливі обмеження входу/виходу |
| метод/функція з параметрами | Пояснити параметри, особливо якщо їх легко переплутати |
| метод/функція з поверненням | Що означає повернене значення, а не його тип |
| throws‑функція | Які помилки виникають і за яких умов |
А ось чого зазвичай не варто: переказувати код рядок за рядком, пояснювати базовий синтаксис Swift, виправдовуватися («тимчасове рішення»), писати TODO: потім переписати прямо в публічному контракті.
Добрий орієнтир: документація має бути корисною людині, яка бачить лише сигнатуру, але не бачить реалізацію.
3. Параметри та повернення: - Parameters: і - Returns:
Документуємо параметри та зв’язок з argument labels
У минулій лекції про проєктування API ми обговорювали argument labels: вони роблять виклик читабельним. Але навіть з ідеальними labels іноді лишається питання: «А що саме сюди передавати? У якому форматі? Чи допустимий порожній рядок?» Ось тут doc comments — ваш шанс зробити API передбачуваним.
Погляньмо на невеликий приклад із нашого CLI-проєкту LibraryCLI. Уявімо, що в Domain ми додали об’єкт-значення BookID і хочемо парсити його з введення користувача.
import Foundation
public struct BookID: Hashable {
public let rawValue: String
/// Створює `BookID` із рядка, прибираючи пробіли по краях.
/// - Parameter text: Рядок ідентифікатора (наприклад, "bk_123"). Не має бути порожнім після `trim`.
public init?(text: String) {
let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines);
guard !trimmed.isEmpty else { return nil }
self.rawValue = trimmed
}
}
Зверніть увагу: ми не пояснюємо, що таке String і що таке init? — це вже було раніше в курсі. Ми фіксуємо правило для входу: «після trim рядок не має бути порожнім». Це і є контракт.
Якщо параметрів кілька, - Parameters: дає змогу описати кожен окремо. Це особливо важливо, коли типи однакові й їх легко переплутати.
/// Повертає рядок для виведення в CLI у форматі `key: value`.
/// - Parameters:
/// - key: Назва поля (наприклад, "title").
/// - value: Значення поля в людиночитному вигляді.
/// - Returns: Готовий рядок для друку через `print`.
func formatRow(key: String, value: String) -> String {
"\(key): \(value)"
}
Тут документація допомагає не компілятору, а людині: що вважати key, що вважати value і навіщо функція взагалі існує.
Документуємо повернення: зміст, а не тип
Дуже часта помилка новачків — документувати повернення як «повертає Int». Це марно: тип і так видно в сигнатурі. Важливо документувати зміст значення, що повертається.
Наприклад, якщо в нас є функція, яка шукає книгу в репозиторії й повертає Optional, зміст результату не завжди очевидний: nil — це «не знайшли» чи «помилка читання»? У нашому курсі ми намагаємося розділяти такі випадки через throws або окремі помилки, але навіть у простому варіанті зміст треба зафіксувати.
/// Перевіряє, чи є рядок допустимою назвою книги для каталогу.
/// - Parameter title: Заголовок книги.
/// - Returns: `true`, якщо заголовок не порожній після `trim`, інакше `false`.
func isValidTitle(_ title: String) -> Bool {
!title.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
}
Якщо функція повертає Void і ефект очевидний, наприклад printHelp(), секція - Returns: зазвичай не потрібна. Але якщо у Void‑функції є важливі побічні ефекти, наприклад вона змінює стан, краще описати це в основному тексті.
4. Помилки та access control: документуємо контракт
Помилки в Swift — це не просто «щось пішло не так», а частина дизайну API. Якщо функція throws, користувач має розуміти, які сценарії вважаються помилкою і що вона може викинути. У мінімальному стандарті ми не зобов’язані перераховувати все на рівні «у рядку 12 може статися X», але зобов’язані описати правила.
Зробімо навчальний приклад для нашого CLI. Нехай у нас є помилка парсингу команди:
public enum CommandParseError: Error {
case emptyInput
case unknownCommand(String)
}
І парсер:
import Foundation
/// Перетворює рядок, який увів користувач, на команду CLI.
/// - Parameter line: Сирий рядок, введений користувачем (наприклад, із `readLine()`).
/// - Throws: `CommandParseError.emptyInput`, якщо після `trim` рядок порожній.
/// - Returns: Нормалізована команда у вигляді рядка (наприклад, "help").
public func parseCommandName(from line: String) throws -> String {
let trimmed = line.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else { throw CommandParseError.emptyInput }
return trimmed
}
Так, тут команда повертається як рядок — ми ще не заглиблюємося в повноцінний enum команд і CLI-граматику в цій лекції. Важливе інше: документація чесно говорить, коли буде помилка. Це дає змогу вибудувати коректний do/catch на рівні CLI-шару.
Є гарне практичне правило: що ширша видимість символу, то важливіша документація. Якщо щось private, ви можете дозволити собі менше тексту, хоча іноді коментар і там рятує. Але якщо щось public, документація стає майже обов’язковою: інакше ви публікуєте контракт, який не пояснено словами.
І тут є тонкий зв’язок із правилом доступів: публічна сутність не має посилатися в сигнатурі на менш доступні типи. Так само і з документацією: публічний API не має вимагати від користувача «залізти всередину модуля, щоб зрозуміти зміст». Тобто doc comment — це така «публічна вивіска», а private деталі — це «внутрішні труби», які користувачеві бачити не треба.
До речі, у реальних Swift API іноді прямо в doc comments фіксують важливі семантичні правила, які сигнатура не передає. Це особливо помітно в системних інтерфейсах: документація пояснює зміст параметрів і дефолтів, бо без цього легко помилитися.
5. Документуємо типи та enum
Документація типів особливо важлива в модульному проєкті (swiftPM targets), тому що тип може використовуватися в десятках файлів і навіть в інших модулях. І якщо у вас public struct без коментаря, IDE покаже користувачеві «порожнечу», а він піде читати реалізацію — і ось вам уже порушення інкапсуляції «через цікавість».
Створімо в домені спрощений тип Book. Заодно покажімо стиль із public private(set), який обговорювали на лекції про інкапсуляцію: читати можна всім, змінювати — лише методами цього типу.
import Foundation
/// Книга в каталозі бібліотеки.
///
/// Зберігає мінімально необхідні дані для пошуку та виведення в CLI.
public struct Book: Equatable {
public let id: BookID
public private(set) var title: String
public init(id: BookID, title: String) {
self.id = id
self.title = title
}
}
Зверніть увагу: ми не документуємо кожне поле довгим текстом, тому що тут вони очевидні. Але ми пояснюємо роль типу й контекст: «каталог, пошук, виведення в CLI». Це допомагає майбутньому вам не перетворити Book на «мішок усього на світі».
Якщо властивість має неочевидний зміст, наприклад зберігається в одному форматі, а показується в іншому, документація біля властивості корисна.
/// Кількість секунд тайм-ауту для операцій CLI (не для мережі).
public let timeoutSeconds: Int = 10
Коротко, але знімає питання «тайм-аут чого саме?».
Enum у Swift часто є «словником смислів»: команди CLI, помилки, режими роботи. Якщо кейси не задокументовані, автодоповнення показує лише імена, і новачок — або ви через місяць — починаєте гадати.
Уявімо, що в нас є мінімальний набір команд у LibraryCLI:
/// Команда, яку користувач може виконати в CLI.
public enum Command {
/// Друкує довідку за доступними командами.
case help
/// Друкує версію застосунку.
case version
}
Тепер, коли ви десь пишете switch command, IDE зможе підказати, що означає кожен кейс. А ще це допомагає втримати API вузьким: якщо ви додаєте нову команду, ви додаєте кейс — і відразу документуєте його призначення.
6. Міні-збірка: документуємо шар CLI
Важливо не впадати в крайність і не документувати «кожен чих». Давайте зберемо маленький фрагмент нашого CLI-застосунку й задокументуємо його за мінімальним стандартом: рівно настільки, щоб користувачеві API було зрозуміло, що відбувається.
import Foundation
/// Друкує довідку для користувача у стандартний вивід.
public func printHelp() {
let text = "Використання: LibraryCLI <command>\nДоступні команди: help, version"
print(text) // Використання: LibraryCLI <command> ...
}
Тут документація буквально один рядок — і цього достатньо: функція робить лише друк.
Додаймо функцію версії:
/// Повертає рядок версії застосунку.
/// - Returns: Версія у форматі SemVer (наприклад, "1.0.0").
public func appVersion() -> String {
"1.0.0"
}
Так, це ще не повноцінне версіонування, але зміст показано: значення, що повертається, — саме версія, а не «якийсь рядок».
Markdown усередині doc comments: як зробити текст читабельним
Doc comments у Swift розуміють базовий Markdown: зворотні лапки для коду, порожні рядки для абзаців. Це робить документацію помітно приємнішою. Головне — не перетворювати її на художній роман.
/// Нормалізує введення користувача:
/// видаляє пробіли по краях і приводить до нижнього регістру.
///
/// Це корисно перед порівнянням команд на кшталт `help` і `HeLp`.
public func normalizeInput(_ text: String) -> String {
text.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
}
Тут два абзаци: перший — «що робить», другий — «навіщо й де застосовується». Для мінімального стандарту це вже майже розкіш, але дуже корисна.
7. Типові помилки під час написання doc comments
Помилка №1: плутати звичайні коментарі та doc comments.
Якщо ви пишете важливий текст про контракт, але використовуєте // замість ///, IDE не прив’яже його до символу. У результаті документація не з’явиться в Quick Help, а користувачі відкриватимуть реалізацію. Це особливо прикро, бо текст ви вже написали — просто не тим «маркером».
Помилка №2: документувати типи, але забувати про публічні init і методи.
Часто пишуть /// Модель книги над public struct Book, але забувають, що користувачеві ще треба зрозуміти, як її створити або змінити. У Swift доступи ставляться на кожен елемент окремо, і документація теж має покривати ключові елементи контракту: init, основні методи, важливі властивості.
Помилка №3: писати - Returns: Int або «повертає значення».
Тип і так видно в сигнатурі, а «повертає значення» взагалі не додає змісту. Корисна документація описує значення як концепт: «кількість секунд», «нормалізована команда», «версія у форматі SemVer». Інакше doc comment перетворюється на шум.
Помилка №4: не описувати правила входу та крайові випадки.
Найкорисніша частина документації зазвичай не в тому, що функція «розбирає рядок», а в тому, що вона робить із порожнім рядком, пробілами й неправильним форматом. Якщо цього не зафіксувати, кожен, хто викликає функцію, додумуватиме своє — і одного дня ці «свої» почнуть конфліктувати.
Помилка №5: документація розходиться з поведінкою.
Це підступна проблема: код змінили, коментар забули. У результаті людина вірить документації й пише «правильний» код, який насправді працює неправильно. Щоб такого не було, тримайте просте правило: змінили контракт — оновіть doc comment в тому ж коміті. Інакше у вас буде не документація, а вигадка за мотивами.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ