Мобильный трудоголик
1.33K subscribers
61 photos
9 videos
264 links
👨‍💻 Пишу простым языком об iOS разработке на Swift и мобильной разработке в целом.
🔹 Вошел в IT задолго до того как это стало мейнстримом.
---
‍Обо мне: https://t.me/hardworkerIT/3
Чат: @hardworkerChatIT
Канал про разработку и жизнь в ИТ: @itDenisov
Download Telegram
Deadlock и Livelock: чем опасны и как избежать.


⚠️ Deadlock — это тупик.
Когда два потока блокируют друг друга, бесконечно ожидая освобождения ресурсов, которые каждый из них удерживает.

♣️ Пример:
Поток 1: держит доступ к базе и ждёт сетевой ответ.
Поток 2: держит сеть и ждёт доступ к базе.

Как предотвратить:
🔹 Устанавливайте строгий порядок блокировки ресурсов.
🔹 Используйте асинхронные вызовы вместо синхронных.


⚠️ Livelock — это бег на месте.
Ситуация, при которой два или более потока не могут выполнять полезной работы по причине борьбы за общий ресурс.

♣️ Пример:
Два потока постоянно меняют флаг "готовности", но реальной работы не происходит.

Как исправить:
🔹 Добавляйте случайные задержки между повторными попытками.
🔹 Используйте семафоры для контроля доступа.
🔹 Используйте барьеры.


💡 Главное отличие:
Deadlock — полная остановка, Livelock — активность без прогресса.

🔢 В Swift особенно актуально при работе с:
🔸 GCD
🔸 OperationQueue
🔸 Новыми Actor-ами

➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2411🔥4
Укажите ключевое слово, которое используется в Swift для создания параметра только для чтения
Anonymous Quiz
11%
readonly
0%
var
19%
const
70%
let
👍24🔥12🫡5
🔢 Типы в Swift.

В Swift типы данных делятся на именованные (собственные) и составные (временные).

Именованные типы:
Это типы, которые объявляются явно и получают своё имя. К ним относятся: структуры, классы, перечисления, протоколы.

♣️ Пример:

struct MyStruct {
let name: String
}

class MyClass {
let value: Int

init(value: Int) {
self.value = value
}
}


Составные типы:
Это временные типы, которые компилятор создаёт автоматически. Они не имеют собственного имени и используются в момент вычислений. К ним относятся: функциональные типы и кортежи.

♣️ Пример:

let coordinates: (x: Int, y: Int) = (10, 20)

let doPrint: (String) -> Void = { print($0) }


⚠️ Когда что использовать?
🔸 Именованные — когда нужна чёткая структура и повторное использование.
🔸 Составные — для временных данных или сокращения кода.

💡 Составные типы упрощают код, но злоупотреблять ими не стоит — это может снизить читаемость кода.

➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2211🔥5
🔢 Дженерики в Swift: мощный инструмент для гибкого кода.

Generics — это механизм, который позволяет писать универсальные функции и типы, работающие с любыми типами данных, сохраняя при этом полную типобезопасность.

🤔 Зачем нужны дженерики?

🔹 Уменьшают дублирование кода:
Больше не нужно писать отдельные функции для Int, Float и других типов — один обобщённый вариант заменит их все.

🔹 Обеспечивают типобезопасность:
Компилятор проверяет типы на этапе компиляции, предотвращая ошибки в рантайме.

🔹 Повышают переиспользуемость:
Один алгоритм можно применять к разным типам данных без изменений.

⚠️ Как работают дженерики?

Дженерики используют параметры типа (обычно обозначаются <T>, <Element> и т. д.), которые заменяются конкретными типами при компиляции.

♣️ Пример использования:

func plusValue<T: Numeric>(first: T, second: T) -> T {
return first + second
}

let a = 5
let b = 10
print(plusValue(first: a, second: b)) // 15

