Разработка Native Module для React Native-приложения (iOS)

TRUETECH занимается разработкой, поддержкой и обслуживанием мобильных приложений iOS, Android, PWA. Имеем большой опыт и экспертизу для публикации мобильных приложений в популярные маркеты Google Play, App Store, Amazon, AppGallery и другие.

Разработка и поддержка любых видов мобильных приложений:

Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

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

Услуги, которые мы предлагаем
Показано 1 из 1Все 1735 услуг
Разработка Native Module для React Native-приложения (iOS)
Сложный
~3-5 дней
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    792
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    671
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1097
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    969
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    914
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    495

Разработка Native Module для React Native-приложения (iOS)

Мост между JavaScript и нативным Swift/Objective-C — один из самых коварных слоёв React Native. Пока приложение работает только с JS-библиотеками, всё относительно предсказуемо. Но как только появляется задача, которую нельзя закрыть стандартным пакетом — работа с Bluetooth Low Energy через CoreBluetooth, доступ к защищённому Keychain через SecItemCopyMatching, интеграция с нативным SDK банка или платёжной системы — приходится писать Native Module вручную.

И вот здесь начинается.

Архитектура моста: старая и новая

До React Native 0.71 мост работал через асинхронную очередь сообщений: JS-поток сериализовал вызов в JSON, отправлял через мост, нативный поток десериализовал и исполнял. Задержка была приемлемой для большинства задач, но при высокочастотных вызовах (например, обновление UI по данным с датчиков) она становилась заметной.

С версии 0.68 появилась New Architecture — JSI (JavaScript Interface) + Turbo Modules. JSI позволяет вызывать нативный код синхронно через C++ host object, минуя очередь сообщений. Это принципиально меняет подход к написанию модулей: вместо RCTBridgeModule нужно реализовывать TurboModule-протокол через кодогенерацию на основе Flow/TypeScript-спецификации.

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

Старая архитектура: RCTBridgeModule

Типичная структура — Swift-класс, унаследованный от NSObject с @objc атрибутами:

@objc(BiometricModule)
class BiometricModule: NSObject, RCTBridgeModule {
  static func moduleName() -> String { "BiometricModule" }

  @objc func authenticate(_ reason: String,
                           resolver: @escaping RCTPromiseResolveBlock,
                           rejecter: @escaping RCTPromiseRejectBlock) {
    let context = LAContext()
    var error: NSError?
    guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
      rejecter("BIOMETRIC_UNAVAILABLE", error?.localizedDescription, error)
      return
    }
    context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics,
                            localizedReason: reason) { success, authError in
      if success { resolver(true) }
      else { rejecter("AUTH_FAILED", authError?.localizedDescription, authError) }
    }
  }
}

Регистрация через RCT_EXTERN_MODULE в Objective-C bridging файле обязательна — без неё модуль не появится в реестре.

Самая частая ошибка на этом этапе: разработчик пишет Swift-класс, забывает добавить @objc(BiometricModule) или неправильно именует метод в RCT_EXTERN_METHOD, и на JS-стороне получает undefined is not a function. Отладить сложно, потому что ошибка появляется в рантайме без стектрейса.

Где реально тратится время

Потокобезопасность. React Native вызывает методы модуля на произвольном потоке из своего пула. Если внутри метода обращаешься к UIKit — краш с UIKit called from background thread. Классическое решение — DispatchQueue.main.async { } вокруг UI-кода. Но это создаёт новую проблему: resolve/reject вызываются асинхронно, и если пользователь успел закрыть экран, completion handler обращается к уже освобождённому объекту.

Паттерн с [weak self] и guard обязателен:

DispatchQueue.main.async { [weak self] in
  guard self != nil else { return }
  resolver(result)
}

Сериализация данных. Мост принимает только типы, которые умеет сериализовать JSON: NSString, NSNumber, NSArray, NSDictionary, NSNull. Хочешь передать Data (бинарные данные) — кодируй в Base64. Хочешь передать кастомный объект — разбирай его в словарь на нативной стороне. Это особенно больно при работе с CoreBluetooth, когда нужно отдавать CBCharacteristic со всеми его свойствами.

Callbacks vs Promises vs Events. Для одноразовых результатов — Promise. Для потока событий (данные с датчика, статус подключения) — RCTEventEmitter. Смешивать подходы в одном модуле — ошибка, которая приводит к утечкам памяти: если RCTResponseSenderBlock сохранить как property и вызвать дважды, приложение крашится с Tried to call a callback that is no longer valid.

New Architecture: Turbo Modules + Codegen

Начиная с RN 0.70+, Codegen генерирует C++ абстракцию по TypeScript-спецификации. Файл spec выглядит так:

import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';

export interface Spec extends TurboModule {
  authenticate(reason: string): Promise<boolean>;
}

export default TurboModuleRegistry.getEnforcing<Spec>('BiometricModule');

На нативной стороне реализуем протокол NativeBiometricModuleSpec, который Codegen сгенерировал автоматически. JSI позволяет вызывать методы синхронно без сериализации в JSON — скорость принципиально другая.

Проблема: если в проекте есть хотя бы один пакет без Turbo Module поддержки, New Architecture будет работать в режиме совместимости, частично теряя преимущества.

Подход к реализации

Аудит начинается с анализа текущей версии RN, наличия JSI-совместимых пакетов и целевого iOS-деплоймента. Если проект на 0.72+ и команда готова к New Architecture — сразу пишем Turbo Module с Codegen. Если нет — классический RCTBridgeModule с прицелом на будущую миграцию.

Покрытие юнит-тестами нативной части через XCTest обязательно. Интеграционные тесты — через Detox или Jest с моком модуля на JS-стороне.

Документируем публичный API в TypeScript-типах, чтобы команда не лезла в нативный код каждый раз.

Что входит в работу

  • Анализ требований и выбор архитектурного подхода (Old Bridge / Turbo Module)
  • Написание нативного кода на Swift с Objective-C bridging
  • TypeScript-типизация публичного API модуля
  • Обработка ошибок, потокобезопасность
  • Юнит-тесты нативной части (XCTest)
  • Интеграция с JS-слоем, проверка в симуляторе и на реальном устройстве
  • Документация по использованию модуля

Сроки

От 3 до 5 дней в зависимости от сложности нативного API, который нужно обернуть. Простая обёртка над одним системным фреймворком — ближе к 3 дням. Модуль с потоком событий, бинарными данными и поддержкой New Architecture — 5 дней и больше. Стоимость рассчитывается индивидуально после анализа требований и кодовой базы.