Розробка нативного модуля для React Native на iOS: покроковий гайд

Розробка нативного модуля для React Native (iOS) Ми вирішуємо задачу, яку не закрити готовим пакетом: Bluetooth Low Energy через CoreBluetooth, захищений Keychain через SecItemCopyMatching, інтеграція SDK банку або платіжної системи. Поки додаток працює тільки з JS-бібліотеками, все передбачувано

Розробка та підтримка будь-яких видів мобільних додатків:

Інформаційні та розважальні мобільні програми
Новинки, ігри, довідники, онлайн-каталоги, погодні, фітнес та здоров'я, туристичні, освітні, соціальні мережі та месенджери, квіз, блоги та подкасти, форуми, агрегатори
Мобільні програми електронної комерції
Інтернет-магазини, B2B-додатки, маркетплейси, онлайн-обмінники, кешбек-сервіси, біржі, дропшиппінг-платформи, програми лояльності, доставка їжі та товарів, платіжні системи
Мобільні програми для управління бізнес-процесами
CRM-системи, ERP-системи, управління проектами, інструменти для команди продажів, облік фінансів, управління виробництвом, логістика та доставка, управління персоналом, системи моніторингу даних
Мобільні програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, платформи надання електронних послуг, платформи кешбеку, відеохостинги, тематичні портали, платформи онлайн-бронювання та запису, платформи онлайн-торгівлі

Це лише деякі з типів мобільних додатків, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 1734 послуг
Розробка нативного модуля для React Native на iOS: покроковий гайд
Складний
~3-5 днів

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

Часті запитання

Останні роботи

  • image_mobile-applications_feedme_467_0.webp
    Розробка мобільного додатка для компанії FEEDME
    898
  • image_mobile-applications_xoomer_471_0.webp
    Розробка мобільного додатку для компанії XOOMER
    784
  • image_mobile-applications_rhl_428_0.webp
    Розробка мобільного додатку для компанії RHL
    1219
  • image_mobile-applications_zippy_411_0.webp
    Розробка мобільного додатку для компанії ZIPPY
    1081
  • image_mobile-applications_affhome_429_0.webp
    Розробка мобільного додатку для компанії Affhome
    1004
  • image_mobile-applications_flavors_409_0.webp
    Розробка мобільного додатку для компанії FLAVORS
    600

Розробка нативного модуля для React Native (iOS)

Ми вирішуємо задачу, яку не закрити готовим пакетом: Bluetooth Low Energy через CoreBluetooth, захищений Keychain через SecItemCopyMatching, інтеграція SDK банку або платіжної системи. Поки додаток працює тільки з JS-бібліотеками, все передбачувано. Але коли з'являється нестандартна потреба — доводиться писати нативний модуль вручну. І тут починається справжня інженерна робота, де наш 10-річний досвід у мобільній розробці дає гарантію стабільності та продуктивності. Зв'яжіться з нами для консультації — обговоримо ваш проект.

Типи даних через міст

Міст React Native приймає тільки типи, серіалізовані в JSON: NSString, NSNumber, NSArray, NSDictionary, NSNull. Бінарні дані (Data) кодуйте в Base64, кастомні об'єкти розбирайте в словник на нативній стороні. Це особливо критично при роботі з CoreBluetooth, де треба передавати CBCharacteristic з усіма властивостями. Неправильна серіалізація призводить до помилок часу виконання, які важко налагодити.

Проблеми, які вирішує нативний модуль

Bluetooth Low Energy. Стандартні пакети (react-native-ble-plx) не завжди підтримують кастомні протоколи або специфічні характеристики. Наш модуль обгортає CoreBluetooth з повним контролем над CBPeripheral, CBCentralManager та керуванням потоком даних.

Keychain і безпека. Зберігання токенів, ключів шифрування, біометричних даних потребує прямого доступу до SecItemCopyMatching. Помилки в імплементації призводять до витоків або крашів — ми використовуємо перевірений шаблон з потокобезпекою та коректною обробкою помилок.

Інтеграція SDK. Багато банків та платіжні системи надають тільки нативні бібліотеки (CocoaPods з Objective-C/Swift). Обгортка в нативний модуль — єдиний шлях до їх використання в React Native.

Як влаштований міст React Native?

До появи New Architecture міст працював через асинхронну чергу повідомлень: JS-потік серіалізував виклик в JSON, відправляв через міст, нативний потік десеріалізував і виконував. Затримка 5–10 мс була прийнятною для більшості завдань, але при високочастотних викликах (наприклад, оновлення UI за даними з датчиків) вона ставала помітною.