let c = 0.5
let d = 3.0
print(plusValue(first: c, second: d)) // 3.0

//

struct Stack<Element> {
private var elements = [Element]()

mutating func push(_ element: Element) {
elements.append(element)
}

mutating func pop() -> Element? {
return elements.popLast()
}
}

var intStack = Stack<Int>()
intStack.push(1)

var stringStack = Stack<String>()
stringStack.push("Swift")


Ограничения дженериков:

Иногда нужно указать, что тип T должен соответствовать определённым требованиям. Для этого используют протоколы:


func plusValue<T: Numeric>(first: T, second: T) -> T {
return first + second
}


Здесь T должен являться числом (Numeric), иначе код не скомпилируется.

➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2213🔥5
👨‍💻 Каких разработчиков никогда не заменят ИИ?

Главный навык разработчиков будущего - уметь писать не только код, чтобы добавить экран в приложение, но и понимать для чего добавляется та или иная кнопка. Т.е. быть ориентированным на продукт. О чем я кстати не раз уже упоминал в блоге. На таких разработчиков работодатели будут всегда охотнее смотреть, так как они мыслят намного шире своей первоначальной специализации, соответственно решение будет более выигрышно для бизнеса. Вот несколько советов, как развивать его разработчику:

1️⃣ Разберись, как и на чем компания зарабатывает. Какие части — драйверы роста и прибыли? Почему? Как ваша команда влияет на общий успех? Без этого контекста решения в вакууме.

2️⃣ Подружись с менеджером продукта. Большинство PM просто мечтают, чтобы инженеры глубже погружались в продукт! Сначала инвестируй время в отношения, покажи искренний интерес. Тогда твои вопросы о стратегии и пользователях будут восприняты с энтузиазмом.

3️⃣ Не ограничивайся кодом. Узнай, как люди на самом деле используют продукт, смотри обращения в поддержку, общайся с дизайнерами, аналитиками и другими. Они видят боль пользователей и работу продукта в реальности.

4️⃣ Предлагай, а не только реализуй. Зная бизнес, продукт и стейкхолдеров — прояви инициативу! Выдвигай обоснованные фичи или улучшения (пусть даже мелкие в рамках текущей таски). Для крупных идей — покажи инженерные и продуктовые усилия/выгоды, чтобы их легко можно было приоритизировать.

5️⃣ Мысли шире стандартных "фича vs. техдолг". Предлагай варианты, где продуктовые решения (например, чуть иной UX) могут значительно снизить инженерные усилия без потери ценности. Обсуждай эти варианты с командой и PM.

6️⃣ Проси фидбэк о своем продуктовом росте. Быть Product-Minded Engineer — значит развивать навыки продукта параллельно с инженерными. Лучший человек для обратной связи здесь — твой PM. Спрашивай: "Насколько полезны мои предложения?", "Где мне расти в продуктовом мышлении?".

➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2713🔥7
🍏 Apple в России загнали в угол!

Госдума приняла закон, обязывающий предустанавливать RuStore на ВСЕ продаваемые смартфоны и планшеты — включая технику Apple!

С 1 сентября (как раз к выходу iOS 26!) продавать iPhone и iPad можно будет только при условии, что на них можно устанавливать и обновлять приложения через RuStore.

Apple придется срочно решать: пойти на беспрецедентные уступки российским требованиям или рисковать штрафами.

Однако депутат Антон Горелкин объяснил, что никак запрещать продажи iPhone и iPad в случае неисполнения закона не планируется.

🤔 Верим?

➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
🗿24👀14🤯8
🔢 @EnvironmentObject в SwiftUI.

@EnvironmentObject — это property wrapper, который позволяет передавать общие данные между несколькими View без явной передачи через инициализатор.

🤔 Как использовать:

♣️ Создаем ObservableObject:

class UserSettings: ObservableObject {
@Published var isDarkMode = false
}


♣️ Добавляем в Environment в корневом View:

