Русский
Русский
English
Статистика
Реклама

Wwdc 2020

Как смотреть WWDC 2020, если ты не разработчик

30.06.2020 14:13:12 | Автор: admin

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


В этой статье расскажу, как сориентироваться в веренице всего и поделюсь нашим опытом работы с материалами конференции. Но для начала немного поговорим про то, что же такое WWDC.



Даб-даб ди си


Именно так произносится название WWDC или просто Даб-даб. Это конференция для тех, кому не безразлична судьба продуктов Apple. С 1983 года они знакомят разработчиков со всего мира с программными новинками и технологиями. А также подводят итоги прошедшего года и делятся планами на следующий.


У большинства людей конференция ассоциируется с Keynote, на которой Apple рассказывает о программных новинках, для кого-то ещё и с Apple Design Awards, где награждают лучшие приложения по версии экспертов эппла. Но самое интересное начинается дальше. В течении одной недели абсолютно закрытая компания Apple приоткрывает завесу тайны над разработками и даёт пообщаться с инженерами и сотрудниками. Для этого проводится около 100 сессий, на которых инженеры рассказывают про различные аспекты связанные с анонсированными новинками и про то, как правильно разрабатывать свои продукты с их учётом. Если сессий недостаточно или есть вопросы, то на лабах можно задавать инженерам любые вопросы, связанные со своими проектами или с только анонсированными технологиями. Также проходит много событий, концертов встреч и подкастов вживую.


Если хочется окунуться в атмосферу, то можно почитать текстовую трансляцию Егора Толстого про поездку на WWDC 2017 года или ребят из RedMadRobot в прошлом году.


Но если раньше для этого нужно было испытать удачу и получить билет, прилететь в США, то в 2020 конференция стала ближе как никогда. И Apple подошли к этому основательно.


keynote_epic


Только посмотрите Keynote и Platform state of the Union, который из стандартной презентации со сцены и сменой ведущих сменился в шоу с эпичными переходами. Остальные сессии хоть и не такие эпичные, но стали заметно живее и теперь лучше смотрятся онлайн.


Где смотреть?


Но для начала определимся как смотреть.
В этом году Apple неплохо обновили своё приложение для разработчиков и добавили возможность просматривать сессии прямо в нём. Если по каким-то причинам официальное приложение не подходит, то все ещё актуально приложение WWDC для macOS.


Если приложения не для вас, то в браузере всё также доступно и все сессии можно посмотреть на странице в портале разработчиков.


Как правило, лекции содержат довольно много общей информации, поэтому большую часть можно смотреть в ускоренном формате или ограничиться просмотром слайдов, конспектами и транскриптами.


  • На сайте https://asciiwwdc.com собираются текстовые версии докладов. (Транскрипты обновляются обычно в течении месяца после завершения очередной WWDC)
  • Сообщество делится конспектами сессий на GitHub, например https://github.com/Blackjacx/WWDC и https://wwdcnotes.com

Что смотреть?


Чтобы не привязываться к конкретным ролям пройдёмся по основным этапам жизни любой фичи проекта:


  • Идея и гипотезы.
  • Проектирование и прототипирование.
  • Разработка и контроль качества.
  • Бета тестирования и релиз.

Для каждой мы подобрали топ наиболее интересных и полезных по нашему мнению сессий. Поехали!





Идея / гипотеза


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



Проектирование и планирование


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


Всем кто отвечает и за эту стадию работы над проектом рекомендуем посмотреть:



Также все материалы про дизайн уже с любовью собраны Apple:



Разработка и контроль качества


Не хочется сильно подробно останавливаться на разработчиках, про WWDC для разработчиков традиционно много будет написано статей и гайдов. Уже появился неплохой обзор от Пола Хадсона и много ссылок в его репозитории на GitHub.


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


Сессии про тестирование собраны, как и сессии для дизайнеров, в отдельный раздел и в подборке The suite life of testing.


Бета тестирования и релиз


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


Также на этой WWDC особое внимание было уделено работе Entreprise. Под это направление отвели отдельную подборку и анонсировали разные интересный фичи в iOS. Чего только стоит Local Push Connectivity, благодаря которому уведомления можно будет рассылать в рамках локальной сети.


Также будет полезен обновлённый раздел с гайдлайнами по ревью.


Наш топ сессий на эти тему тестирования:



Релиз только начальный этап в разработке фичи. На этом этапе собираем аналитику и обратную связь от пользователей, фиксим критические баги, смотрим метрики, которые показывают, что критерии успешности фичи выполняются или не выполняются. В итоге появляются новые идеи и гипотезы, которые начинают новый виток в нашем цикле разработки.


Today@WWDC



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



А что, если нужно поддерживать старые iOS?


Это отличный повод пересмотреть сессии с WWDC 2-х летней давности.


Как правило, внедрение новинок в проекты компаний замедляются из-за поддержки старых версий iOS, но в то же время это даёт повод пересмотреть сессии и фичи прошлых лет и улучшить текущие продукты. В сети и на хабре можно найти много гайдов на эту тему. Вот неплохие на мой взгляд:


  • Гайды по просмотру WWDC 2016-2019 от UseYourLoaf.
  • Подборка нововведений WWDC 2019 от Патрика Балестры.

Как смотреть?


Количество информации очень много, а с каждой неделей после WWDC её становится ещё больше, так как появляется огромное количество статей и обзоров на вышедшие технологии.


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


Чтобы не утонуть в потоке сессий и не смотреть всем одно и то же, темы распределяются по интересам между разработчиками. Для этого у нас есть отдельный канал в Slack и сводная таблица в Google Docs.


Также, мы дополнительно отранжировали темы по релевантности к нашим проектам и платформам.


После просмотра сессии пишется небольшое резюме в Slack, оценивается полезность, чем интересен и что хотелось бы применить на практике.


Пример обзора
Пример обзора


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


