Тёмный

Организация модулей. Типы Inject. Способы доставки зависимостей 

Android Broadcast. Все об Андроид разработке
Просмотров 39 тыс.
50% 1

Из урока вы узнаете про лучшие практики организации модулей в Dagger, различные типы inject, а также способы доставки зависимостей
🔗 Поддержать проект taplink.cc/android_broadcast/...
🔗 Каналы "Android Broadcast" taplink.cc/android_broadcast
🔗 Документация по Dagger dagger.dev/dev-guide/
📺 Курс по Dagger 2 clck.ru/VtY7d
🔗 Код из пример bit.ly/3domAir
Видео сделано при поддержке Лаборатории Касперского
#AndroidBroadcast #DaggerКурс #Dagger2 #DI #DependncyInjection #Hilt #DaggerHilt #КириллРозов #РозовКирилл
0:00 Вступление
0:39 Проект-пример
1:49 Несколько модулей
3:07 Binds
6:29 Inject в конструктор класса
10:09 Inject в метод
11:51 Работа с параметрами артефактов не из графа
14:46 Assisted Inject
17:49 Квалификаторы
19:33 Собственная аннотация-квалификатор
21:53 Lazy и Provider
23:26 Provider
24:22 Заключение
25:00 Титры

Наука

Опубликовано:

 

16 июн 2024

Поделиться:

Ссылка:

Скачать:

Готовим ссылку...

Добавить в:

Мой плейлист
Посмотреть позже
Комментарии : 191   
@AndroidBroadcast
@AndroidBroadcast 3 года назад
💰 Поддержать проект на Boosty bit.ly/3sratqQ или Patreon patreon.com/android_broadcast 🔗 Telegram канал "Android Broadcast" ttttt.me/android_broadcast Видео сделано при поддержке Лаборатории Касперского
@divanlobkowski3473
@divanlobkowski3473 2 года назад
Самое быстрое объяснение dagger на диком западе.
@qIKraytonIp
@qIKraytonIp 2 года назад
Пожалуй одна из самых ценных серий видео-уроков, что я видел, жду 3ю часть
@yakiv1488
@yakiv1488 2 года назад
Курс бомбический! Это лучшее объяснение Dagger из всего что я видел!
@grigoriy3615
@grigoriy3615 2 года назад
Спасибо за видео. Такие видео очень нужны!!!
@UCfpBnsCG17QVj5bLvbBjBrQ
@UCfpBnsCG17QVj5bLvbBjBrQ 2 года назад
Очень классный курс, важно не только посмотреть но и попробовать все что в нем рассказывается. Больше спасибо за труд 🙏
@user-zo4ml2mg2s
@user-zo4ml2mg2s 2 года назад
Спасибо большое за курсы, очень приятно слушать профессионала своего дела)
@uservhhrXdgko1234
@uservhhrXdgko1234 2 года назад
Боже какой же топовый расклад. Это просто божественно, дай бог тебе здоровья)
@vladimirdaryin9351
@vladimirdaryin9351 2 года назад
Благодарю, уже сколько лет работа с Dagger оставляет ощущение, что что-то не до конца понимаешь, эти уроки помогли прояснить общую картину + пару новых удобных моментов себе в проекты утяну, спасибо! 🙏🏻
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Супер! Значит у курса получается делать что нужно
@immortal_lnight
@immortal_lnight 2 года назад
Хоть временами и сложно понять что-то, но спасибо за проделанную работу, Кирилл!
@KudashovDev
@KudashovDev 2 года назад
Круто! Самое доступное и понятное объяснение дагера)
@user-wj1hg2lp2j
@user-wj1hg2lp2j 2 года назад
Было очень интересно,большое спасибо!
@alexiachimov9817
@alexiachimov9817 2 года назад
Спасибо за потрясный контент Кирилл. Не смог пройти мимо такого отличного канала по Android разработке. Обязательно оформлю подписку на Boosty что бы поддержать! Продолжайте в том же духе, успехов и удачи!
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Спасибо! Очень приятно читать такие комменты
@sonar_devices
@sonar_devices 3 года назад
Большое спасибо. Очень интересно.
@AndroidBroadcast
@AndroidBroadcast 3 года назад
Спасибо!
@beginnercoding1089
@beginnercoding1089 2 года назад
Спасибо большое за урок!
@codemachine19
@codemachine19 Год назад
Непросто урок, но подача очень интересная. Спасибо за материал!
@MikhailGureev
@MikhailGureev 2 года назад
Большое спасибо, Кирилл.
@lemarkmain1713
@lemarkmain1713 2 года назад
Спасибо, как раз то что нужно!
@sergeyjoke
@sergeyjoke Год назад
Спасибо. Очень хороший курс. Очень!
@user-by1id2ix4h
@user-by1id2ix4h 2 года назад
Очень крутой курс Спасибо
@MrPwnzrus
@MrPwnzrus 2 года назад
Спасибо , очень познавательно!!!
@axrorxojayodgorov3416
@axrorxojayodgorov3416 2 года назад
Thank you, really helpful tutorial
@kirill6020
@kirill6020 2 года назад
Спасибо за видео!
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Рад стараться!
@essohaitch8147
@essohaitch8147 2 года назад
Отличный ролик
@druce18
@druce18 2 года назад
Спасибо!
@inex550
@inex550 2 года назад
Топ. Просто топ.
@deadchannal
@deadchannal 2 года назад
Ккк, прикольно!
@Mozartnab
@Mozartnab 2 года назад
Спасибо тебе, добрый человек, курс по дагеру оч крутой и подробный! Хоть с первого раза я нефига не понял до конца, но благо можно смотреть заново, пока не разберешься...
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Задавай вопросы, я буду отвечать в комментария
@Mozartnab
@Mozartnab 2 года назад
@@AndroidBroadcast Решил для себя, что нужно сначала разобраться с нативным дагером. А потом уже смотреть в сторону хилта и т.д. Так как я еще совсем джун, и мне нужно знать как это все работает. Сейчас думаю над архитектурой. Я правильно понимаю, что для большинства прложений схема такая. 1 главный апп компонент, который подтягивает все зависимости ему необходимые, и по компоненту на каждую фичу, которые в свою очередь подтягивают все зависимости, которые нужны в этой фиче?
@Mozartnab
@Mozartnab 2 года назад
и как это должно по хорошему в проекте выглядеть? типо условно папка Даггер, там хранятся компоненты, а внутри компонентов уже модули интерфейсы инджект, скоупы и т.д. Или папка дагер и там 3 папки компоненты, модули, скоупы.
@Mozartnab
@Mozartnab 2 года назад
Еще вот у меня есть Продуктпрезентер, в который инжектится модель продукта и создает инстанс модели на основе входящего параметра из базы данных экземпляра продукта, я правильно понял, что этот экземпляр продукта, лучше реализовать с помощью асистед инжекта?
@Mozartnab
@Mozartnab 2 года назад
@@AndroidBroadcast Извиняюсь за нубские вопросы, заранее спасибо.
@user-iq2ic3mh9z
@user-iq2ic3mh9z 2 года назад
Фабрика для фабрики, квалификатор с названием "квалификатор". Ошибка BNR (Brain Not Responding) =) Если серьезно, то согласен с другими зрителями, что видосы у Кирилла топ! Давно смотрю.
@user-rs3dv7nf5j
@user-rs3dv7nf5j 2 года назад
супер
@GriNAME
@GriNAME Год назад
Эх, хороший курс)
@mukhtarbimurat5106
@mukhtarbimurat5106 2 года назад
Спасибо, очень полезно, еще хотелось узнать как правильно заменить prod на stage ретрофит в зависимости от какого то конфига (допустим BuildConfig.DEBUG) не меняя остальные деклараций(такие как репозиторий, use case и т.д)
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Я предпочитаю это делать через прокидывание параметра при создании Component. Будет показано в следующем уроке
@mitiaygorodov4939
@mitiaygorodov4939 2 года назад
@@AndroidBroadcast А если эту переменную прописать в градле, и при переключении типа сборки оно автоматически меняло значение на нужное? Этот подход имеет место быть?
@handleftman
@handleftman 2 года назад
Спасибо за урок! а код viewModel не слишком ли запутанный? где-то видел и даже использовал реализацию фабрики как синглтон в даггере. Надеюсь это будет в следующих частях видео :)
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Да, пример не лучший, но жизненный. Такую сложность оптимизировали в Hilt и вот там уже проще
@ildar2244
@ildar2244 2 года назад
Спасибо за труд - очень полезно. В описании к видео ссылка на "Курс по Dagger 2" выдаёт результат что плейлист не существует.
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Исправил
@denislopatkin6996
@denislopatkin6996 Месяц назад
В кодлабе по даггеру пишется что инжектить фрагмент в онаттач надо именно после вызова родительского аттача, а вот активити - до.
@azatnabiullin2263
@azatnabiullin2263 Год назад
На мой взгляд, плейлист про Dagger у Coding in Flow намного более последовательный и понятный. Хотя, возможно, курс Кирилла предназначен для более продвинутой аудитории. Многие вещи не объясняются, словно их понимание само самой разумеется, поэтому после этого курса есть риск использования их вслепую, потому что Кирилл так написал)
@AndroidBroadcast
@AndroidBroadcast Год назад
У меня весь канал не про новичков, а разработчиков с уже каким-то пониманием технологий. Любой курс имеет свою специфику, подачу, трансляцию опыта и авторский взгляд
@Capitistrum
@Capitistrum 10 месяцев назад
Спасибо за курс! После просмотра начало понимание приходить как устроен Dagger и DI в принципе Вопрос по Inject в метод. Как понял из видео, при аннотации @Inject метод будет выполнен автоматически один раз. А если мне не нужно его выполнение? Например, он должен выполниться при определенных условиях, иначе нет. И есть зависимость которая необходима только ему в классе. Так как метод может и не выполнится, кажется логичным не держать в поле ссылку на необходимую зависимость. Или получается все же в поле хранить?🤔
@AndroidBroadcast
@AndroidBroadcast 10 месяцев назад
Можно делать Inject в методе или в конструктор
@Capitistrum
@Capitistrum 10 месяцев назад
@@AndroidBroadcast да, это я понял. Если делать Inject в метод - то этот метод будет 100% выполнен при инициализации класса? Нет варианта чтобы он не выполнялся автоматически, а по необходимости?
@dmtrlbdv
@dmtrlbdv 2 года назад
хорошо бы ссылки на предыдущие и следующие уроки
@AndroidBroadcast
@AndroidBroadcast 2 года назад
В описании плейлист со всеми видео
@deo5686
@deo5686 2 года назад
Приветствую, спасибо за такой хороший курс. У меня есть вопрос, - @Inject constructor в domain, насколько это удобно, а если мы разрабатываем не только для андроида, не будут ли с этим проблемы? Я может многого не знаю, но мне кажется, если бы мы объявили явно все provides, - это было бы более лаконично, так как мы оставим зависимости все что связанно с di только в app модуле. Спасибо.
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Аннотация Inject не является части Dagger, а она часть JSR 330. Это переносится в рамках любого решения на Java
@antony5092
@antony5092 Год назад
Все круто, но не совсем понимаю для чего на 4 минуте добавлять 2 provides для newsRepository. Dagger позволяет же указывать возвращаемый абстрактный тип, но на самом деле возвращаеть его реализацию.
@aliaksandr_chaulytka
@aliaksandr_chaulytka 3 года назад
🔝🔝🔝 При Lazy инстанс кэшируется только для конкретного места (активити/фрагмента) или как ? Как вообще инстансы создаются в даггере? По умолчанию все синглтоны или фабрики? (В коине это сразу в DSL указывается, вот у меня и возник такой вопрос)
@AndroidBroadcast
@AndroidBroadcast 3 года назад
Да, в графе кэширование настраивается через Scope (будет в уроке 3). Lazy получает зависимость из графа и кеширует ее внутри себя, чтобы при следующем запросе не ходить за ней снова.
@deadchannal
@deadchannal 2 года назад
А какие плагины для Android Studio вы используете?
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Надо снять видео отдельное на эту тему
@volodymyr107
@volodymyr107 2 года назад
Планируется ли подобный курс по тестированию?
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Нет, тема тестирования не интересует, а также я в ней не спец
@slavuuhhaaa3554
@slavuuhhaaa3554 2 года назад
ничего не понятно но очень интересно
@Mozartnab
@Mozartnab 2 года назад
А еще вопрос, я тут делаю универсальный метод по созданию компонента через рефлексию, и хочу составить стрингу из Dagger + название компонента. Но тут возникает проблема, дагер иногда генерирует свой класс, как в названии компонента, а иногда использует нижнее подчеркивание. Вот пример - DaggerProductPresenter_Component и DaggerDataManagerComponent почему в одном так в другом так, от чего это зависит и есть ли еще вариации таких классов?
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Это внутренние классы, нет никаких гарантий их названий и они могут меняться от версии к версии, как и вовсе исчезнуть
@Mozartnab
@Mozartnab 2 года назад
@@AndroidBroadcast понятно, спасибо, а как тогда лучше сделать универсальный метод createComponent?
@Mozartnab
@Mozartnab 2 года назад
типо метод, который принимает в себя класс, который нужно создать, список модулей и список зависимостей
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Это слишком общая задача в которой нет ограничений на выходные параметры. Лучше создавать их без рефлексии обычным способом. Это будет надёжнее и безопаснее при изменении API
@stefanserkhir6478
@stefanserkhir6478 2 года назад
Спасибо за видео! Как правильно запровайдить context?
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Расскажу в следующем уроке про то как в граф добавлять внешние зависимости, но это делается с помощью прокидывания зависимостей во время создания компонентов
@axrorxojayodgorov3416
@axrorxojayodgorov3416 2 года назад
also, could you add about upstream/downstream dependencies
@AndroidBroadcast
@AndroidBroadcast 2 года назад
What is that?
@epicarchitect
@epicarchitect Год назад
Спасибо за урок но у меня возник вопрос тут 7:07. Почему писать анотацию инжект лучше в конструктор а не в модуле делать функцию с провайд анотацией? Тогда ведь весь диай будет размазан по приложению.
@AndroidBroadcast
@AndroidBroadcast Год назад
Тогда зависимости не будут зависеть от графа и легче переносить код по модулям
@user-xs3wx8vy8g
@user-xs3wx8vy8g 2 года назад
Ну незнаю. Очень сложно. Пойду курс попроще искать. Может потом вернусь.
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Да, это рассчитано на Android разработчиков
@user-xs3wx8vy8g
@user-xs3wx8vy8g 2 года назад
Согласен иначе зачем это смотреть. Просто после spring-а. DI в Dagger это конечно такое себе удовольствие. Посмотрел пару уроков по Dagger попроще. И здесь все встало на свои места. Спасибо за качественный контент по android.
@FoRGeish
@FoRGeish 2 года назад
Не очень понял, почему в качестве основного способа показан избыточный с добавлением метода provide*_to_*, а нормальный рабочий вариант в виде небольшой сноски?
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Я таким подходом не пользуюсь, поэтому забыл про него сказать, а во время монтажа вспомнил, поэтому и добавил сноской
@andrew3937
@andrew3937 2 года назад
А как на 23:13 создается viewModel, если на тот момент factory еще не заинжекчена. Одно происходит в момент создания класса, второе только в onAttach(). Я не оч понимаю, подскажите, пожалста.
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Она создаётся только при первом обращении к ней, а дальше сохраняется. Если вы до inject не обратитесь к ViewModel, то все будет хорошо
@GarcianSmt
@GarcianSmt 2 года назад
Я правильно понимаю, что при желании можно использовать только provides вместо binds, и это лишь дело вкуса?
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Да, все верно
@ljlj
@ljlj 11 месяцев назад
Как использовать assisted inject, чтобы получить репозиторий в usecase, когда он находится в модуле, в котором нет даггера, до этого момента он там не нужен был вообще, так как usecase резолвились в модуле, который app
@AndroidBroadcast
@AndroidBroadcast 11 месяцев назад
Вы должны добавить репозиторий в граф: либо модуль и Provide, либо Bind при создании компонента или другие способы
@aydogdyaydogdiev1255
@aydogdyaydogdiev1255 Год назад
во фрагменте нужно инжектить после onAttach
@AndroidBroadcast
@AndroidBroadcast Год назад
Почему?
@olegleonov1310
@olegleonov1310 2 года назад
Так, а почему бы вместо фабрики фабрик не сделать setNewsId для viewModel? И не делать newsId в конструкторе. А то какой-то overcoding получается.
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Нет, делать set метод - плохая практика. Во-первых, непонятно когда появится этот параметр и всем операциям надо его ждать. Также set метод даёт возможность подменить значение в любой момент. В моем случае параметр должен быть сразу и не меняться. То что в текущем варианте с ViewModel было сложно - полностью согласен, но Hilt имеет улучшения этого процесса
@legioncommander7261
@legioncommander7261 2 года назад
А как так работает AssistedFactory? Это же интерфейс, где прописывается реализация?
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Реализация генерится, как и реализация Component в Dagger
@kirillvolkov8556
@kirillvolkov8556 2 года назад
в новых версиях работа с AssistedFactory невозможна с делегатами
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Можно больше информации. Что именно невозможно иэ кода сделать?
@sevenxp8858
@sevenxp8858 2 года назад
Кирил один из тех людей, который помогает мне зарабатывать больше xD
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Пора начинать брать проценты )
@staschernov928
@staschernov928 Год назад
А можно, пожалуйста, выкладывать и сами проекты? А то по краткому обзору вначале невозможно все запомнить, и чтобы понять, о чем идет речь, приходится дублировать вкладку и смотреть 2 разных таймкода.
@AndroidBroadcast
@AndroidBroadcast Год назад
Ссылки в описании к видео разве нет?
@staschernov928
@staschernov928 Год назад
@@AndroidBroadcast ага, нашел, спасибо. В первый раз, видимо, проглядел.
@sabaka1305
@sabaka1305 Год назад
Скажите какая тема в андроид студио используется для кода .?
@AndroidBroadcast
@AndroidBroadcast Год назад
Darcula
@sabaka1305
@sabaka1305 Год назад
@@AndroidBroadcast странно . Есть доп цвета . Скобки другого цвета и тд
@azizbekrasulmetov9293
@azizbekrasulmetov9293 10 месяцев назад
@@sabaka1305 Rainbow brackets plugin
@Chernov1984
@Chernov1984 2 года назад
+
@oleg12395
@oleg12395 Год назад
Что такое резолвиться ? )
@oleg12395
@oleg12395 Год назад
И разрезолвить ?)))
@AndroidBroadcast
@AndroidBroadcast Год назад
Резолвить - выполнять запрос, решить задачу. В этом контексте по запросу найти нужную зависимость в дереве
@user-cd2yi4kx4c
@user-cd2yi4kx4c 2 года назад
не могу запустить проект с github :( Unrecognized Android Studio (or Android Support plugin for IntelliJ IDEA) version '202.7660.26.42.7486908', please retry with version 2020.3.1 or newer.
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Я использовал Android Studio Arctic Fox. Она в Beta
@user-cd2yi4kx4c
@user-cd2yi4kx4c 2 года назад
я с качал её, попробовал собрать проект вылетела новая ошибка: error: [ComponentProcessor:MiscError] dagger.internal.codegen.ComponentProcessor was unable to process this class because not all of its dependencies could be resolved. Check for compilation errors or a circular dependency with generated code. public final class NewsDetailsFragment extends androidx.fragment.app.Fragment не судите строго, навичка
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Локально пример работает у меня. Может у вас нет инета?
@user-th6xn9px9t
@user-th6xn9px9t 11 месяцев назад
А есть рабочий пример? С репозитория с гитхаба ничего не работает, там ни сервер недоступен, ни проекты не собираются
@luyt2
@luyt2 Год назад
Отличный ролик, но информации много сразу, мозг не успевает всё освоить. И ещё одна ложека дёгтя, слово resolved, как-то не прибавляет ясности, хотелось бы русский аналог, или небольшое его пояснение.
@AndroidBroadcast
@AndroidBroadcast Год назад
Resolved - разрешение задачи, выполнение
@AndroidBroadcast
@AndroidBroadcast Год назад
Так часто говорят , а нормальный русский аналог либо целое предложение или непонятное слово
@luyt2
@luyt2 Год назад
@@AndroidBroadcast для джунов, к коим я себя отношу, не все ясно. Спасибо за контент!!
@AndroidBroadcast
@AndroidBroadcast Год назад
Контент не рассчитывался для начинающих, а для разработчиков с опытом. Как и весь контент
@luyt2
@luyt2 Год назад
@@AndroidBroadcast Всё равно, спасибо за качественный материал, и успехов в работе!
@YulyaUlyanova
@YulyaUlyanova 2 года назад
это видео менее понятно чем предыдущее ((
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Всегда можно посмотреть ещё раз или задать вопрос в комментариях
@YulyaUlyanova
@YulyaUlyanova 2 года назад
@@AndroidBroadcast я не поняла зачем нужен Bind (
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Он просто даёт информацию графу, что когда запрашиваетсч один тип, то нужно возвращать вот другой. Если делать такое же через provide, то это будет создавать дополнительные фабрики. Яркий пример использования Bind - inject интерфейса, а благодаря это аннотации можно указать какую реализацию интерыейса нужно предоставить, когда запросят зависимость.
@YulyaUlyanova
@YulyaUlyanova 2 года назад
@@AndroidBroadcast но мне же всё равно через provide придется описывать способ получения зависимости, а в Bind нет этого описания. Зачем нужен bind если реализацию все равно нужно будет указывать и там же указать тип возращаемой зависимости.
@AndroidBroadcast
@AndroidBroadcast 2 года назад
Нет, в граф надо будет добавить только реализацию (через аннотацию Provide в модуле или Inject над конструктором), а Bind - это просто маппинг одного типа на другой, для него не нужно Provide
@user-li9be3jj3z
@user-li9be3jj3z 2 года назад
Эх, чичас бы на стройку или охранником на смену выйти....
@AndroidBroadcast
@AndroidBroadcast 2 года назад
???
@jojomajo
@jojomajo 2 года назад
проблемы с головой?
@user-li9be3jj3z
@user-li9be3jj3z 2 года назад
@@jojomajo у тебя точно да
@jojomajo
@jojomajo 2 года назад
@@user-li9be3jj3z однозначно, куда мне до тебя
@andreikravchenko5612
@andreikravchenko5612 2 месяца назад
На видео ошибка. Lazy не работает для типов, которые отмечены @AssistedFactory, соответственно сделать фабрику фабрики вьюмодели как на видео нельзя, после запуска будет ошибка: Dagger does not support injecting Lazy, Producer, or Produced when T is an @AssistedFactory-annotated type
@d.mertsalov
@d.mertsalov 2 года назад
Спасибо!
Далее
Dagger Component, Subcomponent, Scope, Reusable
22:38
Самый надежный автомобиль
01:00
Просмотров 676 тыс.
Hilt - лёгкий старт в Dagger
32:26
Просмотров 14 тыс.
Dagger Multibindings. Plugin architecture [RU]
17:24
Просмотров 14 тыс.
Все про Dagger Hilt в Android Studio + Kotlin
52:11
MVI в Android на практике
19:20
Просмотров 14 тыс.
ЛУЧШИЙ ПОВЕРБАНК ОТ XIAOMI
0:39
Просмотров 15 тыс.
Holographic transparent flexible LED panel.
0:20
Просмотров 3,5 млн
WWDC 2024 - June 10 | Apple
1:43:37
Просмотров 10 млн