@main
struct MyApp: App {
var settings = UserSettings()

var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(settings) // Передаем объект в корневой вью
}
}
}


♣️ Используем в любом дочернем View:

struct ProfileView: View {
@EnvironmentObject var settings: UserSettings // Автоматическая подписка на объек в сабвью

var body: some View {
Toggle("Темная тема", isOn: $settings.isDarkMode)
}
}


⚠️ Ключевые особенности:
🔸 Автоматическое обновление: View перерисовывается при изменении @Published свойств.
🔸 Глобальный доступ: объект доступен в любой части иерархии View без цепочки @Binding.
🔸 Безопасность: если объект не передан в .environmentObject(), приложение крашнется — это помогает находить ошибки на раннем этапе.

Когда использовать?
🔹 Для глобальных настроек (тема, язык, авторизация и т.д.).
🔹 Когда нужно делиться данными между несвязанными View.
🔹 Вместо долгих цепочек @Binding / @ObservedObject.

➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
18👍116
🍏 Apple нарушает свои же правила.

Рекламные пуши добрались и до Wallet. Пользователи iOS сообщают о получении рекламных push-уведомлений от приложения Wallet. Уведомления предлагали скидку на билеты к фильму Apple TV+ «Формула-1».

Многие отмечают, что не давали явного согласия на получение такой рекламы, что потенциально противоречит собственным правилам Apple относительно маркетинговых коммуникаций.

Интересно, что в свежей бета-версии iOS 26 уже обнаружилась настройка для отключения этих самых «Рекламных предложений» в Wallet.

➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
🗿22🤯12🔥5👀2
🔢 Разница между Frame и Bounds в Swift.

Frame — это прямоугольник, который определяет положение и размер вью относительно её родителя. Он отвечает на вопрос: "Где и какого размера этот элемент находится в родительском контейнере?"

Bounds — это прямоугольник, который описывает внутреннюю систему координат вью. Он отвечает на вопрос: "Какое пространство доступно для отрисовки внутри этого элемента?"

⚠️ Ключевые отличия:

Система координат
🔸 Frame использует систему координат родительского вью.
🔸 Bounds использует собственную систему координат.

Влияние трансформации
🔸 Frame меняется при применении трансформаций (например, при повороте вью он может измениться неопределенным образом).
🔸 Bounds остаётся неизменным при любых трансформациях.

Когда используется
🔸 Frame — когда нужно расположить вью относительно родителя.
🔸 Bounds — когда работаете с внутренним содержимым вью (например, обрезка изображения).

➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2217🤝7
🔨 Как работают библиотеки в iOS.

В iOS-разработке мы часто используем готовые библиотеки или упаковываем свой код для повторного использования. Тут есть два принципиально разных подхода: статические и динамические библиотеки.

🔹 Статические библиотеки (*.a) встраивают весь свой код прямо в бинарник приложения во время компиляции. С одной стороны, это удобно — после сборки приложение не зависит от внешних файлов. С другой — любое изменение в библиотеке потребует полной перекомпиляции проекта.

🔹 Динамические библиотеки (.dylib) работают иначе. Вместо включения в исполняемый файл они подгружаются в момент запуска приложения. Это уменьшает размер основного бинарника и ускоряет запуск (если библиотек немного). Но есть нюанс — если динамических зависимостей слишком много, время старта, наоборот, увеличится.

⚠️ Особенность iOS в том, что здесь нет общей области для динамических библиотек. Даже если десять приложений используют один и тот же фреймворк, система будет хранить десять отдельных копий — это следствие песочницы. Каждое приложение работает изолированно и не может делиться библиотеками с другими.

🖼 Ресурсы (картинки, локализация) поставляются вместе с библиотекой в специальных Bundle — это стандартный способ упаковки дополнительных файлов.

💡 Главный вывод: выбор типа библиотеки зависит от задач. Статические — для простоты, динамические — для гибкости, но с оглядкой на особенности iOS.

➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍23125
🔨 Xtool — кроссплатформенная замена Xcode для iOS-разработки.

Новый инструмент Xtool позволяет собирать, подписывать и запускать iOS приложения без Xcode. Как отмечает Томас Рикуар, теперь разрабатывать под iOS можно даже на Linux или Windows — достаточно минимального Swift пакета.

⚠️ Что это за инструмент?
🔸 Открытый билдчейн с полным контролем над процессом сборки.
🔸 Поддержка кроссплатформенной разработки (macOS / Linux / Windows).
🔸 Работает с чистыми Swift пакетами — без Storyboard и xcodeproj.

🤔 Как это меняет разработку?
🔹 Больше не нужен Xcode для базовых задач.
🔹 Сборка на любом ПК — даже без Mac (если есть сертификаты подписи).
🔹 Гибкость CI/CD — можно использовать Linux серверы для сборки iOS приложений.

🔗 Официальный репозиторий Xtool

🔗 Документация

💡 Xtool пока не заменяет Xcode полностью (нет Interface Builder, отладчика), но открывает новые возможности для автоматизации и кросс-платформенных разработки.

➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍23🔥10🤯4
🔢 @ObservedObject в SwiftUI.

@ObservedObject — это property wrapper, который позволяет подписываться на изменения внешнего объекта данных и автоматически обновлять View при их изменении.

🤔 Как использовать:

♣️ Создаем ObservableObject:

class Counter: ObservableObject {
@Published var value = 0
}


♣️ Передаем объект в View (через инициализатор):

struct CounterView: View {
@ObservedObject var counter: Counter

var body: some View {
Button("+1") { counter.value += 1 }
Text("Значение: \(counter.value)")
}
}


♣️ Используем в родительском View:

struct ParentView: View {
@StateObject private var counter = Counter()

var body: some View {
CounterView(counter: counter)
}
}


⚠️ Ключевые особенности:
🔸 Автоматическое обновление: View перерисовывается при изменении @Published свойств.
🔸 Локальная область видимости: работает только для конкретного View, куда передан объект.
🔸 Гибкость: можно передавать между View как обычное свойство.

💡 Для глобальных данных лучше использовать @EnvironmentObject.

➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥23👍133
😁30👍11🔥4🤔2
👨‍💻 Nutrition Labels в App Store: как рассказать о доступности своего приложения.

Apple добавила в App Store новый раздел с настройками доступности, где разработчики могут отметить, какие функции поддерживает их приложение. Это помогает пользователям с особыми потребностями быстрее находить подходящие программы.

Какие параметры можно указать?
🔹 Скринридер (VoiceOver) — поддержка озвучивания интерфейса.
🔹 Управление голосом (Voice Control).
🔹 Увеличение текста (Dynamic Type).
🔹 Темная тема (Dark Mode).
🔹 Различие не только цветом (не полагается только на цвет для передачи информации).
🔹 Повышенный контраст (для слабовидящих).
🔹 Уменьшение движения (Reduce Motion).
🔹 Субтитры (скрытые титры для видео).
🔹 Аудиоописание (описание визуального контента).

🤔 Как это работает?
Разработчик самостоятельно отмечает в App Store Connect, какие функции доступности реализованы в приложении. Apple не проверяет это автоматически — ответственность лежит на команде.

🔗 Документация

⚠️ Зачем это нужно?
🔸 Пользователи смогут фильтровать приложения по нужным им функциям.
🔸 Разработчики получат дополнительный способ выделить своё приложение.
🔸 Повышение инклюзивности — больше людей смогут комфортно пользоваться приложением.

💡 Где настроить?
→ App Store Connect → [Ваше приложение] → Доступность

➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍24👀14🔥3
🛒 Командная работа — важный навык.

Ты часто слышишь: «Нам нужны командные игроки». Но что это на самом деле значит?
Это не про бесконечные митинги и формальные отчёты. И уж точно не про работу в одиночку, под эгидой одной компании.