Часть результатов прошлогоднего брейншторма
Часть результатов прошлогоднего брейншторма


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


На вышеописанные процедуры в прошлом году у нас ушло около месяца: 2 недели на просмотр сессий, 1 неделя на обсуждение и отбор тем, пара дней на проведение общего мозгового штурма и оформление результатов. Тем самым мы продлили праздник WWDC у себя в компании ещё на один месяц после его завершения, получили на выходе большое количество идей для новых фич для интеграции в наши проекты и планов для внутренних исследований.


Надеемся, что наш гайд поможет в знакомстве с новинками представленными Apple и показала, что WWDC может быть полезна не только разработчикам. А если ты разработчик, то отправь его или расскажи про сессии своим коллегам.


Желаем приятного просмотра!

Подробнее..

SwiftUI 2020. Что изменилось?

24.06.2020 16:09:31 | Автор: admin
Приветствую вас, жители Хабра и все интересующиеся разработкой под IOS. На связи Анна Жаркова, Senior iOS/Android разработчик компании Usetech
Сегодня мы поговорим о тех изменениях и новшествах, которые нам представляет Apple на WWDC 2020. А именно про доработанную и даже переработанную версию фреймворка SwiftUI.

Эта технология для декларативной разработки приложений была представлен в прошлом году на WWDC 2019. Если вы с ней еще не работали, то рекомендую заглянуть сюда

Итак. Apple и их инженеры внимательно весь этот год следили за обзорами, статьями, решениями и комментариями от разработчиков-энтузиастов. В конце видео What's new in SwiftUI они выражают благодарность всем неравнодушным за помощь.

Теперь SwiftUI позиционируется как полноценный инструмент для разработки под разные платформы (от watchOS до macOS):



Причем разработку под разные платформы можно вести в едином проекте. В Xcode 12 появляется шаблон для упрощения создания такого решения:



Система создаст проект с Shared-блоком и платформенными таргетами, где вы можете добавлять что-то специфичное для конкретной платформы:



Может показаться, что где-то это вы уже видели в Kotlin Multiplatform. Что ж, видимо, в этом году тренд на явное заимствование у конкурентов.

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

Расширили поддержку контролов. Теперь практически все контролы UIKit портированы на SwiftUI



Немаловажно, что в SwiftUI появляется аналог UICollectionView LazyVGrid/LazyHGrid, использующий GridItem:



Кстати, в процессе работы над новым контролом Apple оптимизировали работу на UITableView/UICollectionView в UIKit.

Появились средства поддержки адаптивности в настройке параметров UI (размеры контролов, шрифт, отступы и т.п). Например, атрибут @ScaledMetric у настраиваемой величины:



Также расширили поддержку фреймворков на SwiftUI.



Теперь можно использовать их вместе с ViewState:

Map(coordinateRegion: <#T##Binding<MKCoordinateRegion>#>)

Добавили поддержку Document Based Apps, Widgets, App Clips. Последние 2 являются новыми фичами iOS SDK 14. Виджеты практически аналог того, что было в Android.

Туториалы и видео работы с ними будут представлены вот-вот на WWDC 2020.

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

1. Оптимизирована работа с памятью. Memory Performance.

Это глобальное изменение для Swift 5.3 в целом. Переход внутри на структуры позволяет использовать передачу по значению вместо ссылок, тем самым сокращая размер кучи (heap), размер бинарников и времени на компиляцию.



Кстати, некоторые в самом SwiftUI стали использовать Lazy подход. Например, те же списки и LazyVGrid/LazyHGrid(аналог UICollectionView). По идее, это должно убрать проблему с инициализацией View сразу. Однако, пока еще ничего не было сказано про NavigationLink .

2. Использование DSL внутри блоков ViewBuilder.

Ура, теперь мы можем добавить if/else или switch-case в декларативных блоках в своих целях. Например, сделать фабрику Child View внутри родительского View. Или внутри NavigationView.



Это очень круто.

Также теперь можно проверять условие по @PropertyWrapper внутри блока:

@State private var isVisible = trueif isVisible == true {    Text("Hello") // Only rendered when isVisible is true.}

3. Теперь можно создать приложение 100% на компонентах SwiftUI.

Да-да. Для этого Apple придумали, как обойтись без AppDelegate, SceneDelegate и UIHostingViewController.

С помощью аннотации main и протоколов App, Scene вы сможете этого достичь:

@mainstruct testmultiplatformApp: App {  @SceneBuilder var body: some Scene {        WindowGroup {            MailViewer()        }        Settings {            SettingsView()        }    }}

main сигнализирует, что это новая входная точка вашего приложения. Реализация протокола App обязательна. Обратите внимание на тип свойства body структуры App. В приложении может быть одна или несколько так называемых сцен, каждая из которых реализует протокол Scene. У каждой сцены есть свой root View и свой жизненный цикл. Если вы хотите использовать несколько сцен (как, например, в многооконном приложении), то необходимо поставить у body атрибут SceneBuilder. По сути это механизм инкапсуляции UISceneDelegate и его логики.

WindowGroup используется для создания единого полноразмерного экрана, как и UIWindow/UIWindowScene.

Однако, мы помним, что SwiftUI это надстройка над UIKit. И UIKit остался внутри. Если мы запустим даже вот такое шаблонное приложение, то в иерархии View мы увидим:



И UIHostingViewController на месте, и UIWindowScene. Но внутри. Да, для инициализации это очень упрощает работу. Но продумали ли они решение, чтобы не пришлось возвращать в приложение UISceneDelegate с явным заданием всей навигационной структуры.

Отслеживание lifecycle сцены сокращается до метода:

public func onChange<V>(of value: V, perform action: @escaping (V) -> Void) -> some Scene where V : Equatable

Вот так предлагают использовать данный метод для отслеживания перехода приложения в фоновое состояние:

  ///    /// Use this modifier to trigger a side effect when a value changes, like    /// the value associated with an ``SwiftUI/Environment`` key or a    /// ``SwiftUI/Binding``. For example, you can clear a cache when you notice    /// that a scene moves to the background:    ///    ///     struct MyScene: Scene {    ///         @Environment(\.scenePhase) private var scenePhase    ///         @StateObject private var cache = DataCache()    ///    ///         var body: some Scene {    ///             WindowGroup {    ///                 MyRootView()    ///             }    ///             .onChange(of: scenePhase) { newScenePhase in    ///                 if newScenePhase == .background {    ///                     cache.empty()    ///                 }    ///             }    ///         }    ///     }    ///    /// The system calls the `action` closure on the main thread, so avoid    /// long-running tasks in the closure. If you need to perform such tasks,    /// dispatch to a background queue:    ///    ///     .onChange(of: scenePhase) { newScenePhase in    ///         if newScenePhase == .background {    ///             DispatchQueue.global(qos: .background).async {    ///                 // ...    ///             }    ///         }    ///     }    ///    /// The system passes the new value into the closure. If you need the old    /// value, capture it in the closure.    ///

4. Изменение предлагаемой архитектуры для SwiftUI.

Видео-презентации еще не было, но судя по документации на сайте, это уже не MVVM, а MVI или Redux:



Да-да, обратите внимание на изменение связей.

Что ж, это еще одно подтверждение тренда на заимствование у конкурентов. Пока Google вдохновляются SwiftUI для новой версии JetPack Compose, Apple вдохновляются предложениями Google. Ну а факт заимствования у энтузиастов Apple и не скрывали. Статей, посвященных использованию именно Redux/MVI с SwiftUI, в сети очень много на разных языках еще с прошлого года: ссылка.

Как пример.

И это, конечно, далеко не все. Сессия в самом разгаре, интересное еще впереди. Но в принципе этого уже достаточно, чтобы понять, что меняется многое.

Не расстраивайтесь, если у вас были планы на работу с несовершенствами SwiftUi в прошлой реализации. По крайней мере, вы знаете базу и сможете перестроиться на новую версию.

В общем, присоединяйтесь к WWDC 2020. И ждите новых статей на Хабре)

Подробнее..

Развитие ARKit в этом году и новая возможность в ARKit 4 Location Anchors

28.06.2020 16:16:57 | Автор: admin

В последние годы я очень активно работаю с сфере разработки нативных игр под платформу Apple и интересуюсь возможной интеграцией игровых процессов в AR. Поэтому стараюсь следить за всеми обновлениями которые ежегодно анонсируются на WWDC. Хотя за последние годы не было никаких обновлений для SpriteKit, SceneKit и GameplayKit, но Apple продолжает активно обновлять и продвигать ARKit и RealityKit которые можно интегрировать с этими игровыми инструментами.

Хотя Apple мало уделила внимания дополненной реальности во время презентации на WWDC, но выпущенная на этой неделе новая версия комплекта программного обеспечения для разработчиков (SDK), действительно может оказаться весьма полезной и интересной для всех, кто занимается созданием AR-приложений. В ARKit 4 представлены новые возможности для разработчиков, которые доступны на всех iOS/iPadOS устройствах с процессором A12 Bionic и выше.

Как можно видеть, за последние годы, инвестиционные вложения Apple в развитие AR-технологии оказались вполне оправданными, поскольку, используя компьютерную визуализацию, машинное обучение и функцию определения движения теперь можно создавать более сложный мир интерактивной компьютерной графики, с помощью стационарной камеры. Развитие данной технологии оказалась даже более успешным и результативным, чем попытки конкурентов создать систему виртуальной реальности на базе смартфона.

Инструмент также добавляет новые важные возможности обнаружения глубины, доступные на устройствах, оснащенных Apple LiDAR Scanner (в настоящее время доступно только в последних iPad Pro). Но, пожалуй, самое важное, что имеется в ARKit 4 это привязка к местоположению, которая позволяет разработчикам размещать виртуальный объект в определенном месте в реальном мире.

LiDAR: усовершенствованная система дистанционного отслеживания


Apple представила API Scene Geometry в ARKit 3.5 после выпуска линейки iPad Pro со сканерами LiDAR. Я ожидаю, что Apple добавит сканеры LiDAR в свои iPhone следующего поколении, которые планирует выпустить к концу этого года, так что именно эта функция, скорее всего, будет самой обсуждаемой во время следующего запуска.

Новый API Scene Geometry позволяет собирать информацию с помощью LiDAR-сканера и на базе полученных данных создавать топологическую карту окружающего мира. Эта информация может использоваться для идентификации определенных физических объектов, размещения их на сцене и создания игровой симуляции взаимодействия между объектами реального и виртуального мира.

ARKit 4 поможет размещать виртуальные объекты впереди или позади реальных людей и реалистично показывать события, используя функцию Ray Casting, чтобы размыть грань между тем, что действительно реально, и цифровым контентом.

В iOS 14 Apple расширяет возможности iOS устройств при поддержке сканера LiDAR, чтобы лучше определять расстояние между ними и объектами в виртуальной среде. Машинное обучение позволяет объединять цветное RGB-изображение, полученное с широкоугольной камеры устройства, с показателями глубины со сканера LiDAR для создания плотной глубокой картины. Эти данные глубины обновляются с частотой 60hz, что позволяет iOS не просто отобразить объекты на сцене, а обеспечить их размещение в реальном времени в виртуальной среде.

LiDAR также позволяет усовершенствовать функцию, называемую Ray Casting, которая представляет собой метод рендеринга, использующий вычислительную геометрию для создания трехмерного пространства на двухмерной плоскости. К тому же, Apple предоставила возможности отслеживания объектов еще в предыдущей версии ARKit, но их применение было доступно только для устройств с фронтальной камерой True-Depth. ARKit 4 существенно расширяет эти возможности, сделав доступной функцию отслеживания лица для всех устройств, оснащенных процессором A12 Bionic или более поздней версии, включая недавно выпущенный iPhone SE нового поколения. Отслеживание лиц позволяет разработчикам создавать приложения, которые помещают изображение человека поверх виртуального контента, и наблюдать за его выражением в режиме реального времени.


Location Anchors


Безусловно, возможности, предоставляемые сканером LiDAR весьма впечатляюще, на фоне всех новых функций, анонсированных Apple. Новая технология Location Anchors в ARKit 4 теперь выводит AR-контент более высокого качества на улицу, позволяя разработчикам задавать долготу и широту для размещения виртуальных объектов. Затем ARKit 4 использует эти координаты и данные из Apple Maps для размещения AR объекта в определенном месте, на определенной высоте в реальном мире.

Процесс управления AR-интерфейсом следующего поколения получил название Location Anchors. Он позволяет точно зафиксировать ваше устройство по отношению к окружающей среде и, по утверждениям Apple, это можно выполнять значительно точнее, чем с помощью GPS. Весь этот процесс управляется на основе передовых методов машинного обучения локально на вашем устройстве.

В результате, когда разработчик помещает виртуальный объект в реальный мир, например, виртуальную скульптуру на оживленной площади, данный объект будет сохраняться и отображаться в этом месте таким образом, чтобы каждый, кто просматривает его с помощью AR-устройства Apple, мог его увидеть в данной локации. Location Anchors вначале появится в крупных городах, таких как Лос-Анджелес, Сан-Франциско, Чикаго, Майами и Нью-Йорк, а затем, уже в конце этого лета, станет доступно еще несколько городов.
Пример работы Location Anchor из WWDC сессии


Важность Location Anchors могут оценить многие разработчики и это демонстрирует далеко идущие планы Apple в развитии устройств и технологий, связанных с AR. Сегодня многие стартапы, ориентированные на технологии дополненной реальности, пытаются занять инновационный сегмент рынка, чтобы развивать ее функциональные возможности, а Apple спокойно запустила множество новых функций в ARKit 4 на этой неделе, причем, без всякой помпезности.

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

Как работает Location Anchors


С ARKit 4 теперь без проблем можно привязать AR-контент к определенному месту в реальном мире, просто задав координаты. Приложения на основе AR, которые ранее базировались на распознавании изображений или сканировании кода для запуска действий в определенном месте, теперь имеют возможность обновляться в реальном времени и использовать преимущества привязки к местоположению.

В рамках преобразования базы данных Apple Maps, компания длительное время собирала данные с камер и 3D LiDAR на городских улицах в разных уголках мира. Для привязки к местоположению ARKit загружает виртуальную карту, окружающую ваше устройство из облака и сопоставляет ее с данными камеры. Используя GPS, ARKit может быстро и точно определить ваше местоположение в реальном мире. Вся обработка информации происходит при помощи технологии машинного обучения прямо на вашем устройстве.

Доступность


Как я уже отмечал, функция Location Anchors или, другими словами Geo-Tracking, поддерживается на всех устройствах с GPS и чипом A12 и более новых моделях. Поскольку функция требует, чтобы эта область уже была ранее нанесена на карту Apple, она доступна только в определенных городах США. По состоянию на июнь 2020 года, в зоне ее поддержки оказалось только пять городов, но я очень надеюсь, что далее Apple будет расширять зону поддержки.

Geo-Tracking базируется на новой ARConfiguration, получившей название ARGeoTrackingConfiguration, которая позволяет легко проверять наличие совместимых устройств и их доступность.

Вначале следует проверить поддерживает ли ваше устройство данную фичу:
guard ARGeoTrackingConfiguration.isSupported else { return }

Теперь вы можете проверить, находится ли устройство в поддерживаемом городе. Если это так, запустите конфигурацию геотрекинга в ARView. Если вы используете RealityKit, вы не можете применить автоматическую настройку, поэтому придется запустить ее вручную.
ARGeoTrackingConfiguration.checkAvailability { available, error in    guard available else { return }    arView.session.run(ARGeoTrackingConfiguration())}

Важное примечание. Функция checkAvailability требует наличия сетевого подключения для загрузки AR ресурсов. Available будет false, если устройство не подключено к интернету.

Построение Location Anchors


Как же происходит функционирование Location Anchors? Ведь известно, что ARKit пользуется собственной системой координат для определения относительного положения устройства, в то время как в реальном мире местоположение описывается с широтой и долготой. В геотрекинге эта проблема решена созданием единой системы координат. Оси ARKit автоматически совпадают с компасом, где ось X указывает направление на восток, а ось Z на юг.

Все, что вам нужно знать для создания ARGeoAnchor это отдельные GPS-координаты. Вот как можно создать привязку, например, для определения местоположения моста Golden Gate Bridge. Для большей надежности используем систему координат с точностью до десятичных знаков.
let coordinate = CLLocationCoordinate2D(latitude: 37.8185, longitude: -122.4738)let geoAnchor = ARGeoAnchor(name: "Golden Gate Bridge", coordinate: coordinate)

При желании можно указать высоту в метрах, которая, по умолчанию, принята заданием параметров над уровнем моря.
let geoAnchor = ARGeoAnchor(name: "Golden Gate Bridge", coordinate: coordinate, altitude: 67)

Теперь можно добавить анкор к главной сцене. В RealityKit это работает на основе инструмента AnchorEntity из ARGeoAnchor.
arView.session.add(anchor: geoAnchor)let geoAnchorEntity = AnchorEntity(anchor: geoAnchor)arView.scene.addAnchor(geoAnchorEntity)

Теперь, создавая приложение, можно добавлять любые другие объекты в привязке к выбранному местоположению, вращая и располагая их, как вам нравится, в реальном пространстве.

Преобразования между пространственными координатами


Если вы хотите получить координаты GPS для определенной точки в сцене вашего приложения, ARKit позволит легко сделать это с помощью ARGeoAnchor и системы координат XYZ ARKit. Достаточно одного касания на экране и привязка к местоположению готова!
let point = SIMD3<Float>([0, 1, -2])arView.session.getGeoLocation(forPoint: point) { coordinate, altitude, error inlet geoAnchor = ARGeoAnchor(coordinate: coordinate, altitude: altitude)}

Вот так все просто и доступно.

Прогресс AR-технологи неизбежен


Многие, кто пристально следит за развитием AR-технологий, периодически сетуют на медленный темп продвижения данного сектора технологий. Безусловно, многие из нас не отказались бы иметь, например, AR-очки от Apple уже сегодня и сейчас. В действительности, это достаточно сложная технология, и здесь более важна не скорость, а правильное ее внедрение. В дополнение к реальным проблемам по созданию таких устройств, которые связаны с оптикой, временем автономной работы, беспроводным подключением и многими другими аспектами, требуется еще и хороший AR контент с глубоким пониманием и отражением нашего реального, постоянно меняющегося мира. Лишь немногие компании имеют ресурсы, чтобы осилить данную проблему самостоятельно, среди них, кроме Apple, в числе более успешных, можно выделить Microsoft и Niantic, недавно приобретшей стартап 6D.AI.

Еще одну проблему по-прежнему создает недостаток аппаратных и программных платформ, на которых сможет работать AR-контент. С помощью ARKit 4 и iOS 14 Apple может существенно укрепить свои позиции в качестве крупнейшей в мире AR-платформы на рынке современных технологий, предоставив разработчикам новые инструменты для создания AR-приложений, которые давно ждут пользователи.

WWDC сессия посвященная новинкам в ARKit 4:
developer.apple.com/videos/play/wwdc2020/10611
Пример использования Location Anchors c исходным кодом:
developer.apple.com/documentation/arkit/tracking_geographic_locations_in_ar
Подробнее..

Apple WWDC 2020 что нового в тестировании iOS

03.07.2020 18:07:41 | Автор: admin
Привет, меня зовут Сергей, и я тестирую iOS приложения в Exness. В конце июня 2020 г. закончилась очередная WWDC. Давайте разберемся, что же она принесла нового в мир тестирования iOS приложений.

image

Но вначале краткий исторический экскурс: Apple WWDC (WorldWide Developers Conference), или просто даб-даб, это конфа, которую Apple с конца восьмидесятых проводит в Калифорнии. В этом году конференция впервые прошла в онлайн-формате. И если раньше билеты разыгрывались в лотерее, и тем, кто не получил желанного email, оставалось довольствоваться видео с сайта https://developer.apple.com/videos/, то в этом году по понятным причинам других вариантов не было: видео смотрели все.
Итак, что же там можно было высмотреть по тестированию?
Сразу оговорюсь, что на WWDC 2020 не было какой-то большой общей сессии, посвященной тестированию в экосистеме Apple, как в прошлые годы (Testing in Xcode 2019 и Whats new in testing 2018, 2017). Новинки тестирования в 2020 размазали на шесть мини-сессий. Поехали!

XCTSkip для ваших тестов


В Xcode 11.4 добавили новый API для управления запуском тестов в зависимости от условий XCTSkip.
Часто в тестах, особенно интеграционных, есть условия или требования, которые нелегко замокать. Например, приложение имеет какой-то специфический функционал для айпада, который не работает на айфоне. Или какие-то фичи для конкретной версии операционной системы.
И раньше, когда тесты доходили до подобных кейсов (проверка айпад-only функционала на iPhone), стоял выбор:

  • Закончить выполнение тестового набора;
  • Пометить тест как пройденный и пойти дальше;
  • Зафейлить тест.

Теперь у нас есть ошибка, при возникновении которой текущий тест перестает выполняться и помечается как пропущенный.
Таким образом, теперь в XCTest стало три статуса для пройденного теста вместо двух:

image

Подробнее тут и тут.

Обработка прерываний и алертов в UI тестах


Обработка прерываний и алертов была в XCTest и раньше, однако в сессии механизм его работы был раскрыт более подробно. Мне показалась интересной новая функциональность, добавленная в Xcode 11.4, iOS/tvOS 13.4 и macOS 10.15.4, а именно, сброс пермишенов (aka protected resources).
Суть в следующем: если раньше вы, например, в тесте #1 дали приложению доступ к камере или контактам, то потом, в тесте #2, #n этот доступ так просто не отобрать. Чтобы сделать это, придется переустанавливать приложение.
Теперь с помощью API для сброса авторизации для protected resources можно отобрать ранее выданный доступ:

Class XCUIApplication {open func resetAuthorizationStatus(for: XCUIProtectedResource)}

Сброс настроек для пермишенов заставляет приложение вести себя так, как будто оно ни разу до этого не запрашивало у пользователя доступ к protected resources.
Это позволяет пройти все пути с выдачей и забором пермишенов для контактов, календаря, фото, микрофона, камеры и геолокации. На iOS еще дополнительно можно сбросить доступ к Bluetooth и Keyboard network access, а начиная с Xcode 12 / iOS 14, к данным Health. На Mac OS можно сбросить доступ к директориям Desktop и Downloads.
Ниже пример, как сбросить доступ приложения к фото:

// Examplefunc testAddingPhotosFirstTime() throws {let app = XCUIApplication()app.resetAuthorizationStatus(for: .photos)app.launch()// Test code...}

Важно помнить, что часто (но не всегда) при сбросе пермишенов приложение убивается.

Подробнее тут, тут и тут.

Устраняем лаги анимации с помощью XCTest


Лаги анимации, или hitches такое поведение, когда фрейм появляется позже, чем ожидалось.
В лекции рассказано, как предотвратить появление лагов анимации вашего приложения путем замеров и тестирования с помощью Performance XCTests.
Также приводятся лучшие практики, и определено, какие лаги терпимы, а на какие стоит обратить внимание:

image

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

Триаж и диагностика упавших тестов


Часто починка упавших тестов это боль, которая занимает много времени и ресурсов.
В Xcode 12 появится новое API, которое должно облегчить починку упавших тестов. API должно помочь быстрее ответить на вопросы: что, как, почему и самое главное где упало?
Если раньше после того, как тест упал, приходилось искать место падения в
Issue navigator или report navigator, то с Xcode 12 процесс поиска упростился: теперь место падения подсвечивается в самом тесте.
Ошибка с выделением серым цветом появляется, если строка обращается к какой-то другой строке в дальнейшем:

image

И красным цветом, если ошибка произошла непосредственно в этой строке:

image

Удобная новая фича открытие редактора кода не в отдельном окне, а прямо в report navigator:

image

Кроме того, в Xcode 12 добавился новый объект XCTIssue, который, помимо того, что инкапсулировал в себя данные об ошибках, которые ранее собирал в себе XCTest (сообщение, путь, номер строки и флаг Expected), теперь добавляет:

  • Distinct types;
  • Detailed description;
  • Associated error;
  • Attachments.

Подробнее тут и тут.

Пишите тесты для того, чтобы они падали


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

Используйте человекочитаемые сообщения в ассерt`ах:

image

Убедитесь, что используйте подходящий для вашей ситуации тип ассерt`а:

image

Unwrap'те optional'ы, чтобы ваши тесты падали, выбрасывая ошибку, а не крашились. Swift предоставляет несколько способов для этого, но в тестах, как правило, используют XCTUnwrap, который являет собой упрощение конструкции guard let.

image

Используйте waitForExistence() вместо sleep() для асинхронных ожиданий.

Используйте XCTContext.runActivity() для повышения читабельности лога выполнения теста:

image

И если хотите добавить дополнительное логирование, можно добавить вложение, приложить скриншот или вывод дебагера, как здесь. Это функция особенно полезна, если ваши тесты запускаются в CI/CD.

image

Подробнее тут.

Получайте результат тест рана быстрее


Обидно, когда утром в понедельник вы обнаруживаете, что запущенная в пятницу вечером длинная джоба так и не отработала до конца, зависнув на середине или вообще в самом начале. И вам предстоит начать рабочую неделю с разбора полетов: почему это произошло? Как избежать подобной ситуации в будущем? Как я мог спустить девять тысяч на коктейли за один вечер?
В Xcode 12 появились инструменты для защиты от зависаний. Это новая опция тест плана Execution Time Allowance.
Когда опция включена, Xcode устанавливает временной лимит исполнения каждого теста.
Если лимит превышен, Xcode делает следующее:

  1. Собирает отчет (spindump);
  2. Убивает зависший тест;
  3. Перезапускает тест раннер, чтобы оставшаяся часть сьюта смогла выполниться.

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

image

Еще это можно сделать опцией команды xcodebuild:

xcodebuild option-default-test-execution-time-allowance <seconds>

Аналогично можно задать максимальное время выполнения теста:

image

xcodebuild option-maximun-test-execution-time-allowance <seconds>

Даже если вам нужно задать время выполнения для какого-то конкретного теста или тестового класса, то это тоже возможно с помощью executionTimeAllowance API:

Class XCTestCase: XCTest {var executionTimeAllowance: TimeInterval // с округлением до минуты}

Точная настройка выполнения того или иного теста позволит вам сэкономить время, но и это еще не все, что можно сделать для ускорения прохождения длинного тест сьюта.
Xcode 12 позволяет запускать тесты на нескольких девайсах одновременно. Эту фичу назвали Parallel Distributed Testing. Польза от запуска тестов на нескольких девайсах очевидна приличная экономия времени.

image
image

Но, к сожалению, есть и подводные камни: порядок запуска тестов в параллели не детерминирован, нет никакой гарантии, что на девайсе #1 после теста номер 5, будет выполнен тест номер 6. Этот факт обязательно нужно учитывать при планировании запуска тестов с помощью Parallel Distributed Testing.
Вообще идея запусков тестов в параллели не нова. Такая возможность была и до Xcode 12, но именно в Xcode 12 появилась возможность запускать тесты на реальных девайсах (пока только с помощью xcodebuild).

Команда для запуска параллельных распределенных тестов выглядит следующим образом:

xcodebuild test    -project MyProject.xcodeproj    -scheme MyProject    -parallel-testing-enabled YES    -parallelize-test-among-desinations    -destination 'platform=iOS,name=iPhone 11'    -destination 'platform=iOS,name=iPad pro' 

Подробнее тут.

На этом обзор новых тесто-фич с WWDC 2020 закончен. Спасибо, что дочитали до конца. Надеюсь, эта статья будет вам полезной. Happy testing!
Подробнее..

Перевод Пользовательский инструмент, который не помешал бы вам в вашем приложении

11.08.2020 20:13:10 | Автор: admin
В преддверии старта базового курса iOS-разработчик подготовили для вас еще один интересный перевод.





Примечание: Эта статья был написана незадолго до WWDC 2020, но вам следует читать ее только после просмотра конференции. Все, о чем здесь пойдет речь, уже доступно вам не нужно скачивать бета-версию или ждать до осени, чтобы использовать это. Также, я собираюсь обновить статью, если что-нибудь относящееся к ней зарелизят на WWDC

Большинство из вас, вероятно, уже работали или в настоящее время работают над приложением, для которого значительная часть функциональности зависит от связи с сервером посредством HTTP. Когда что-то работает не так, как ожидалось, или вы просто хотите понять область кода, с которой вы еще не знакомы, часто бывает полезно посмотреть HTTP-запросы, идущие между приложением и сервером. Какие запросы были сделаны? Что именно отправляет сервер? Для этого вы, вероятно, используете такие инструменты, как Charles Proxy или Wireshark.

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

В качестве подготовки к WWDC на следующей неделе1, я (пере)смотрел пару выступлений из предыдущих WWDC. Так или иначе, я полностью упустил, что ядро инструментов было переписано, чтобы унифицировать их, и сделать его намного проще для создания пользовательских инструментов для Xcode 10. Кроме того, WWDC 2019 оказался отличным введением в инструменты, чего мне не хватало все эти годы.

Круто, теперь вы можете писать собственные инструменты для измерения того, что инструменты обычно не измеряют. Но что вы можете измерить и насколько это легко? Я бы сказал: почти все и не то чтобы очень сложно, достаточно быстро. Как правило, все, что вам нужно написать XML-файл, в котором будет указано, как преобразовывать signpost-указатели из вашего кода в данные для отображения в инструментах, а XML-код, необходимый для этого, не особо замысловат. Основным препятствием является то, что код, который вы пишете, вероятно, будет сильно отличаться от того, к чему вы привыкли, примеров очень мало, документация дает только общий обзор того, как это нужно делать, и хотя Xcode на самом деле довольно строго проверяет XML-файлы, автодополнения нет и мало что может упростить вам поиск ошибок. Но, потратив немного времени, можно найти нужные вам элементы, а если у вас есть пример для адаптации кода, вы сможете добиться результата довольно быстро. Здесь я как раз и собираюсь привести такой пример и постараться перечислить все полезные ссылки.

Но давайте начнем с самого начала: я хочу, чтобы каждый из вас, кто раньше использовал Charles или Wireshark для отладки своего приложения, или разрабатывал приложение, которое выполняет множество HTTP-запросов, смог создать инструмент трассировки HTTP, настроенный под ваше приложение или, по крайней мере, фреймворк. Он будет выглядеть так:



У меня ушел приблизительно один день на то, чтобы создать и отладить этот прототип (после просмотра соответствующих видео WWDC). Если вас не интересует ничего кроме кода, вы можете посмотреть его здесь.

Обзор


Самый простой способ создать пользовательский инструмент это использовать os_signpost, собственно что мы и собираемся здесь делать. Вы используете его для логирования signpost-указателей .event или .begin и .end. Затем вы настраиваете пользовательский инструмент для анализа этих os_signpost интервалов и извлечения дополнительных значений, которые вы логировали в него, настраиваете, как отобразить их на графике, как сгруппировать их, какие отфильтровать и как представлять структуры списков или деревьев/блок-схем в панели подробностей инструмента.

Мы хотим создать инструмент, который отображает все HTTP-запросы, которые проходят через нашу сетевую библиотеку, как интервалы (начало + конец), чтобы мы могли видеть, сколько времени они занимают, и сопоставлять их с другими событиями, происходящими в нашем приложении. В этой статье я использую Alamofire в качестве сетевой библиотеки инструмента и Wordpress в качестве приложения для профилирования, просто потому что у них открытый исходный код. Но вы легко сможете адаптировать весь код к вашей сетевой библиотеке.

Шаг 0: Ознакомьтесь с Instruments App


  1. Начало работы с инструментами (Сессия 411 из WWDC 2019) это очень хороший обзор инструментов. Посмотрите хотя бы раздел Orientation, чтобы ознакомиться с терминологией, такой как инструменты (instruments), треки (tracks), полосы (lanes), трейсы (traces), шаблоны (templates), подробный вид (detail view) и т. д.
  2. Посмотрите Создание пользовательских инструментов (Сессия 410 из WWDC 2018), чтобы получить представление о том, что мы здесь делаем. Если вы слишком нетерпеливы, достаточно посмотреть разделы Architecture (для получения дополнительной информации о том, как работают инструменты, и что вы на самом деле настраиваете) и Intermediate. Не ожидайте, что поймете все тонкости во время просмотра, там показано слишком много всего для одного сеанса, и они не объясняют каждую деталь из-за временных ограничений. Мне приходилось смотреть это несколько раз и искать дополнительную документацию, прежде чем я действительно смог заставить свой инструмент работать так, как мне было нужно. Тем не менее, я попытаюсь заполнить некоторые пробелы ниже.


Шаг 1: Логирование нужных вам данных в signpost-указатели


Мы хотим построить наш инструмент на signpost-указателях, то есть мы будем логировать наши данные через signpost. Alamofire отправляет Notification каждый раз, когда запрос начинается или завершается, поэтому все, что нам нужно, это что-то вроде этого2:

NotificationCenter.default.addObserver(forName: Notification.Name.Task.DidResume, object: nil, queue: nil) { (notification) in    guard let task = notification.userInfo?[Notification.Key.Task] as? URLSessionTask,        let request = task.originalRequest,        let url = request.url else {            return    }    let signpostId = OSSignpostID(log: networking, object: task)    os_signpost(.begin, log: SignpostLog.networking, name: "Request", signpostID: signpostId, "Request Method %{public}@ to host: %{public}@, path: %@, parameters: %@", request.httpMethod ?? "", url.host ?? "Unknown", url.path, url.query ?? "")}NotificationCenter.default.addObserver(forName: Notification.Name.Task.DidComplete, object: nil, queue: nil) { (notification) in    guard let task = notification.userInfo?[Notification.Key.Task] as? URLSessionTask else { return }    let signpostId = OSSignpostID(log: networking, object: task)    let statusCode = (task.response as? HTTPURLResponse)?.statusCode ?? 0    os_signpost(.end, log: SignpostLog.networking, name: "Request", signpostID: signpostId, "Status: %@, Bytes Received: %llu, error: %d, statusCode: %d", "Completed", task.countOfBytesReceived, task.error == nil ? 0 : 1, statusCode)}


Когда запрос начинается, мы логируем signpost .begin, когда он завершается, мы добавляем signpost .end. Для сопоставления завершения вызова с соответствующим началом вызова используется signpostId, чтобы убедиться, что мы закрываем правильный интервал, если несколько запросов происходят параллельно. В идеале мы должны хранить signpostId в объекте запроса, чтобы быть уверенными, что мы используем один и тот же для .begin и .end. Однако я не хотел править тип Request в Alamofire, поэтому решил использовать OSSignpostID(log:, object:) и передавать ему объект-идентификатор. Мы используем базовый объект URLSessionTask, поскольку в обоих случаях он будет одинаковым, а это позволяет OSSignpostID(log:, object:) возвращать нам один и тот же идентификатор при его многократном вызове.

Мы логируем данные посредством format string. Вам, вероятно, следует всегда разделяете два аргумента некоторой четко определенной строкой, чтобы упростить анализ на стороне инструмента, а также облегчить парсинг. Обратите внимание, что вам не нужно логировать данные в .end вызове, если вы уже логировали их в .begin. Они будут объединены в один интервал и у вас будет к ним доступ.

Шаг 2: Создайте новый проект пользовательского инструмента в Xcode.


Следуйте по шагам из Создания пользовательских инструментов (Сессия 410 из WWDC 2018) или справки Instruments App Создание проект пакета инструментов, чтобы создать новый проект пакета инструментов в Xcode. В результате вы получите базовый проект Xcode с .instrpkg файлом. Все детали мы укажем там.

Шаг 3: Сделайте все остальное


В основном вы будете следовать шагам, описанным в справке Instruments App Создание инструмента на основе данных signpost-указателей. Несмотря на то, что описания всех шагов здесь корректны, им все же не хватает множества деталей, поэтому лучше иметь перед собой пример реального пользовательского инструмента. Вы можете посмотреть на мой здесь. В основном вам понадобятся следующие части:

Схема

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

<os-signpost-interval-schema><id>org-alamofire-networking-schema</id><title>Alamofire Networking Schema</title><subsystem>"org.alamofire"</subsystem><category>"networking"</category><name>"Request"</name><start-pattern>    <message>"Request Method " ?http-method " to host: " ?host ", path: " ?url-path ", parameters: " ?query-parameters</message></start-pattern><end-pattern>    <message>"Status: " ?completion-status ", Bytes Received: " ?bytes-received ", error: " ?errored ", statusCode: " ?http-status-code</message></end-pattern><column>    <mnemonic>column-http-method</mnemonic>    <title>HTTP Method</title>    <type>string</type>    <expression>?http-method</expression></column><!-- и в том же духе --></os-signpost-interval-schema>


mnemonic является идентификатором того, что вы собираетесь ссылаться на этот столбец позже. Мне почему-то было немного странно называть столбцы так же, как переменные, поэтому я поставил перед ними префикс column. Но, насколько я знаю, необходимости это делать нет.

Инструмент

Инструмент состоит из базового определения:

<instrument>    <id>org.alamofire.networking.instrument</id>    <title>Alamofire</title>    <category>Behavior</category>    <purpose>Trace HTTP calls made via Alamofire, grouped by method, host, path, etc.</purpose>    <icon>Network</icon>        <create-table>        <id>alamofire-requests</id>        <schema-ref>org-alamofire-networking-schema</schema-ref>    </create-table>    <!-- Остальная часть определения инструмента --></instrument>


Все довольно просто. Большинство из этих полей представляют собой текст произвольной формы или относятся к материалам, которые вы определили ранее (schema-ref). Но category и icon могут иметь только небольшой набор значений, определенных здесь и здесь.

График внутри инструмента

График (graph) определяет графическую часть пользовательского интерфейса инструмента, визуальное представление, которое вы видите в области трека. Это выглядит примерно так:

<instrument>    <!-- Базовое определение инструмента -->    <graph>        <title>HTTP Requests</title>        <lane>            <title>the Requests</title>            <table-ref>alamofire-requests</table-ref>                        <plot-template>                <instance-by>column-host</instance-by>                <label-format>%s</label-format>                <value-from>column-url-path</value-from>                <color-from>column-response</color-from>                <label-from>column-url-path</label-from>            </plot-template>        </lane>    </graph>    <!-- остальные части инструмента --> </instrument>


У вас могут быть разные полосы (lane), и вы можете использовать шаблон графика (plot-template), чтобы реализовать динамическое число графиков в полосе. Мой пример содержит пример простого графика. Я не совсем уверен, почему у graph и lane есть заголовки. В дополнение к этому каждый график в plot-template также получает метку от label-format.

Список, агрегация или что там еще для подробного представления

С одним лишь графиком инструменты выглядели бы несколько неполными. Вы также хотели бы отобразить что-нибудь в подробном представлении (Detail View). Вы можете сделать это с помощью list, aggregation или narrative. Может быть даже больше вариантов, которые я еще не встречал. Агрегация выглядит примерно так:

<instrument>    <!-- Базовое определение инструмента -->    <aggregation>        <title>Summary: Completed Requests</title>        <table-ref>alamofire-requests</table-ref>        <slice>                <column>column-completion-status</column>                <equals><string>Completed</string></equals>        </slice>        <hierarchy>            <level>                <column>column-host</column>            </level>            <level>                <column>column-url-path</column>            </level>        </hierarchy>                <column><count/></column>        <column><average>duration</average></column>        <column><max>duration</max></column>        <column><sum>column-size</sum></column>        <column><average>column-size</average></column>    </aggregation>    <!-- остальные части инструмента --> </instrument>


список выглядит следующим образом:

<instrument>    <!-- Базовое определение инструмента -->    <list>        <title>List: Requests</title>        <table-ref>alamofire-requests</table-ref>        <column>start</column>        <column>duration</column>        <column>column-host</column>        <!-- Остальные столбцы ->    </list>    <!-- остальные части инструмента --></instrument>


Бонусный материал


На этом, по сути, все
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru