И первый взгляд на оболочку свойства NotificationCenterPublisher

Как следует из названия, оболочка свойства — это слой, который обертывает ваше обычное свойство, чтобы обеспечить дополнительную функциональность. Этот слой действует как посредник между вами и фактическим имуществом. Итак, всякий раз, когда вы хотите установить или получить значение свойства, вы должны общаться через оболочку свойства. Теперь вы можете спросить: «Зачем вводить посредника в этом взаимодействии?». Продолжайте читать, и вы найдете свой ответ.

Допустим, вы являетесь разработчиком приложения Twitter и хотите сохранить новый твит в свойстве. Как вы знаете, размер твита ограничен 280 символами. В идеале у вас уже есть пользовательский интерфейс, который запрещает пользователю вводить более 280 символов.

Но это не значит, что мы должны ограничивать эту проверку символов только пользовательским интерфейсом. Что, если пользовательский интерфейс сломается, и вдруг люди будут публиковать эссе в Твиттере? Вы ведь не хотите, чтобы вас уволили? Давайте посмотрим, как обертка свойства может спасти вашу работу.

В приведенном выше примере мы используем свойство tweet для хранения фактического твита. Если вы заметили, прямо перед объявлением нашей переменной у нас есть @RestrictedCharacterCount(maxCharacterCount: 33). Это обертка нашего свойства.

Чтобы использовать оболочку свойства, начните с символа @, за которым следует имя оболочки свойства. Мы также передаем 33 в качестве нашего значения maxCharacterCount, так как это будет максимальное количество символов, которое мы будем хранить для этого примера.

Из вывода вы можете заметить, что только первые 33 символа нашего предложения, которое мы написали в Твиттере, были фактически сохранены на нашем ресурсе. Остальное игнорировалось. Если вы помните, это потому, что ранее мы явно установили для «maxCharacterCount» значение 33. Теперь вы можете установить это число на 280 символов и сохранить свою работув твиттере.

Вы также могли заметить, что мы обращаемся к нашему объекту-оболочке свойства, используя знак $ (доллар) перед именем нашего свойства. Это называется прогнозируемой стоимостью.

Прежде чем мы отпразднуем нашу победу, давайте разберемся, как это работает внутри.

Чтобы написать оболочку свойства,

  1. Вы можете использовать либо структуру, либо класс
  2. Аннотируйте свой объект с помощью синтаксиса @propertyWrapper.
  3. Объявите свойство с именем wrappedValue. Он должен быть нестатичным. Это фактическое свойство, которое мы будем обертывать. Тип данных этого свойства зависит от вас.
  4. Вы можете либо указать wrappedValue, значение по умолчанию, либо позволить пользователю предоставить его. Чтобы сделать последнее, обязательно включите wrappedValue как часть инициализатора вашего объекта.
  5. Помимо получения значения для вашего wrappedValue в вашем инициализаторе, вы также можете указать другие параметры, которые, по вашему мнению, необходимы для вашей оболочки свойства.
  6. Теперь, как посредник, всякий раз, когда свойство «устанавливается» или «получается», вы можете свободно изменять значение в соответствии со своей логикой. То, что вы решите, станет окончательным значением, которое будет сохранено или возвращено пользователю.
  7. Проецируемое значение — это то, к чему пользователь может получить доступ, если перед именем свойства ставится префикс $ (знак доллара). То, что вы возвращаете в качестве прогнозируемого значения, полностью зависит от вас.
  8. Например, у вас может быть несколько методов или свойств в оболочке свойства, чтобы предоставить пользователю дополнительные функциональные возможности. Чтобы раскрыть это, вы можете вернуть саму оболочку свойства в качестве проецируемого значения.

Теперь, когда у вас есть общее представление о том, как все работает, давайте посмотрим на реализацию нашей оболочки свойств @RestrictedCharacterCount из предыдущего примера.

«Братан, почему ты должен усложнять вещи, делая их универсальными»

Потерпите меня несколько минут. Позвольте мне объяснить это для вас, и вы поймете, насколько это действительно мощно. Итак, вот основная идея.

Создайте оболочку свойства, которая будет принимать максимальное количество символов в качестве параметра, и всякий раз, когда количество символов данных, хранящихся внутри свойства, превышает максимальное количество символов, обрежьте лишние символы с конца. Оболочка свойств также должна быть совместима с широким спектром типов данных, чтобы обеспечить максимальную гибкость.

Теперь давайте разберем нашу оболочку свойства,

  1. Чтобы сделать его совместимым с широким спектром типов данных, мы делаем тип данных нашего wrappedValue универсальным и называем его T.
  2. Мы также удостоверяемся, что T соответствует протоколу LosslessStringConvertible. Это позволяет осуществлять свободное преобразование между T ←→ String.
  3. Теперь мы преобразуем наш wrappedValue типа данных T в строку, чтобы подсчитать количество символов. Если мы превышаем maxCharacterCount, мы обрезаем лишние символы в конце. Наконец, мы преобразуем эту обрезанную строку обратно в исходный тип данных T и сохраняем ее в переменной wrappedValue.
  4. Таким образом, каждый раз, когда пользователь устанавливает значение для нашего свойства, мы проверяем данные, подсчитываем символы, обрезаем все лишние символы и, наконец, сохраняем их.

Вот несколько примеров того, почему, сделав его универсальным, он стал действительно мощным:

Позвольте мне привести вам еще несколько примеров, чтобы закрепить ваше понимание.

@RestrictedRange — это оболочка свойств, которая помогает вам ограничить значение вашего свойства определенным диапазоном. Всякий раз, когда какой-либо пользователь пытается установить значение, которое не попадает в допустимый диапазон, вызывается замыкание, чтобы заменить его допустимым значением.

@NotificationCenterPublisher — это универсальная оболочка свойств, которая публикует каждое новое значение свойства с помощью Центра уведомлений.

Вы должны передать имя уведомления, а также можете дополнительно установить закрытие с именем getUserInfo, которое позволит вам передать словарь userInfo непосредственно перед публикацией уведомления.

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

При желании вы можете добавить projectedValue к вашей обертке собственности, к которой затем можно будет получить доступ через причудливый синтаксис $ (доллар). Это полезно для предоставления любых дополнительных функций в оболочке вашего свойства.

Обертки свойств — большая часть мира SwiftUI, и их понимание имеет решающее значение.