Optionals в Swift: ?, ! и безопасное извлечение

Optionals в Swift нужны, чтобы отсутствие значения было видно в типе. Переменная либо содержит значение, либо содержит nil, и Swift заставляет вас обработать этот факт до использования. В этом уроке разберем String?, if let, guard let, optional chaining и опасный force unwrap через !

К концу урока вы напишете функцию, которая безопасно делает подпись пользователя и не падает, если имени нет

Что получится в конце

Пример:

func userLabel(name: String?) -> String {
    guard let name, !name.trimmingCharacters(in: .whitespaces).isEmpty else {
        return "User: Guest"
    }

    return "User: \(name)"
}

print(userLabel(name: "Dinar"))
print(userLabel(name: nil))
print(userLabel(name: "   "))

Ожидаемый вывод:

User: Dinar
User: Guest
User: Guest

Здесь name может отсутствовать, но программа не падает и возвращает понятный запасной вариант

Что такое optional

Обычная строка:

let name: String = "Swift"

не может быть nil

Optional-строка:

let name: String? = nil

может содержать строку или nil. Вопросительный знак меняет тип. String и String? — разные вещи

Если попытаться сразу взять:

name.count

компилятор остановит код. Он не знает, есть ли внутри строка. Сначала нужно безопасно извлечь значение

if let

if let достает значение, если оно есть:

let nickname: String? = "Dinar"

if let nickname {
    print("Nickname: \(nickname)")
} else {
    print("No nickname")
}

Внутри блока if nickname уже обычный String, а не String?

Этот способ хорош, когда у вас есть два нормальных сценария: значение есть и значения нет

guard let

guard let удобен, когда без значения функция не может продолжать работу:

func printName(_ name: String?) {
    guard let name else {
        print("No name")
        return
    }

    print(name)
}

После guard переменная доступна ниже как non-optional. Код получается ровнее: плохой сценарий выходит раньше, основной путь остается без лишней вложенности

В реальных функциях guard часто используют для проверки входных данных, URL, id, токенов и обязательных зависимостей

Optional chaining

Optional chaining позволяет вызвать свойство или метод только если значение есть:

let length = name?.count

Если name содержит строку, length будет optional-числом с длиной. Если name == nil, length будет nil

Для запасного значения используйте nil-coalescing:

let length = name?.count ?? 0

Теперь length — обычный Int, потому что на случай nil есть 0

Почему ! опасен

name!.count

говорит Swift: «я уверен, что здесь не nil». Если вы ошиблись, приложение упадет

Иногда ! встречается в шаблонах, legacy-коде или местах, где значение действительно гарантировано внешней системой. Но для новичка правило жесткое: не ставьте !, чтобы просто заткнуть компилятор

Перед ! проверьте:

  1. Можно ли использовать if let
  2. Можно ли использовать guard let
  3. Можно ли дать fallback через ??
  4. Можно ли изменить тип, чтобы значение не было optional

Если да, force unwrap не нужен

Домашка: безопасный промокод

Создайте функцию:

func promoLabel(code: String?) -> String

Правила:

  • если code == nil, вернуть "No promo"
  • если строка пустая после trimming, вернуть "No promo"
  • иначе вернуть "Promo: CODE"

Проверьте:

print(promoLabel(code: nil))
print(promoLabel(code: ""))
print(promoLabel(code: " SAVE10 "))

Дополнительная задача: сделайте код uppercase перед выводом

Mini-check для optionals

Когда видите String?, задайте три вопроса:

  • где значение может стать nil
  • кто обязан обработать отсутствие
  • есть ли рядом подозрительный !

Если optional передается через много функций без обработки, код становится мутным. Лучше обработать отсутствие там, где понятно бизнес-решение: показать гостя, вернуть ошибку, остановить функцию или дать fallback

Частые ошибки и порядок проверки

Путаете String и String? Это разные типы. Optional нужно извлекать перед обычным использованием

Ставите ! без причины Force unwrap может уронить приложение. Используйте его только там, где гарантия действительно железная

