1. Углубляемся в интерполяцию
Когда программа что-то сообщает пользователю, она обычно собирает фразу из кусочков: немного текста, какие-то значения переменных, иногда результаты вычислений. В Swift это удобно делать через интерполяцию — вставку значений внутрь строки.
Важно держать простую модель: у вас есть шаблон сообщения, и вы подставляете в него значения, а не «конструируете строку как конструктор LEGO из сотни деталей».
Базовый синтаксис: "\(…)"
Интерполяция в Swift выглядит так: внутри строкового литерала пишем \(выражение). Выражением может быть переменная, число, результат арифметики, даже тернарный оператор — главное, чтобы получился какой-то выводимый результат.
let user = "Alice"
let points = 42
print("User \(user) has \(points) points.") // User Alice has 42 points.
Обратите внимание: вы не делаете String(points) для перевода типа в строку — Swift сам знает, как превратить многие значения в текст для вывода.
Внутри \(…) можно писать выражения
Это удобно, но есть нюанс: если выражение сложное, строка становится трудно читаемой. Начинайте с простого — так проще отлаживать.
let price = 3
let count = 5
print("Total: \(price * count)") // Total: 15
Если выражение становится «простынёй», лучше вынести расчёт в let, а в строку вставить уже готовый результат. Это не «лишний код», это забота о вашем будущем.
Маленькая полезная внутренняя деталь
Если вам интересно, что происходит «под капотом»: интерполяция в Swift — это не магия, а механизм, который компилятор разворачивает в последовательность операций «добавь кусочек текста» и «добавь значение». В стандартной библиотеке это описывается через специальные методы вроде appendInterpolation, которые компилятор использует при сборке строки.
Вам не нужно это запоминать как API, но полезно понимать идею: интерполяция — это правильный, встроенный способ собрать строку из разных значений.
Конкатенация +: когда можно, но лучше не надо
Конкатенация — это «склейка» строк оператором +. Она существует, и иногда её используют, но в реальном коде очень быстро превращается в кашу: вы начинаете вручную расставлять пробелы, добавлять String(...) для чисел, и через пять минут хочется сделать вид, что этого писали не вы.
Интерполяция почти всегда читаемее, потому что в ней сразу видно шаблон сообщения.
let name = "Alice"
let points = 42
let message1 = "User " + name + " has " + String(points) + " points."
print(message1) // User Alice has 42 points.
let message2 = "User \(name) has \(points) points."
print(message2) // User Alice has 42 points.
Во втором варианте меньше шума, меньше шансов забыть пробел, и нет ручного String(points).
Если вы собираете фразу для человека — берите интерполяцию. Конкатенация остаётся как запасной инструмент: например, когда вы уже работаете со строками как с «кирпичиками», а не как с предложениями.
2. Пробелы и пунктуация в шаблонах
Когда вы начинаете активно использовать интерполяцию, внезапно выясняется, что самая частая ошибка новичка — вовсе не синтаксис, а… пробелы. Программа выполняется, компилируется, всё «правильно», но вывод выглядит как будто текст склеили суперклеем: слова прилипли друг к другу, запятые уехали, а пользователь читает это и грустит.
Поэтому здесь мы сознательно тренируемся видеть пробелы как часть сообщения.
Пробелы — это часть шаблона, а не часть значения
Значение name не обязано знать, что вы хотите пробел перед ним. Пробел — это ваша ответственность в строке-шаблоне.
let name = "Bob"
print("Hello,\(name)!") // Hello,Bob!
print("Hello, \(name)!") // Hello, Bob!
Вторая строка отличается всего одним пробелом, но выглядит как нормальная человеческая фраза.
Пунктуация должна быть «пришита» к правильному месту
Практическое правило: запятая и точка обычно «прилипают» к слову слева, а пробел — стоит после запятой. И если вы пишете шаблон, то и соблюдайте это в шаблоне.
let city = "Paris"
let year = 2026
print("City: \(city), year: \(year).") // City: Paris, year: 2026.
Переносы строк: лучше управлять ими явно
Если вы выводите несколько строк, иногда проще сделать несколько print, а иногда — один print с "\n". Но даже если вы делаете несколько print, всё равно думайте о «форме» вывода: где заголовок, где пустая строка, где данные.
let title = "Report"
let items = 3
print(title) // Report
print("Items: \(items)") // Items: 3
print("---") // ---
3. print как мини‑форматтер
Иногда вам не хочется собирать одну длинную строку. Например, вы выводите несколько значений подряд и хотите контролировать, чем они разделяются и как заканчивается вывод.
В Swift print умеет:
- печатать несколько аргументов за один вызов;
- настраивать разделитель через separator:;
- управлять окончанием вывода через terminator:.
Несколько значений и separator:
По умолчанию print ведёт себя так: между аргументами ставит пробел, а в конце печатает перевод строки. Это можно воспринимать как «нормальный человеческий вывод».
Небольшая табличка, чтобы закрепить:
| Что настраиваем | Параметр print | Значение по умолчанию | Что означает |
|---|---|---|---|
| Разделитель между аргументами | |
|
Чем разделять print(a, b, c) |
| Окончание вывода | |
|
Чем закончить вывод (обычно новая строка) |
Пример: несколько значений и красивый разделитель:
let a = 10
let b = 20
let sum = a + b
print("a=", a, "b=", b, "sum=", sum, separator: " ")
// a= 10 b= 20 sum= 30
Да, тут получаются пробелы после = — потому что "a=" и a печатаются как два отдельных аргумента. Если хотите без пробелов, либо меняйте подход (делайте интерполяцию), либо подбирайте separator:.
let a = 10
let b = 20
print("a=\(a)", "b=\(b)", separator: ", ") // a=10, b=20
Здесь мы соединили «ярлык и значение» через интерполяцию, а разделение пар сделали через separator: ", ".
Когда вы хотите, чтобы данные визуально читались как колонки, вертикальная черта — простой и понятный разделитель:
let name = "Bob"
let role = "Reader"
let id = 7
print(name, role, id, separator: " | ") // Bob | Reader | 7
terminator: и вывод в одну строку
Очень частая ситуация в консольных программах: вы хотите напечатать приглашение к вводу, чтобы курсор остался на той же строке. Или хотите вывести «Loading...done» красивым способом.
По умолчанию print всегда завершает строку символом Enter, но terminator: позволяет это изменить.
Приглашение к вводу на той же строке
print("Enter your name: ", terminator: "")
let name = readLine() ?? "Unknown"
print("Hello, \(name)!") // (например) Hello, Alice!
Здесь ключевой момент: мы печатаем приглашение без перевода строки, чтобы пользователь вводил имя прямо после двоеточия.
«Два print — одна строка»
print("Loading", terminator: "")
print("...", terminator: "")
print("done") // done (и тут перевод строки)
Это выглядит смешно простым, но такой приём часто используется в маленьких CLI‑утилитах: он позволяет «нарастить» одну строку по частям.
4. String(repeating:count:) для линий и отступов
Когда вы делаете вывод красивым, вам хочется рисовать разделители: линии, отступы, рамки, простейшие «прогресс‑бары». И тут новичок обычно делает страшное: начинает считать пробелы глазами и копировать "--------" вручную.
В Swift для повторяющихся фрагментов есть String(repeating:count:).
Отступ как значение
let indent = String(repeating: " ", count: 4)
print("\(indent)Item 1") // Item 1
print("\(indent)Item 2") // Item 2
Отступ — это такие же данные, как имя пользователя. Мы создаём его один раз и используем сколько нужно.
Разделительная линия
let line = String(repeating: "-", count: 20)
print(line) // --------------------
print("Menu")
print(line) // --------------------
Читать вывод сразу легче: глаз цепляется за структуру.
Простейший «бар» прогресса
Пока мы не изучаем ничего сложного, но уже можем сделать «визуальный индикатор» в одну строку.
let done = 3
let total = 10
let filled = String(repeating: "#", count: done)
let empty = String(repeating: ".", count: total - done)
print("[\(filled)\(empty)] \(done)/\(total)") // [###.......] 3/10
В этом примере особенно видно, почему интерполяция хороша: строка выглядит как шаблон того, что мы хотим увидеть.
5. Собираем аккуратный вывод мини‑приложения
Сейчас мы сделаем маленькую консольную заготовку, которая похожа на «микро‑CLI»: показывает заголовок, печатает подсказки, читает ввод и отвечает. Никаких сложных структур данных — только аккуратный вывод.
Это важный момент: пользователь судит программу по тому, что он видит. Даже если внутри у вас гениальный алгоритм, но программа пишет enterNameplz, доверия не будет.
Шаг 1: заголовок и приветствие
let appName = "TinyLibrary"
let version = "0.1"
let line = String(repeating: "=", count: 30)
print(line) // ==============================
print("\(appName) v\(version)") // TinyLibrary v0.1
print("Welcome! Type a command below.") // Welcome! Type a command below.
print(line) // ==============================
Обратите внимание на мелочь: "v\(version)" даёт ровно тот формат, который мы хотим видеть. Если бы мы печатали "v" + version, это было бы не трагедией, но интерполяция читается проще.
Шаг 2: приглашение к команде без переноса строки
print("Command: ", terminator: "")
let command = readLine() ?? ""
print("You typed: \"\(command)\"") // You typed: "help"
Здесь мы сделали две «вежливости» сразу: ввод на той же строке и кавычки вокруг команды, чтобы пользователю было видно, что именно он ввёл (иногда это помогает заметить лишний пробел).
Шаг 3: «псевдо‑помощь» в стиле CLI
Пока мы не разбираем команды по частям (это отдельная тема), но мы можем красиво вывести подсказку.
let indent = String(repeating: " ", count: 2)
print("Available commands:")
print("\(indent)help - show this message")
print("\(indent)exit - quit the program")
Такие «ровные» отступы создают ощущение аккуратности.
Шаг 4: один print, много значений, контролируем разделитель
Иногда хочется собрать строку как «лог‑сообщение»: уровень, категория, текст. separator: делает это просто и явно.
let level = "INFO"
let category = "CLI"
let message = "Waiting for command"
print("[\(level)]", "[\(category)]", message, separator: " ")
// [INFO] [CLI] Waiting for command
Если захотите, вы можете заменить разделитель на " | " — и стиль станет более «табличным». Главное, что у вас есть контроль.
6. Типичные ошибки
Ошибка №1: “Где мой пробел?” — слипшийся текст из-за неверного шаблона.
Когда вы пишете "Hello,\(name)!", программа честно делает ровно то, что вы попросили: запятая и имя стоят вплотную. Лечится это не «какой-то особой интерполяцией», а дисциплиной шаблона: пробел — это символ, который вы должны поставить сами в нужном месте ("Hello, \(name)!").
Ошибка №2: попытка запихнуть всю логику внутрь \(…).
Swift позволяет вставлять выражения, и это удобно. Но если в интерполяции появляется три тернарных оператора, четыре сложения и одно «на всякий случай умножение» — строка становится нечитаемой. Обычно лучше вынести расчёт в отдельный let с хорошим именем, а в шаблон подставить результат. Вы удивитесь, насколько меньше ошибок начнёте делать.
Ошибка №3: путаница между “разделитель между аргументами” и “разделитель внутри текста”.
separator: работает только между аргументами print(a, b, c). Он не вставляет пробелы «внутрь» аргумента. Поэтому если вы печатаете "a=" и a как разные аргументы, то пробел появится (если separator: — пробел). Если вам нужно a=10 без пробела, проще собрать аргумент через интерполяцию: "a=\(a)".
Ошибка №4: забыли, что terminator: "" отключает перевод строки.
Это одна из тех ошибок, которые выглядят как «почему всё сломалось», хотя на самом деле всё логично: вы сами отменили \n. Если после приглашения к вводу вы вдруг видите, что следующий вывод «прилип» — проверьте, где последний раз вы ставили terminator: "", и добавьте перевод строки там, где он реально нужен.
Ошибка №5: “Нарисую линию руками” и получу пляску дефисов.
Когда вы пишете "----------------------" вручную, вы почти гарантированно где-то ошибётесь (или вам потом понадобится длина 40, и вы начнёте пересчитывать). String(repeating:count:) решает проблему красиво: длина линии становится числом, а не квестом на внимательность.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