Лекция 14. Многопоточность, Обработка ошибок. CS193P Spring 2023.

Ниже представлен небольшой фрагмент Лекции 14 Стэнфордского курса CS193P Весна 2023 «Разработка iOS приложений с помощью SwiftUI«.
Полный русскоязычный неавторизованный конспект Лекции 14 в формате Google Doc и в виде PDF-файла, который можно скачать и использовать offline, доступны здесь.
Код находится на GitHub.

С полным перечнем Лекций и Домашних Заданий на русском языке можно познакомиться здесь.

. . . . . . . . . . . . . .

Демонстрационный пример: конечный автомат Background

Итак, давайте поговорим о том, как я собираюсь загружать фоновое изображение background концептуально. Я собираюсь использовать конечный автомат (state machine).
Сколько человек реально написали код, который использует конечный автомат (state machine)?
Ну, не так уж и много. Интересно.
Итак идея программирования конечных автоматов заключается в том, что я собираюсь подумать обо всех состояниях (states), в которых я могу оказаться, и выполняю некоторый процесс. На самом деле я собираюсь закодировать их и делать каким-то образом пометки в моем коде для каждого из этих шагов. И отличный ТИП данных для конечных автоматов — это перечисление enum. Потому что по определению вы перемещаетесь по этим различимым состояниях (states), a перечисление enum как раз и представляет различимые состояния (states).
Вот что представляет собой мой конечный автомат (state machine). Я размещу весь этот код отдельно, в разделе // MARK: — Background Image. Это будет перечисление enum с именем Background:

И каковы состояния (states) выборки чего-либо из Интернета?
Вы можете быть в состоянии none, то есть вы ничего не делаете. Нет, фонового изображения  background, которое Drag & Drop (перетаскивается и сбрасывается), вы просто нигде:

Возможно, я сейчас выбираю данные из Интернета, так что я мог бы назвать состояние fetching. Но когда я выбираю, возможно, я хочу знать URL, по которому идет выборка, и я могу получить URL в качестве ассоциированных данных:

Итак, я сделал выборку по этому URL, и либо у меня есть изображение, либо мне не удается этого сделать из-за какого-то сетевого сбоя или чего-то в этом роде.
В результате у меня действительно добавляются еще два состояния в моем конечном автомате.
В случае успешного завершения выборки я нахожусь в состоянии found с изображением UIImage, в противном случае я нахожусь в состоянии failed, и в этом случае я мог бы сохранить ошибку, которую я получил:

Для простоты я сохраняю ошибку в виде String, которая описывает, вероятно, localizedDescription полученной ошибки.
Итак, это состояния моего конечного автомата.
Я собираюсь пройти через эти состояния шаг за шагом и выполнить все необходимые действия.
Теперь, поскольку у меня есть этот маленький конечный автомат enum Background, я также создал небольшие удобные функции.
Давайте посмотрим на них:

Это маленькие переменные var, которые просто возвращают ассоциированные данные определенного состояния моего конечный автомат enum Background, если я нахожусь в этом состоянии.
Итак, вы видите вычисляемую переменную var uiImage:

Внутри мы переключаемся switch по self, и если я нахожусь в состоянии found с ассоциированным значение uiImage в виде выбранного изображения, то возвращает это изображение uiImage. В противном случае он просто возвращает nil.
То же самое происходит с получением URL-адреса urlBeingFetched, по которому осуществляется выборка:

Внутри мы переключаемся switch по self, и если я нахожусь в состоянии fetching с ассоциированным значение url, то возвращает этот url, по которому идет выборка изображения. В противном случае он просто возвращает nil.
То же самое с причиной неудачной выборки failureReason:

Если я нахожусь в состоянии failed, дайте мне причину reason моей неудачи.
Это всего лишь удобные переменные.
Так что я могу просто спросить, какое у меня фоновое изображение uiImage прямо сейчас?
И это будет nil, если у меня его просто нет. Если я не в состоянии found, когда я получаю изображение uiImage, a в любом другом состоянии: none или fetching или failed, я также получу nil.
Я разместил также маленькую Bool переменную var isFetching:

Эта переменная равна true только в том случае, когда мой urlBeingFetched не равен nil, то есть когда я действительно выбираю изображение из Интернета. Потому что когда я нахожусь; в состоянии fetching, у меня есть URL-адрес, и это единственное состояние, когда у меня есть этот URL-адрес.
Теперь, когда у меня есть мой компактный конечный автомат enum Background, я собираюсь избавиться от переменной var background, я закомментирую это, чтобы запомнить, как это было:

Вместо этого я собираюсь создать новую @Published переменную var, которую назову background, её ТИП будет Background и её начальное значение будет .none:

Я делаю её @Published, потому что именно так мой UI будет видеть фоновое изображение background. UI всегда имеет возможность увидеть, в каком состоянии я нахожусь, потому что смотрите — я даю ему это перечисление enum Background, в котором вы также можете использовать удобные функции.

Читать далее

Лекция 13. Представляющие (Presenting) Views. Навигация. CS193P Spring 2023.

Ниже представлен небольшой фрагмент Лекции 13 Стэнфордского курса CS193P Весна 2023 «Разработка iOS приложений с помощью SwiftUI«.
Полный русскоязычный неавторизованный конспект Лекции 13 в формате Google Doc и в виде PDF-файла, который можно скачать и использовать offline, доступны здесь.
Код находится на GitHub.

С полным перечнем Лекций и Домашних Заданий на русском языке можно познакомиться здесь.

. . . . . . . . . . . . . .

TextField и @Binding в действии

Следующее, самое важное — я хочу иметь возможность редактировать название палитры, а также добавлять эмодзи (смайлики).
Как мне сделать это редактируемым текстовым полем?
Редактируемые текстовые поля в Swift — это View отличные от обычного текста Text. Вместо Text они называются TextField.

TextField имеет два аргумента.
Первый аргумент — это то, что мы называем Placeholder (Заполнитель) текста или слово, которое можно использовать, чтобы помочь пользователю понять, о чем мы здесь просим.
Второй аргумент, он называется text — это тот текст, который мы редактируем внутри этого текстового поля TextField.
И еще этот второй аргумент является привязкой Binding.

Если подумать о том, что здесь происходит, то у нас есть текстовое поле TextField, в котором текстом text является “Vehicles”. Мы хотим иметь возможность передать текстовому полю “Vehicles” как начальное значение, и всякий раз, когда что-то меняется в нем, мы хотим знать об этом.
И то, как мы собираемся это сделать, заключается в создании единственного “источника истины” (single source of truth) для этого текста text. Мы будем делать это с помощью привязки Binding.
В частности, этот второй аргумент text текстового поля TextField является привязкой Binding.
TextField знает, что он не хочет поддерживать копию того, что редактируется, он хочет редактировать эту вещь напрямую. Поэтому он просит вас дать ему привязка Binding к этому “источнику истины” (source of truth).
Ну a что является “источником истины” (source of truth) для этого palette.name?
Он находится в нашей ViewModel, в нашем PaletteStore. Следовательно, нам нужно дать здесь нашему TextField обратную привязку Binding к нашей ViewModel.
Для этого нам нужна привязка Binding к палитре palette, которую нам дали отредактировать в верхней части нашего PaletteEditor:

Сделав эту переменную var @Binding, мы заставляем того, кто создаст этот PaletteEditor, дать нам привязку Binding к “источнику истины” (source of truth) для этой палитры palette.
Теперь каждый раз, когда мы ссылаемся на эту палитру palette где угодно в нашем коде здесь, в редакторе PaletteEditor, на самом деле мы будем ссылаться в обратном порядке на палитру в нашей ViewModel.
И мы также можем использовать эту привязку Binding для передачи привязки Binding к имени палитры palette.name в нашем TextField:

Это потому, что для $, то есть projectedValue, для @Binding — это еще одна привязка Binding к той привязке @Binding.
Итак, $palette.name здесь означает привязку Binding к этой @Binding var palette, которая будет привязана в обратный порядке в конечном итоге к нашей ViewModel. Теперь наше текстовое поле TextField будет редактировать имя name палитры palette напрямую в ViewModel.
Вы видите, что эти $, эти привязки Binding, проходят через всю нашу систему Views.

Читать далее