Даете неверный fallback ?? "" не всегда хорош. Пустая строка может скрыть ошибку данных

Делаете слишком глубокие if let Если вложенность растет, попробуйте guard let для раннего выхода

Как выбирать между if let, guard let и ??

У optionals есть несколько рабочих приемов, и новичок часто пытается выбрать один “самый правильный”. На практике выбор зависит от формы кода

Используйте if let, когда значение нужно только внутри небольшой ветки:

let email: String? = "dinar@example.com"

if let email {
    print("Send email to \(email)")
} else {
    print("Email is missing")
}

Используйте guard let, когда без значения функция не может нормально продолжаться:

func showProfile(name: String?) {
    guard let name else {
        print("Name is missing")
        return
    }

    print("Profile: \(name)")
}

Используйте ??, когда нужен короткий запасной вариант:

let city: String? = nil
print(city ?? "City not set")

И почти никогда не начинайте с !. Force unwrap уместен только когда вы действительно доказали, что nil невозможен, а не просто хотите быстрее убрать ошибку компилятора

Практика: форма пользователя

Представьте, что пользователь заполняет профиль. Имя обязательно, никнейм может отсутствовать, возраст приходит строкой из поля ввода:

let rawName: String? = "Dinar"
let nickname: String? = nil
let rawAge: String? = "32"

guard let name = rawName else {
    fatalError("Name is required")
}

let displayName = nickname ?? name
let age = Int(rawAge ?? "")

print("Display name: \(displayName)")

if let age {
    print("Age: \(age)")
} else {
    print("Age is not valid")
}

Здесь есть сразу три типичных ситуации: обязательное значение через guard, запасной вариант через ?? и преобразование строки в число, которое тоже возвращает optional

Поменяйте rawAge на "thirty" и запустите код. Программа не должна падать. Она должна попасть в ветку Age is not valid

Почему Int("32") возвращает Optional

На первый взгляд странно: почему преобразование строки в число не возвращает обычный Int. Ответ простой: не каждая строка является числом

let first = Int("32")
let second = Int("Swift")

first будет Optional(32), а second будет nil. Swift не может молча превратить слово Swift в число, поэтому честно показывает в типе: результат может отсутствовать

Именно поэтому optionals так важны в реальном коде. Данные приходят из форм, URL, JSON, словарей, настроек и пользовательского ввода. Там отсутствие значения — нормальная часть жизни программы

Мини-правило для ревью своего кода

Перед тем как оставить !, задайте себе три вопроса:

  1. Кто гарантирует, что здесь не будет nil
  2. Что увидит пользователь, если гарантия нарушится
  3. Можно ли заменить ! на if let, guard let или ??

Если на первый вопрос нет четкого ответа, force unwrap лучше убрать. Это не делает код длиннее “ради красоты”, это делает отказ программы управляемым

Optional chaining на реальном примере

Optional chaining помогает аккуратно пройти по цепочке, где любое звено может отсутствовать:

struct Profile {
    let email: String?
}

struct User {
    let profile: Profile?
}

let user = User(profile: Profile(email: "dinar@example.com"))
let emailLength = user.profile?.email?.count

print(emailLength ?? 0)

Если profile или email будет nil, выражение не упадет. Оно вернет optional-результат, который можно обработать через ?? или if let

Этот прием часто встречается при работе с вложенными данными из API. Но если цепочка становится слишком длинной, лучше остановиться и вынести проверку в отдельные понятные шаги

Что может быть еще интересно по этой теме

Optionals похожи на null safety в Kotlin? Да по идее: отсутствие значения видно в типе. Но синтаксис и привычные idioms отличаются

Почему Swift вообще допускает nil? Потому что отсутствие значения реально бывает: пользователь не ввел поле, URL не собрался, словарь не нашел ключ. Swift заставляет обработать это явно

Когда использовать guard let? Когда без значения дальше нет смысла продолжать. Это делает основной путь функции прямым

Что дальше после optionals? Функции, struct и class. Там optional-поля и безопасное извлечение будут встречаться постоянно

Что почитать дальше по Swift

Оцените статью
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x