Владелец типа публикует свойство, которое внешним клиентам нужно читать, но нельзя менять: какой механизм Swift задаёт разные права доступа?
Для этого используют private(set): свойство остаётся доступным для чтения в пределах доступности самого свойства, но его setter доступен только внутри разрешённой области типа. Например, public private(set) var status можно читать из другого модуля, но изменять — только из реализации самого типа.
Разделение доступа к чтению и записи решает распространённую задачу инкапсуляции: тип должен публиковать своё состояние, но сохранять контроль над тем, как оно изменяется. Без такого механизма разработчику пришлось бы скрывать всё свойство и добавлять отдельный getter или метод для чтения.
В Swift права доступа getter и setter могут различаться. Это позволяет сохранить простой синтаксис свойства, не раскрывая внешнему коду возможность произвольного присваивания.
Если свойство доступно для записи извне, любой вызывающий код может нарушить инварианты объекта: например, установить статус без проверки, изменить баланс напрямую или перевести объект в недопустимое состояние.
Полностью объявлять свойство private тоже не всегда удобно: внешнему коду может требоваться его текущее значение для отображения интерфейса, сериализации или принятия решения. Требуется открыть чтение, но оставить изменение под контролем типа.
private(set) ограничивает именно setter. Уровень доступа getter определяется объявлением свойства и контекстом типа, поэтому в публичном типе обычно пишут public private(set), если чтение должно быть доступно внешним модулям.
В примере изменение проходит через метод, который проверяет допустимый диапазон. Для структуры такой метод должен быть mutating, поскольку он изменяет хранимое свойство экземпляра; для класса mutating не нужен.
private(set) не делает значение неизменяемым навсегда. Код внутри разрешённой области может менять его, а внешнему коду доступно только чтение. Ограничение применяется к присваиванию самому свойству, а не обязательно к внутреннему состоянию объекта, на который оно ссылается: если свойство хранит экземпляр класса, его публичные методы всё ещё могут изменять этот экземпляр.
Важно отличать private(set) от let. let запрещает изменение значения после инициализации на уровне всей доступной модели, тогда как private(set) оставляет изменение возможным внутри типа. Также private(set) не является property wrapper и не добавляет автоматическую синхронизацию, валидацию или наблюдателей.
Сервис загрузки должен показывать UI текущий прогресс, но запрещать контроллеру устанавливать произвольное значение. Вариант с публичным изменяемым свойством прост, однако любой клиент может записать некорректный прогресс; вариант с полностью закрытым свойством лучше защищает состояние, но требует отдельного метода чтения или дублирующего API.
Выбранное решение — private(set) и метод updateProgress, который проверяет диапазон и меняет состояние. Это сохраняет удобное чтение через обычное свойство, централизует правила изменения и уменьшает связанность клиентов с внутренней реализацией.
Какой доступ имеет getter у свойства с private(set)?
private(set) ограничивает только setter. Getter сохраняет уровень доступа, заданный для свойства или выведенный из контекста. Поэтому у свойства public private(set) чтение публично, а запись доступна только внутри типа и соответствующей области private.
Можно ли изменить такое свойство через метод типа?
Да. Метод, находящийся в разрешённой области доступа, может вызывать setter. Для структуры изменяющий метод должен быть помечен mutating, поскольку изменение свойства означает изменение всего значения структуры; для класса специальная пометка не нужна.
Защищает ли private(set) объект, хранящийся в свойстве, от любых изменений?
Нет. Ограничивается присваивание ссылки или значения самому свойству. Если свойство содержит экземпляр класса, внешний код не сможет заменить ссылку, но сможет вызвать доступные методы этого экземпляра и изменить его внутреннее состояние. Для полной защиты нужно отдельно проектировать API самого ссылочного типа.