Настоящая командная работа — это:
🔹 Готовность делиться — не только проблемами, но и идеями, знаниями, ресурсами.
🔹 Взаимное уважение — к коллегам, их времени и вкладу в общее дело.
🔹 Поддержка вместо конкуренции — ты не просто «закрываешь свои таски», а помогаешь другим двигаться вперёд.
🔹 Общий результат важнее личных амбиций — успех проекта зависит от всех, а не только от тебя.

«Но у меня мало опыта в больших командах!» — не проблема.
Важно не количество людей вокруг, а твой подход к совместной работе.

🤔 Как показать это на собеседовании?
🔸 На вопросы наподобие «Расскажите о вашей прошлой работе» нужно подробно рассказать о составе прошлой команды, с кем тебе приходилось взаимодействовать.
🔸 На вопросы вроде «Расскажите какие задачи приходилось решать?» нужно обязательно отвечать о совместных с командой достижениях и решенных проблемах и в меньшей степени о единоличных успехах.
🔸 На вопросы «Как ты делал работу команды эффективнее?» желательно отвечать что-то вроде: «Я инициировал ежедневные созвоны, чтобы быстро решать вопросы, предлагал упрощённые решения, которые экономили время всем, и всегда был на связи, если кому-то нужна была помощь».
🔸 Вопрос «Какой был результат вашей работы?» предполагает ответ вроде: «Мы уложились в дедлайн, избежали лишних доработок, и бизнес заказчик остался доволен итогом работы».

⚠️ Дополнительный козырь — менторство.
Если у тебя есть опыт наставничества с джунами, мидлами и другими сотрудниками — не скрывай этого!

💡 Почему это важно?
🔹 Ты не только специалист, но и наставник — можешь развивать команду.
🔹 Ты мыслишь масштабнее — понимаешь, что рост коллег = рост компании.
🔹 Ты умеешь объяснять — а это редкий и ценный навык.

Умение работать с людьми — вот что делает тебя по-настоящему ценным. Настали временна, когда код пишут многие, нужно выделяться и качать софт-скиллы.

➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍26🔥142👀1
🔢 🏋️ Swift для Android: что задумали разработчики?

Комьюнити Swift официально запускает Android Workgroup — рабочую группу, которая займётся адаптацией языка под разработку для Android.

🤔 Что это значит?
🔹 Swift может стать официально поддерживаемым языком для Android.
🔹 Появится возможность писать нативные приложения на Swift под Android (как это уже делает Apple Music).
🔹 Открытая разработка: любой может присоединиться к обсуждениям и вносить правки.

⚠️ Какие задачи у группы?
🔸 Поддержка Android в дистрибутиве Swift.
🔸 Адаптация Swift пакетов под Android идиомы.
🔸 Интеграция с Java SDK (взаимодействие Swift и Java кода).
🔸 Система CI/CD для тестирования сборки под Android.
🔸 Отладка Swift кода на Android устройствах.

Сообщество может влиять на процесс через форумы Swift. Членство в группе открыто для всех.

💡 Почему это важно?
Если инициатива реализуется, разработчики смогут:
🔹 Использовать один язык (Swift) для iOS и Android.
🔹 Переносить код между платформами с минимальными изменениями.
🔹 Упростить поддержку кроссплатформенных проектов.

Пока рано говорить о сроках, но сам факт создания рабочей группы — большой шаг в сторону универсального Swift.
Будем следить за развитием!

➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥29👍10👀4
Для тех кто не знал, в Xcode можно быстро убрать из логов сообщения от любых библиотек или системных процессов.

🤔 Зачем это нужно?
🔸 Чище вывод логов при отладке.
🔸 Не отвлекает лишний шум от сторонних зависимостей.
🔸 Работает даже с системными сообщениями.

➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍30🔥104
🔢 Как правильно использовать Glass-эффект в Swift.

Главное правило:
Если применяете glassEffect() к кастомным вью — оборачивайте их в общий контейнер (например, HStack или UIView).

ZStack {
HStack {
FirstView()
SecondView()
}
.glassEffect() // Эффект применяется ко всей группе
}

HStack {
ZStack { // Отдельные контейнеры для каждой вью
FirstView()
.glassEffect() // Эффект применяется только к данному вью
}

ZStack { // Отдельные контейнеры для каждой вью
SecondView()
.glassEffect() // Эффект применяется только к данному вью
}
}


⚠️ Почему это важно?
🔸 Glass-эффект анализирует пиксели вокруг себя для создания полупрозрачного размытия.
🔸 Если наложить два эффекта друг на друга — получится грязное наложение (артефакты, мерцание).
🔸 Нативные системные элементы (навигация, тулбары) уже содержат правильную группировку — их не нужно оборачивать.

Когда можно нарушить правило?
🔹 Если используете одиночный glass-элемент без пересечений с другими.
🔹 Для статичного фона (например, картинка под стеклом).

➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍24🔥1532
😤 Сильный сотрудник начал сдавать? 8 реальных причин.

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


🔸 Талантливый специалист ≠ хороший руководитель. Если сотрудника повысили, но не научили управленческим навыкам, он может тонуть в задачах, которые раньше решал за час.
🔸 Сильные сотрудники часто берут на себя слишком много — особенно если им не говорят «стоп». Со временем это приводит к ошибкам и апатии.
🔸 Даже топовые специалисты — живые люди. Развод, болезнь, уход за родственником снижают концентрацию.
🔸 Если сотрудник не чувствует поддержки коллег или борется с токсичным окружением, его мотивация падает.
🔸 Рутина убивает амбиции. Если человек не видит развития, он начинает работать «на минимуме».
🔸 Хронический стресс, депрессия, недосып или синдром самозванца снижают продуктивность.
🔸 Если компания резко меняет курс (например, с качества на скорость), принципиальные сотрудники теряют мотивацию.
🔸 Плохое согласование может привести к неэффективной работе сотрудников.

Прежде чем ругать сотрудника, разберитесь в причинах. Часто проблема не в нём, а в системе.

➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👀23👍162🤝2
🔢 Swift 6: Typed Throws — точечная обработка ошибок.

С выходом Swift 6 работа с ошибками стала точнее. Теперь вместо простого throws можно указывать конкретный тип или даже вариант ошибки, что делает код безопаснее и понятнее.

♣️ Раньше функция могла выбросить любую ошибку, и компилятор не проверял её тип:

func readFile(at path: String) throws -> String {
}


♣️ Теперь можно явно указать, какую именно ошибку ожидать:

func readFile(at path: String) throws(FileNotFoundError) -> String {
}


Если функция попытается выбросить другой вариант FileNotFoundError (например FileNotFoundError.whileSearchingFile), компилятор заранее сообщит об ошибке.

🤔 Зачем это нужно?
🔹 Безопасность: компилятор проверяет, что вы ловите именно те ошибки, которые задекларированы.
🔹 Читаемость: из сигнатуры функции сразу видно, что может пойти не так.
🔹 Упрощение кода: больше не нужен «универсальный» catch для всех неизвестных ошибок:

do {
let content = try readFile(at: "path")
} catch FileNotFoundError.whileReadingFile {
print("Файл поврежден")
} catch FileNotFoundError.whileSearchingFile {
print("Ошибка при поиске файла")
}


⚠️ Важно:
🔸 Тип ошибки должен соответствовать протоколу Error.
🔸 Можно указать как общий catch (как было ранее), так и конкретный case из enum.

💡 Вывод:
Typed Throws в Swift 6 — это шаг к предсказуемому и самодокументируемому коду. Меньше скрытых багов, проще отладка!

➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥26👍132