Telegram Web Link
Чем были прекрасны эти 2 года? При написании любой даже самой маленькой игры в голове генерировалось просто куча идей, как сделать игровой процесс лучше, какой алгоритм использовать. И как только появлялся хоть какой-то геймплей, то все свободное время я занимался лишь изучением возможностей фреймворка, общался на форумах о последних апдейтах. Сидишь вечерами и пытаешься экспериментировать над геймплеем и это очень увлекает, так как процесс полностью интерактивный. Помимо этого увидел реальное применение алгоритмам, о которых до только читал в книжках до этого. Много работал с массивами, графами, было очень интересно описывать игровые сущности классами, экспериментировал с паттернами программирования. И самое главное, что каждая новая игра заставляла поразмыслить над геймплеем и особенностями реализации.
🔥10👍1
Проработав в геймдеве 2 года, я попал в Mercury, где моим первым проектом стало приложение для ТВ приставки Roku, где программы пишутся на языке brightscript (который совсем не bright, писать на нем было не очень привычно). Помню, что было очень неудобно дебажить, так как у приставки было мало видеопамяти и она периодически подвисала, не выдерживая моего неправильного кода.
🔥83🥰2🥴1
За годы, проведенные в Mercury, также посчастливилось поработать на Ember.js, расширяя функционал админки на одном из проектов. Было сложным привести legacy-код на проекте в порядок из-за огромного количества ненужных библиотек, во многих местах наряду с ember-компонентами использовался JQuery. Со временем вошел во вкус, и даже появилась идея все переписать на новом Ember.js, но увы данную часть проекта свернули и мне так и не удалось использовать последний Ember. К слову, фреймворк довольно неплохой. Также у Ember неплохое community и отличная документация, а также есть отличная CLI, через которую можно установить большое количество ember библиотек (аддонов).
👍4❤‍🔥32
Параллельно я изучал React и React-Native. В первое время фронтовых проектов было почти столько же, сколько и мобильных, но со временем число фронтенд-проектов стало меньше и большая часть проектов, на которых я работал, была разработана на React Native. Первые впечатления от фреймворка были довольно положительные. Кроме работы с Xcode, такое большое количество настроек, каждая из которых может все поломать пугала, а обновления Xcode без немалой доли терпения точно не пережить. Но после того как настроил и привык к окружению Xcode для iOS, Android Studio для Android начала вникать в разработку кроссплатформенных приложений, используя JavaScript или TypeScript. На проектах постепенно втягивался в разработку на React Native, используя доступные библиотеки.

Однако сложность задач росла и со временем все чаще наблюдал баги, где задачи не решались стандартным способом, приходилось лезть под капот React Native модуля и разбираться. Спасибо отзывчивым коллегам и форумам, которые помогали понять, что в нативном коде есть ошибка и ее нужно поправить. Часами напролет я пытался найти решение, чтобы решить задачу. Но лезть в нативный код мне также не хотелось. Поначалу я хотел делегировать задачи, связанные с нативом, однако потом мне это надоело и я решил потихонечку разбираться и пробовать свои силы в редактировании нативного кода и исправлении существующих библиотек под свои нужды. Поначалу приходилось форкать библиотеку, но с появлением patch-package написание патчей стало гораздо проще.
🔥83❤‍🔥2
Наступил момент, когда доступных решений на некоторых проектах просто не было, кроме как использовать нативные SDK. Например, на одном из проектов нам нужно было интегрировать нативный компонент для iOS, который мы не могли найти в React Native библиотеке, на другом пришлось писать модуль для работы с платежной системой PayPall. Медленно, но верно я начал вникать и пытаться что-то сделать в нативном коде, хоть это было и довольно сложно для человека незнакомого с Objective-C.

На последнем из проектов появились задачи посложнее, например, коннектиться к стороннему девайсу — панорамной камере ricoh theta, — и показывать с нее лайв превью, фотографировать и многое другое. Пришлось писать нативный модуль с нуля для iOS и Android для отображения этого самого лайв превью. Поначалу это кажется непосильной задачей, но со временем появляется какое-то понимание. Тут мне хочется немного остановиться и рассказать про ценность и необходимость написания нативных модулей для React Native разработчика. об этом уже завтра. А пока, если есть какие-то идеи или вопросы, приглашаю пообщаться в комментариях 👇
🔥6❤‍🔥43
Всем привет! Сегодня расскажу, что же такое нативные модули и как они работают. Все приложения на React Native мы пишем, используя React, JavaScript или TypeScript. Но для определенных задач, например, для общения со сторонними девайсами для которых доступен только нативный SDK, нам придется писать нативный код. Пишем мы нативные модули на языке Objective-C или Swift для iOS, Java или Kotlin для Android. Другими словами, мы можем написать нативный модуль для доступа к нужному нам API и использовать его в нашем React Native проекте
6👍1
Старая архитектура vs новая архитектура

Когда React Native только появился, то команда Facebook предложила следующую концепцию. JavaScript-инструкция отправлялась через bridge выполняя код из нативного модуля, затем результат выполнения отправляется обратно в JavaScript-код через bridge. Эта операция является асинхронной, обозначая, что JavaScript-код может выполняться не дожидаясь возвращения результата от нативного модуля. Данный подход нужен для того, чтобы приложение осталось отзывчивым, особенно для задач, которые занимают много времени, например, обработка изображений или доступ к камере, к Bluetooth, NFC, работа с OpenGL.


Проблемы старой архитектуры
1. Большое количество событий, требующих сериализации / десериализации (передающихся через Bridge) становится бутылочным горлышком и замедляет UI.
2. Асинхронность: коммуникация через bridge всегда асинхронна, что делает невозможным выполнение синхронных вызовов между JavaScript и нативными модулями.

Разберем на примере игры с обновлениями в реальном времени, которую мы захотим написать на React Native, где пользовательские вводы отправляются в нативный код для обработки — например, для определения столкновений в физическом движке. Мост (bridge) создает задержки из-за времени, необходимого на:
• Сериализацию входных данных в формат JSON;
• Отправку данных в нативный модуль;
• Обработку данных на нативной стороне;
• Сериализацию и возврат ответа обратно в JavaScript.

Эта задержка делает приложение менее отзывчивым и может вызывать зависания и лаги в приложениях, работающих в реальном времени. Пользователи могу столкнуться с заметными задержками.
6👍1
В новой архитектуре нативные модули стали называться TurboModules. Главным нововведением стало удаление bridge и замена его на JSI (JavaScript Interface). В старой архитектуре много времени уходило на отправку и получение больших сериализованных сообщений через bridge, а TurboModules позволяют JavaScript напрямую дергать нативный код без использования бриджа.

Основные изменения:

1. Удаление bridge и добавление JSI. JSI предоставляет JavaScript прямой доступ к нативным объектам, что позволяет выполнять операции быстрее и с меньшими накладными расходами.
2. Lazy Click Me Load More, в старой архитектуре все нативные модули подгружались при загрузке приложения, а в новой только по необходимости. Соответственно есть выигрыш в памяти.
3. Бридж сериализовывал сообщения и десериализовывал, что может также замедлять работу приложения.
4. TurboModules получают и отправляют данные напрямую через JSI.
5. В TurboModules улучшено управление потоками.
6. Fabric (новый UI рендеринг) — обеспечивает более быстрый рендеринг пользовательского интерфейса. Поддерживает асинхронное обновление UI, что предотвращает блокировки основного потока. Позволяет обновлять только измененные части UI вместо перерисовки всего экрана.


Как итог TurboModules смогли улучшить производительность, оптимизировать использование ресурсов, и улучшили работу анимаций, время загрузки и отзывчивость приложения.
6❤‍🔥4👍1
Далее хочу рассказать об одном нативном модуле, который я написал, чтобы решить довольно непростую проблему в проекте.

У нас есть проект для риэлторов, в котором необходимо подключаться через приложение к 360 камере и делать панорамные снимки. На камере есть специальный баркод, такой маленький в виде data matrix, в котором зашит ssid нашей камеры. Нам необходимо было отсканировать его, считать ssid и подключиться к камере через Wi-Fi. Сперва мы искали существующие решения, но библиотеки просто не могли отсканировать такой маленький баркод, а задать область было невозможно. Перебрали немало библиотек, какие-то из них могли распарсить маленький баркод на iOS, например, библиотека react-native-vision-camera позволяла это сделать, но на Android не получалось ничего отсканировать, так как баркод был и инвертированный к тому же. Поэтому сперва его нужно было инвертировать и только потом отсканировать. Было принято решение написать кастомный нативный модуль, про который я сейчас немного расскажу. На прикрепленной картинке изображена нижняя часть камеры с кодом YN14010033, который вшит в дата матрицу справа от этого кода.
5👍2🌭2
Проблемы готовых решений
Отсутствие одинакового поведения на нескольких платформах.Так на iOS могло работать на ультра широкой камере, на Android не хотелось заводиться совсем.
1. Частые ошибки при сканировании из-за недостаточного освещения.
2. Отсутствие сканирования по области.
3. Нам необходимо было сделать процесс сканирования удобным, дабы избежать ситуации, когда пользователь пытается безуспешно несколько минут отсканировать код, но с готовыми библиотеками об этом сразу можно было забыть.
3👍2🌭2
Проблема многих библиотек под React Native в том, что они работают для общих случаев, тот же QR код платежный сканировался практически везде. Но если случай не самый обычный, то частенько не остается никаких альтернатив, кроме как взяться за написание нативного модуля, чем я в итоге и занялся, бросив попытки найти ту самую библиотеку. Самое интересное, что мозг пытается верить, что библиотечку удастся настроить под свои нужды, и довольно сложно заставить себя открывать тот же XCode и Android Studio и пытаться по мануалам написать свой первый нативный модуль, который будет работать так как нужно тебе. И здесь хочу отметить, что в этом месте я был немного разочарован в React Native, что все также возникает необходимость писать нативный код, иногда даже много нативного кода. Хотя фреймворк обещает Learn Once. Write Everywhere, мне довольно часто приходилось лезть внутрь библиотек и разбираться, почему ничего не работает и патчить на свой страх и риск. Поэтому, на мой взгляд, разрабатывать для React Native без знания натива достаточно сложно. Для простых экранов и относительно стандартного UI можно найти много библиотек, которые просто будут работать из коробки, но если требуется что-то кастомное, то же подключение к сторонним устройствам, сканирование баркодов и многое другое может отнять кучу нервов и заставить погрузиться в мир нативной разработки хоть чуть-чуть.
👍114❤‍🔥2
После того как мы обозначили важность нативных модулей в коде, предлагаю немного погрузиться в имплементацию данного модуля. Напомню, что основная цель это сканирование маленького баркода камеры для автоматического подключения к камере по Wi-Fi. Данный модуль состоит из трех частей.
1. BarcodeView — это обычная вьюха, которая показывает превью с камеры и по сути является нашим React-компонентом. Данная вьюха показывает поток с камеры, позволяя пользователю видеть картинку в реальном времени.
2. BarcodeCameraManager — это класс, который настраивает камеру и превью с ней. Для настрйоки камеры требуется создать camera input, camera output и настроить capture session. По факту данный класс полностью несет ответственность за работу с камерой, позволяя выбрать нужную камеру, настроить фокус. А также данный класс принимает колбэки, которые перенаправляют видеопоток в функцию, которая сканирует баркод.
3. BarcodeScanner — класс, где случается вся магия. Класс, который проверяет каждый видеофрейм на наличие баркода, накладывая фильтры на изображение для улучшения видимости этого баркода (накладываем grayscale для улучшения контраста и инвертируем картинку, чтобы алгоритмы Vision Framework смогли распознать код).

Приложу ссылку на исходный код, если хотите взглянуть https://github.com/kidasov/qrparser
4👍4❤‍🔥1
Далее расскажу вкратце об исходном коде. BarcodeView — это UIView, аналог View в React Native, в котором мы инициализируем инстансы BarcodeCameraManager и BarcodeDetector, в качестве параметров данный компонент принимает функцию onBarcodeRead, которая вызывается в момент успешного сканирования баркода.
При добавлении в родительскую вьюху запускается setupCameraLiveView(), чтобы включить камеру. При удалении из родительской вьюхи вызывается stopCameraLiveView() для освобождения ресурсов. Также компонент реализует протокол BackgroundListenerDelegate, при переходе в background срабатывает функция onBackgroundMove, которая останавливает работу камеры при переходе приложения в фон. При переходе в foreground срабатывает onForegroundMove, которая перезапускает камеру при возвращении приложения в foreground. Также, для дебага мы отображаем картинку, в которой мы ищем баркод и отрисовываем ее в функции
_DebugSetPhotoImagePreview. Код можно посмотреть здесь https://github.com/kidasov/qrparser/blob/main/ios/BarcodeView.swift
4❤‍🔥1
Следующим у нас идет код класса BarcodeCameraManager, который выполняет настройку камеры, захват видео и обработку кадров. Перечислим свойства данного класса.

backCamera — устройство захвата видеопотока (задняя камера в нашем случае). captureVideoOutput — выходной поток для получения видеокадров.
captureSession — сессия захвата, которая связывает входы (камера) и выходы (видео).
cameraPreviewLayer — слой для отображения видео с камеры в пользовательском интерфейсе.

В функции setupCamera мы настраиваем камеру, устанавливаем разрешение для видеопотока hd1920x1080. Затем в configureBackCamera мы находим заднюю камеру телефона, далее настраиваем input и output для нашей сессии, и в функции startCaptureSession запускаем сессию. Класс реализует протокол AVCaptureVideoDataOutputSampleBufferDelegate, что позволяет получать каждый кадр из видеопотока: метод captureOutput(_:didOutput:from:): вызывается при получении нового кадра и передает кадр в onCaptureOutput для дальнейшей обработки (для сканирования баркода).
https://github.com/kidasov/qrparser/blob/main/ios/BarcodeCameraManager.swift
❤‍🔥32
Расскажу еще об одном челлендже при попытке отсканировать наш баркод. Многие библиотеки сканировали баркод только с помощью широкогоугольной камеры (builtInUltraWideCamera), но этой камеры у нас не было на iPhone X, поэтому это был еще один аргумент в пользу написания кастомного модуля для того, чтобы баркод сканировался и на устаревших моделях iPhone. В коде мы используем builtInWideAngleCamera — это системное обозначение основной камеры на устройствах iOS. Эта камера является стандартной для всех iPhone, начиная с самых ранних моделей.
❤‍🔥32
И наконец, у нас остается класс детектора баркода BarcodeDetector, который отвечает за обнаружение штрихкодов на изображениях, используя Vision Framework от Apple. Он инкапсулирует весь процесс обработки изображения от его предварительной подготовки до распознавания баркода. Первая проблема, с которой мы столкнулись, была в том, что баркод не сканировался средствами Vision Framework, поскольку он был слишком маленький. Поэтому сперва пришлось обрезать изображение — мы использовали imageOffsetX и imageOffsetY для определения области интереса на изображении (ROI, Region of Interest).
❤‍🔥4
На вход компонент принимает три колбэка:
onPreview — показывает предварительный просмотр обработанного изображения;
onBarcodeRead — возвращает результат распознавания;
onShowError — уведомляет об ошибках.


Распознавание штрихкода просиходит в методе (detectBarcodeFromImage) Далее о том, что выполняет этот метод.
❤‍🔥4
1. Мы хотим вырезать область с баркодом из изображения полученного с камеры. Изображение с камеры отличается по размеру от превью. Для того чтобы рассчитать коэффициент, на которое оно отличается, мы используем метод calculateImageScale. Метод учитывает то, что исходное изображение повернуты на 90 градусов.Вычисляет масштаб изображения. Метод сравнивает соотношение сторон изображения и кадра, чтобы определить коэффициент масштабирования для корректной обрезки.
❤‍🔥4
2. Обрезка изображения, вычисляем область интереса (ROI) с учетом смещения (imageOffsetX, imageOffsetY) и размера (imageSize). Маленький синий квадратик на экране
❤‍🔥4
Процесс сканирования выглядит следующим образом
❤‍🔥4
2025/07/11 18:20:10
Back to Top
HTML Embed Code: