Рекомендации по проектированию API
Для удобства использования в качестве быстрого справочника, подробности многих рекомендаций можно развернуть индивидуально. Подробности никогда не скрываются при печати этой страницы.
Оглавление
Введение
Обеспечение ясного и согласованного опыта разработчика при написании кода Swift в значительной степени определяется именами и идиомами, которые появляются в API. Эти рекомендации по проектированию объясняют, как убедиться, что ваш код ощущается как часть более широкой экосистемы Swift.
Основы
-
Ясность в момент использования является вашей самой важной целью. Сущности, такие как методы и свойства, объявляются только один раз, но используются многократно. Проектируйте API таким образом, чтобы эти использования были ясными и лаконичными. При оценке дизайна редко достаточно просто прочитать объявление; всегда проверяйте пример использования, чтобы убедиться, что он выглядит понятно в контексте.
-
Ясность важнее краткости. Хотя код Swift может быть компактным, стремление к самому короткому коду с наименьшим количеством символов не является целью. Краткость в коде Swift, где она встречается, является побочным эффектом сильной системы типов и функций, которые естественным образом сокращают избыточность.
-
Напишите комментарий к документации для каждого объявления. Знания, полученные при написании документации, могут значительно повлиять на ваш дизайн, поэтому не откладывайте это.
Если у вас возникают трудности с описанием функциональности вашего API простыми словами, возможно, вы спроектировали неправильный API.
-
Используйте диалект Markdown Swift.
-
Начните с краткого описания, которое описывает объявляемую сущность. Часто API можно полностью понять по его объявлению и краткому описанию.
/// **Returns a "view" of `self` containing the same elements in** /// **reverse order.** func reversed() -> ReverseCollection<Self>-
Сфокусируйтесь на кратком описании; это самая важная часть. Многие отличные комментарии к документации состоят лишь из хорошего краткого описания.
-
Используйте фрагмент предложения, если это возможно, заканчивающийся точкой. Не используйте полное предложение.
-
Опишите, что делает функция или метод и что она возвращает, опуская нулевые эффекты и
Voidвозвращаемые значения:/// **Inserts** `newHead` at the beginning of `self`. mutating func prepend(_ newHead: Int) /// **Returns** a `List` containing `head` followed by the elements /// of `self`. func prepending(_ head: Element) -> List /// **Removes and returns** the first element of `self` if non-empty; /// returns `nil` otherwise. mutating func popFirst() -> Element?Примечание: в редких случаях, подобных
popFirstвыше, краткое описание состоит из нескольких фрагментов предложений, разделенных точкой с запятой. -
Опишите, к чему обращается индекс:
/// **Accesses** the `index`th element. subscript(index: Int) -> Element { get set } -
Опишите, что создаёт инициализатор:
/// **Creates** an instance containing `n` repetitions of `x`. init(count n: Int, repeatedElement x: Element) -
Для всех остальных объявлений опишите, что представляет собой объявляемая сущность.
/// **A collection that** supports equally efficient insertion/removal /// at any position. struct List { /// **The element at the beginning** of `self`, or `nil` if self is /// empty. var first: Element? ...
-
-
Дополнительно можно использовать один или несколько абзацев и пунктов списка. Абзацы разделены пустыми строками и используют полные предложения.
/// Writes the textual representation of each <span class="graphic">←</span><span class="commentary"> Summary</span> /// element of `items` to the standard output. /// <span class="graphic">←</span><span class="commentary"> Blank line</span> /// The textual representation for each item `x` <span class="graphic">←</span><span class="commentary"> Additional discussion</span> /// is generated by the expression `String(x)`. /// /// - **Parameter separator**: text to be printed <span class="graphic">⎫</span> /// between items. <span class="graphic">⎟</span> /// - **Parameter terminator**: text to be printed <span class="graphic">⎬</span><span class="commentary"> <a href="https://developer.apple.com/library/prerelease/mac/documentation/Xcode/Reference/xcode_markup_formatting_ref/SymbolDocumentation.html#//apple_ref/doc/uid/TP40016497-CH51-SW14">Parameters section</a></span> /// at the end. <span class="graphic">⎟</span> /// <span class="graphic">⎭</span> /// - **Note**: To print without a trailing <span class="graphic">⎫</span> /// newline, pass `terminator: ""` <span class="graphic">⎟</span> /// <span class="graphic">⎬</span><span class="commentary"> <a href="https://developer.apple.com/library/prerelease/mac/documentation/Xcode/Reference/xcode_markup_formatting_ref/SymbolDocumentation.html#//apple_ref/doc/uid/TP40016497-CH51-SW13">Symbol commands</a></span> /// - **SeeAlso**: `CustomDebugStringConvertible`, <span class="graphic">⎟</span> /// `CustomStringConvertible`, `debugPrint`. <span class="graphic">⎭</span> public func print<Target: OutputStreamType>( _ items: Any..., separator: String = " ", terminator: String = "\n")-
Используйте общепринятые элементы разметки разметки символов, чтобы добавить информацию, выходящую за рамки краткого описания, если это уместно.
-
Знайте и используйте общепринятые пункты списка с синтаксисом команд символов. Такие популярные инструменты разработки, как Xcode, предоставляют особый вид отображения для пунктов списка, начинающихся со следующих ключевых слов:
-
-
Именование
Повышение ясности использования
-
Включайте все необходимые слова для избежания неоднозначности при чтении кода, где используется имя.
Например, рассмотрите метод, удаляющий элемент по заданной позиции в коллекции.
extension List { public mutating func remove(at position: Index) -> Element } employees.remove(at: x)Если мы опустим слово
atиз сигнатуры метода, то читатель может предположить, что метод ищет и удаляет элемент, равныйx, а не используетxдля указания позиции удаляемого элемента.employees.remove(x) // unclear: are we removing x? -
Опускайте ненужные слова. Каждое слово в имени должно содержать важную информацию в месте использования.
Могут потребоваться дополнительные слова для уточнения намерения или устранения неоднозначности, но те, которые избыточны с точки зрения уже имеющейся у читателя информации, следует опустить. В частности, опускайте слова, которые просто повторяют информацию о типе.
public mutating func removeElement(_ member: Element) -> Element? allViews.removeElement(cancelButton)В этом случае слово
Elementне добавляет ничего существенного в месте вызова. Этот API был бы лучше:public mutating func remove(_ member: Element) -> Element? allViews.remove(cancelButton) // clearerИногда повторение информации о типе необходимо для избежания неоднозначности, но в целом лучше использовать слово, описывающее роль параметра, а не его тип. Подробности см. в следующем пункте.
-
Называйте переменные, параметры и связанные типы в соответствии с их ролью, а не с ограничениями их типа.
var **string** = "Hello" protocol ViewController { associatedtype **View**Type : View } class ProductionLine { func restock(from **widgetFactory**: WidgetFactory) }Переиспользование имени типа таким образом не оптимизирует ясность и выразительность. Вместо этого следует выбирать имя, отражающее роль сущности.
var **greeting** = "Hello" protocol ViewController { associatedtype **ContentView** : View } class ProductionLine { func restock(from **supplier**: WidgetFactory) }Если связанный тип настолько тесно связан с ограничением протокола, что имя протокола является ролью, избегайте коллизий, добавляя
Protocolк имени протокола:protocol Sequence { associatedtype Iterator : Iterator**Protocol** } protocol Iterator**Protocol** { ... } -
Компенсируйте слабую информацию о типе для уточнения роли параметра.
В особенности, когда тип параметра является
NSObject,Any,AnyObject, или фундаментальным типом, таким какIntилиString, информация о типе и контекст в месте использования могут не полностью передать намерение. В этом примере объявление может быть ясным, но место использования неясно.func add(_ observer: NSObject, for keyPath: String) grid.add(self, for: graphics) // vagueДля восстановления ясности, предваряйте каждый слабо типизированный параметр существительным, описывающим его роль:
func add**Observer**(_ observer: NSObject, for**KeyPath** path: String) grid.addObserver(self, forKeyPath: graphics) // clear
Стремитесь к плавному использованию
-
Предпочитайте имена методов и функций, которые в местах использования образуют грамматически корректные английские фразы.
x.insert(y, at: z) <span class="commentary">“x, insert y at z”</span> x.subviews(havingColor: y) <span class="commentary">“x's subviews having color y”</span> x.capitalizingNouns() <span class="commentary">“x, capitalizing nouns”</span>x.insert(y, position: z) x.subviews(color: y) x.nounCapitalize()Допустимо, что плавность может ухудшиться после первого-второго аргумента, если эти аргументы не являются центральными для смысла вызова:
AudioUnit.instantiate( with: description, **options: [.inProcess], completionHandler: stopProgressBar**) -
Начинайте имена методов-фабрик со слова “
make”, например,x.makeIterator(). -
Первый аргумент вызова инициализаторов и методов-фабрик не должен образовывать фразу, начинающуюся с базового имени, например,
x.makeWidget(cogCount: 47)Например, первые аргументы этих вызовов не читаются как часть одной фразы с базовым именем:
let foreground = **Color**(red: 32, green: 64, blue: 128) let newPart = **factory.makeWidget**(gears: 42, spindles: 14) let ref = **Link**(target: destination)В следующем примере автор API пытался создать грамматическую связь с первым аргументом.
let foreground = **Color(havingRGBValuesRed: 32, green: 64, andBlue: 128)** let newPart = **factory.makeWidget(havingGearCount: 42, andSpindleCount: 14)** let ref = **Link(to: destination)**На практике, данное руководство, наряду с рекомендациями по меткам аргументов, означает, что у первого аргумента будет метка, если вызов не выполняет преобразование типа с сохранением значения.
let rgbForeground = RGBColor(cmykForeground) -
Называйте функции и методы в соответствии с их побочными эффектами
-
Функции без побочных эффектов должны читаться как существительные, например,
x.distance(to: y),i.successor(). -
Функции с побочными эффектами должны читаться как императивные глагольные фразы, например,
print(x),x.sort(),x.append(y). -
Называйте пары методов `mutating`/`nonmutating` согласованно. Метод `mutating` часто имеет аналогичный по семантике вариант `nonmutating`, но который возвращает новое значение вместо обновления экземпляра на месте.
-
Когда операция естественно описывается глаголом, используйте императивную форму глагола для метода `mutating` и добавьте суффикс «ed» или «ing» для названия его `nonmutating` варианта.
Mutating Nonmutating x.sort()z = x.sorted()x.append(y)z = x.appending(y)-
Предпочтительнее называть `nonmutating` вариант с использованием прошедшего причастия глагола (обычно добавляя «ed»):
/// Reverses `self` in-place. mutating func reverse() /// Returns a reversed copy of `self`. func revers**ed**() -> Self ... x.reverse() let y = x.reversed() -
Когда добавление «ed» не является грамматически корректным, потому что глагол имеет прямой объект, назовите `nonmutating` вариант, используя настоящее причастие глагола, добавив «ing».
/// Strips all the newlines from `self` mutating func stripNewlines() /// Returns a copy of `self` with all the newlines stripped. func strip**ping**Newlines() -> String ... s.stripNewlines() let oneLine = t.strippingNewlines()
-
-
Когда операция естественно описывается существительным, используйте существительное для `nonmutating` метода и добавьте префикс «form» для названия его `mutating` варианта.
Nonmutating Mutating x = y.union(z)y.formUnion(z)j = c.successor(i)c.formSuccessor(&i)
-
-
-
Использование булевых методов и свойств должно читаться как утверждения о получателе, когда использование является не изменяющим, например,
x.isEmpty,line1.intersects(line2). -
Протоколы, описывающие, что нечто представляет собой, должны читаться как существительные (например,
Collection). -
Протоколы, описывающие возможность, должны называться с использованием суффиксов
able,ibleилиing(например,Equatable,ProgressReporting). -
Имена других типов, свойств, переменных и констант должны читаться как существительные.
Используйте терминологию правильно
- Термин искусства
- существительное - слово или фраза, имеющая точное, специализированное значение в конкретной области или профессии.
-
Избегайте неясных терминов, если более распространённое слово передает тот же смысл. Не говорите «эпидермис», если «кожа» подойдёт. Термины, используемые в специальных областях, являются важным инструментом коммуникации, но должны использоваться только для передачи критически важного смысла, который в противном случае будет утерян.
-
Придерживайтесь установленного значения, если используете термины, применяемые в специальных областях.
Единственная причина использования технического термина вместо более распространённого слова заключается в том, что он точно выражает то, что в противном случае было бы неоднозначным или неясным. Следовательно, API должен использовать этот термин строго в соответствии с его общепринятым значением.
-
Не удивляйте эксперта: любой, уже знакомый с термином, удивится и, вероятно, рассердится, если у нас, похоже, есть новое значение для него.
-
Не сбивайте с толку новичка: любой, пытающийся изучить термин, вероятно, выполнит поиск в интернете и найдёт его традиционное значение.
-
-
Избегайте сокращений. Сокращения, особенно нестандартные, являются эффективными терминами, используемыми в специальных областях, потому что понимание зависит от правильного перевода их в полную форму.
Предполагаемое значение любого используемого сокращения должно быть легко найдено с помощью поиска в интернете.
-
Следуйте прецеденту. Не оптимизируйте термины для абсолютных новичков в ущерб соответствию существующей культуре.
Лучше назвать непрерывную структуру данных
Array, чем использовать упрощённый термин, такой какList, даже если новичок может легче понять значениеList. Массивы являются фундаментальными в современной вычислительной технике, поэтому каждый программист знает — или скоро узнает — что такое массив. Используйте термин, с которым знакомы большинство программистов, и их поиски в интернете и вопросы будут вознаграждены.В определённой области программирования, такой как математика, широко используемый термин, такой как
sin(x), предпочтительнее пояснительной фразы, такой какverticalPositionOnUnitCircleAtOriginOfEndOfRadiusWithAngle(x). Обратите внимание, что в этом случае прецедент перевешивает рекомендацию избегать сокращений: хотя полное слово — этоsine, «sin(x)» используется программистами уже десятилетия, а математиками — веками.
Конвенции
Общие конвенции
-
Документируйте сложность любого вычисляемого свойства, которое не является O(1). Люди часто предполагают, что доступ к свойству не подразумевает значительных вычислений, потому что они воспринимают свойства как хранилища. Убедитесь, что вы предупреждаете их, когда это предположение может быть нарушено.
-
Предпочитайте методы и свойства свободным функциям. Свободные функции используются только в особых случаях:
-
Когда нет очевидного
self:min(x, y, z) -
Когда функция является неограниченным обобщением:
print(x) -
Когда синтаксис функции является частью установленной нотации области:
sin(x)
-
-
Следуйте правилам именования. Имена типов и протоколов —
UpperCamelCase. Всё остальное —lowerCamelCase.Аббревиатуры и сокращения, которые обычно пишутся заглавными буквами в американском английском, должны единообразно записываться с прописными или строчными буквами в соответствии с правилами именования:
var **utf8**Bytes: [**UTF8**.CodeUnit] var isRepresentableAs**ASCII** = true var user**SMTP**Server: Secure**SMTP**ServerДругие аббревиатуры должны рассматриваться как обычные слова:
var **radar**Detector: **Radar**Scanner var enjoys**Scuba**Diving = true -
Например, следующее рекомендуется, так как методы выполняют по существу одинаковые действия:
extension Shape { /// Returns `true` if `other` is within the area of `self`; /// otherwise, `false`. func **contains**(_ other: **Point**) -> Bool { ... } /// Returns `true` if `other` is entirely within the area of `self`; /// otherwise, `false`. func **contains**(_ other: **Shape**) -> Bool { ... } /// Returns `true` if `other` is within the area of `self`; /// otherwise, `false`. func **contains**(_ other: **LineSegment**) -> Bool { ... } }И поскольку геометрические типы и коллекции являются разными областями, это также допустимо в одной программе:
extension Collection where Element : Equatable { /// Returns `true` if `self` contains an element equal to /// `sought`; otherwise, `false`. func **contains**(_ sought: Element) -> Bool { ... } }Однако, эти
indexметоды имеют разные семантики и должны быть названы по-другому:extension Database { /// Rebuilds the database's search index func **index**() { ... } /// Returns the `n`th row in the given table. func **index**(_ n: Int, inTable: TableID) -> TableRow { ... } }Наконец, избегайте «перегрузки по типу возвращаемого значения», поскольку это приводит к неоднозначности при наличии вывода типов.
extension Box { /// Returns the `Int` stored in `self`, if any, and /// `nil` otherwise. func **value**() -> Int? { ... } /// Returns the `String` stored in `self`, if any, and /// `nil` otherwise. func **value**() -> String? { ... } }
Параметры
func move(from **start**: Point, to **end**: Point)
-
Выбирайте имена параметров для документации. Несмотря на то, что имена параметров не отображаются в месте использования функции или метода, они играют важную объяснительную роль.
Выбирайте эти имена, чтобы сделать документацию лёгкой для чтения. Например, эти имена делают документацию естественной для чтения:
/// Return an `Array` containing the elements of `self` /// that satisfy `**predicate**`. func filter(_ **predicate**: (Element) -> Bool) -> [Generator.Element] /// Replace the given `**subRange**` of elements with `**newElements**`. mutating func replaceRange(_ **subRange**: Range<Index>, with **newElements**: [E])Эти же имена, однако, делают документацию неудобной и грамматически неправильной:
/// Return an `Array` containing the elements of `self` /// that satisfy `**includedInResult**`. func filter(_ **includedInResult**: (Element) -> Bool) -> [Generator.Element] /// Replace the **range of elements indicated by `r`** with /// the contents of `**with**`. mutating func replaceRange(_ **r**: Range<Index>, **with**: [E]) -
Используйте параметры с значениями по умолчанию, когда это упрощает часто используемые случаи. Любой параметр с одним часто используемым значением является кандидатом для значения по умолчанию.
Параметры по умолчанию улучшают читаемость, скрывая нерелевантную информацию. Например:
let order = lastName.compare( royalFamilyName**, options: [], range: nil, locale: nil**)может стать гораздо более простым:
let order = lastName.**compare(royalFamilyName)**Параметры по умолчанию, как правило, предпочтительнее использования семейств методов, потому что они накладывают меньшую когнитивную нагрузку на всех, кто пытается понять API.
extension String { /// *...description...* public func compare( _ other: String, options: CompareOptions **= []**, range: Range<Index>? **= nil**, locale: Locale? **= nil** ) -> Ordering }Вышесказанное может быть не простым, но оно намного проще, чем:
extension String { /// *...description 1...* public func **compare**(_ other: String) -> Ordering /// *...description 2...* public func **compare**(_ other: String, options: CompareOptions) -> Ordering /// *...description 3...* public func **compare**( _ other: String, options: CompareOptions, range: Range<Index>) -> Ordering /// *...description 4...* public func **compare**( _ other: String, options: StringCompareOptions, range: Range<Index>, locale: Locale) -> Ordering }Каждый член семейства методов должен быть отдельно задокументирован и понят пользователями. Чтобы выбрать между ними, пользователю нужно понять все из них, а иногда неожиданные отношения — например,
foo(bar: nil)иfoo()— не всегда являются синонимами — делают этот процесс выявления незначительных различий в большей части идентичной документации утомительным. Использование одного метода с параметрами по умолчанию обеспечивает гораздо лучший опыт программиста. -
Предпочитайте размещать параметры с значениями по умолчанию в конце списка параметров. Параметры без значений по умолчанию, как правило, более важны для семантики метода и обеспечивают стабильный начальный шаблон использования, где методы вызываются.
-
Если ваш API будет использоваться в рабочей среде, предпочтительнее
#fileIDвместо альтернатив.#fileIDэкономит место и защищает конфиденциальность разработчиков. Используйте#filePathв API, которые никогда не используются конечными пользователями (например, вспомогательные тесты и скрипты), если полный путь упростит рабочие процессы разработки или будет использоваться для ввода-вывода файлов. Используйте#fileдля сохранения обратной совместимости со Swift 5.2 или более ранними версиями.
Метки аргументов
func move(**from** start: Point, **to** end: Point)
x.move(**from:** x, **to:** y)
-
Опускайте все метки, когда аргументы невозможно различить, например,
min(number1, number2),zip(sequence1, sequence2). -
В инициализаторах, выполняющих преобразования типов с сохранением значения, опускайте метку первого аргумента, например,
Int64(someUInt32)Первый аргумент всегда должен быть источником преобразования.
extension String { // Convert `x` into its textual representation in the given radix init(**_** x: BigInt, radix: Int = 10) <span class="commentary">← Note the initial underscore</span> } text = "The value is: " text += **String(veryLargeNumber)** text += " and in hexadecimal, it's" text += **String(veryLargeNumber, radix: 16)**Однако при «сужающих» преобразованиях типов рекомендуется метка, описывающая сужение.
extension UInt32 { /// Creates an instance having the specified `value`. init(**_** value: Int16) <span class="commentary">← Widening, so no label</span> /// Creates an instance having the lowest 32 bits of `source`. init(**truncating** source: UInt64) /// Creates an instance having the nearest representable /// approximation of `valueToApproximate`. init(**saturating** valueToApproximate: UInt64) }Преобразование типа с сохранением значения — это мономорфизм, т.е. каждое различие в значении источника приводит к различию в значении результата. Например, преобразование из
Int8вInt64сохраняет значение, потому что каждое раздельноеInt8значение преобразуется в раздельноеInt64значение. Однако преобразование в обратном направлении не может сохранять значение:Int64имеет больше возможных значений, чем может быть представлено вInt8.Примечание: возможность извлечения исходного значения не влияет на то, сохраняет ли преобразование значение.
-
Если первый аргумент является частью предложного оборота, используйте метку для этого аргумента. Метка аргумента обычно начинается с предлога, например,
x.removeBoxes(havingLength: 12).Исключение возникает, когда первые два аргумента представляют части одного абстракции.
a.move(**toX:** b, **y:** c) a.fade(**fromRed:** b, **green:** c, **blue:** d)В таких случаях начинайте метку аргумента после предлога, чтобы сохранить ясность абстракции.
a.moveTo(**x:** b, **y:** c) a.fadeFrom(**red:** b, **green:** c, **blue:** d) -
В противном случае, если первый аргумент является частью грамматического оборота, опустите его метку, присоединив любые предшествующие слова к основному имени, например,
x.addSubview(y)Это руководство подразумевает, что если первый аргумент не является частью грамматического оборота, он должен иметь метку.
view.dismiss(**animated:** false) let text = words.split(**maxSplits:** 12) let studentsByName = students.sorted(**isOrderedBefore:** Student.namePrecedes)Важно, чтобы оборот передавал правильное значение. Следующее было бы грамматически правильным, но выражало бы неверное значение.
view.dismiss(false) <span class="commentary">Don't dismiss? Dismiss a Bool?</span> words.split(12) <span class="commentary">Split the number 12?</span>Также обратите внимание, что аргументы с значениями по умолчанию можно опустить, и в этом случае они не являются частью грамматического оборота, поэтому всегда должны иметь метки.
-
Укажите метки для всех остальных аргументов.
Специальные инструкции
-
Укажите метки для членов кортежей и имена параметров замыканий там, где они появляются в вашем API.
Эти имена обладают пояснительной силой, могут быть упомянуты в комментариях к документации и обеспечивают выразительный доступ к членам кортежей.
/// Ensure that we hold uniquely-referenced storage for at least /// `requestedCapacity` elements. /// /// If more storage is needed, `allocate` is called with /// **`byteCount`** equal to the number of maximally-aligned /// bytes to allocate. /// /// - Returns: /// - **reallocated**: `true` if a new block of memory /// was allocated; otherwise, `false`. /// - **capacityChanged**: `true` if `capacity` was updated; /// otherwise, `false`. mutating func ensureUniqueStorage( minimumCapacity requestedCapacity: Int, allocate: (_ **byteCount**: Int) -> UnsafePointer<Void> ) -> (**reallocated:** Bool, **capacityChanged:** Bool)Имена, используемые для параметров замыканий, должны выбираться так же, как имена параметров для функций верхнего уровня. Метки для аргументов замыканий, которые появляются в месте вызова, не поддерживаются.
-
Обращайте особое внимание на неявное полиморфизм (например,
Any,AnyObjectи неявные параметры обобщения), чтобы избежать неоднозначности в наборах перегрузок.Например, рассмотрите следующий набор перегрузок:
struct Array<Element> { /// Inserts `newElement` at `self.endIndex`. public mutating func append(_ newElement: Element) /// Inserts the contents of `newElements`, in order, at /// `self.endIndex`. public mutating func append<S: SequenceType>(_ newElements: S) where S.Generator.Element == Element }Эти методы образуют семантическую семью, и типы аргументов на первый взгляд резко различаются. Однако, когда
ElementявляетсяAny, один элемент может иметь тот же тип, что и последовательность элементов.var values: [Any] = [1, "a"] values.append([2, 3, 4]) // [1, "a", [2, 3, 4]] or [1, "a", 2, 3, 4]?Чтобы устранить неоднозначность, более явно укажите имя второй перегрузки.
struct Array { /// Inserts `newElement` at `self.endIndex`. public mutating func append(_ newElement: Element) /// Inserts the contents of `newElements`, in order, at /// `self.endIndex`. public mutating func append<S: SequenceType>(**contentsOf** newElements: S) where S.Generator.Element == Element }Обратите внимание, как новое имя лучше соответствует комментариям к документации. В этом случае сам процесс написания комментариев к документации обратил внимание автора API на эту проблему.
The Swift Programming Language, Copyright © 2014-2025 Apple Inc.
Swift and the Swift logo are trademarks of Apple Inc.
Documentation for Swift 6.0.3
https://www.swift.org/documentation/api-design-guidelines/