З появою New Architecture — JSI (JavaScript Interface) + Turbo Modules — з'явилася можливість викликати нативний код синхронно через C++ host object, минаючи чергу повідомлень. Це принципово змінює підхід: замість RCTBridgeModule потрібно реалізувати TurboModule-протокол через кодогенерацію на основі TypeScript-специфікації.

На практиці 80% проектів досі використовують стару архітектуру, тому що оновлення ламає залежності. Тому ми підтримуємо обидва підходи і допомагаємо мігрувати поступово. Детальніше про Turbo Modules можна почитати в офіційному репозиторії.

Аспект Старий Bridge (RCTBridgeModule) Новий Turbo Module (JSI)
Механізм виклику Асинхронна черга повідомлень Синхронний виклик через C++
Серіалізація JSON (NSString, NSNumber, …) Пряма передача типів (без JSON)
Затримка 5–10 мс <1 мс
Сумісність Будь-яка версія RN RN з підтримкою Codegen
Продуктивність Середня Висока (в 3–5 разів швидше)

Стара архітектура: RCTBridgeModule

Типова структура — Swift-клас, успадкований від NSObject з @objc атрибутами. Реєстрація через RCT_EXTERN_MODULE в Objective-C bridging файлі обов'язкова — без неї модуль не з'явиться в реєстрі.

Приклад реалізації
@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) } } } } 

Найчастіша помилка на цьому етапі: розробник пише Swift-клас, забуває додати @objc(BiometricModule) або неправильно іменує метод в RCT_EXTERN_METHOD, і на JS-стороні отримує undefined is not a function. Налагодити складно, тому що помилка з'являється в рантаймі без стектрейсу. Наш сертифікований інженер з досвідом понад 100 проектів виключає такі помилки на етапі код-рев'ю.

Як забезпечити потокобезпеку?

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

Починаючи з версії React Native, що підтримує New Architecture, 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 — швидкість принципово інша. Порівняно з RCTBridgeModule, Turbo Module забезпечує продуктивність у 3-5 разів вище.

Проблема: якщо в проекті є хоча б один пакет без підтримки Turbo Module, New Architecture буде працювати в режимі сумісності, частково втрачаючи переваги. Наша команда допомагає провести аудит залежностей і спланувати міграцію без простою.

Як розробити нативний модуль: покроковий план

  1. Визначити вимоги до нативного API та обрати архітектуру (Old Bridge або Turbo Module).
  2. Створити Swift-клас з наслідуванням від NSObject і додати @objc атрибути.
  3. Зареєструвати модуль в Objective-C bridging файлі через RCT_EXTERN_MODULE.
  4. Реалізувати методи з Promise або Events, забезпечивши потокобезпеку.
  5. Написати TypeScript-типи для публічного API.
  6. Покрити нативний код юніт-тестами (XCTest).
  7. Протестувати інтеграцію з JS-шаром на симуляторі та реальному пристрої.

Підхід до реалізації

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

Покриття юніт-тестами нативної частини через XCTest обов'язкове. Інтеграційні тести — через Detox або Jest з моком модуля на JS-стороні. Це знижує кількість багів у релізі на 40%.

Документуємо публічний API в TypeScript-типах, щоб команда не лізла в нативний код щоразу. Результат — прискорення онбордингу нових розробників у 2 рази.

Типова помилка Наслідок Рішення
Відсутність @objc на класі Модуль не реєструється Додати @objc(ModuleName)
Виклик UIKit без main.async Краш додатку DispatchQueue.main.async
Подвійний виклик callback Краш додатку Використовувати Promise або guard
Передача кастомного об'єкта Помилка серіалізації Розібрати в NSDictionary

Що входить в роботу

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

Строки та вартість

Від 3 до 5 днів залежно від складності нативного API, який потрібно обгорнути. Проста обгортка над одним системним фреймворком — ближче до 3 днів (від $300). Модуль із потоком подій, бінарними даними та підтримкою New Architecture — 5 днів і більше (від $500). Вартість розраховується індивідуально після аналізу вимог та кодової бази, але економія на адмініструванні сягає до 30%.

Отримайте консультацію — зв'яжіться з нами для попередньої оцінки вашого проекту. Замовте розробку нативного модуля у нас — гарантуємо стабільність та продуктивність.