Wearable IoT Device Mobile App Development

NOVASOLUTIONS.TECHNOLOGY is engaged in the development, support and maintenance of iOS, Android, PWA mobile applications. We have extensive experience and expertise in publishing mobile applications in popular markets like Google Play, App Store, Amazon, AppGallery and others.
Development and support of all types of mobile applications:
Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1 servicesAll 1735 services
Wearable IoT Device Mobile App Development
Complex
from 1 week to 3 months
FAQ
Our competencies:
Development stages
Latest works
  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1052
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    445

Developing Mobile Companion Apps for Wearable IoT Devices

Wearable devices — activity trackers, medical patches, industrial worker badges — live in constant conflict: small battery, lots of data needed, unstable connectivity. Developing a companion app for such device is primarily BLE work, power management, and accumulated data synchronization. General patterns for BLE peripheral communication are described in a separate section on fitness tracker data transfer; this section focuses on custom IoT wearable specifics.

BLE GATT Profile of Custom Device

Custom wearable is not Apple Watch or Fitbit. It has its own GATT service with proprietary UUIDs assigned by manufacturer. First task — get GATT specification from firmware team or reverse-engineer via Nordic nRF Connect.

Typical industrial wearable GATT profile:

Service UUID Characteristic Properties Description
0x1800 Device Name Read Standard GAP
0x180F Battery Level Read, Notify Standard BAS
{custom}-0001 Raw Sensor Data Notify IMU/sensor stream
{custom}-0002 Buffered Data Read, Indicate Accumulated records
{custom}-0003 Control Point Write Commands to device
{custom}-0004 Device Status Read, Notify Status, errors, uptime

Connect and subscribe to Notify characteristic on Android via coroutines:

class WearableRepository(private val context: Context) {
    private var gatt: BluetoothGatt? = null
    private val _sensorData = MutableSharedFlow<SensorFrame>(extraBufferCapacity = 64)
    val sensorData: SharedFlow<SensorFrame> = _sensorData.asSharedFlow()

    suspend fun connect(device: BluetoothDevice): Result<Unit> = withContext(Dispatchers.IO) {
        val connected = CompletableDeferred<Boolean>()

        gatt = device.connectGatt(context, false, object : BluetoothGattCallback() {
            override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    gatt.discoverServices()
                } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                    connected.complete(false)
                    scheduleReconnect(device)
                }
            }

            override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    enableSensorNotify(gatt)
                    connected.complete(true)
                }
            }

            override fun onCharacteristicChanged(
                gatt: BluetoothGatt,
                characteristic: BluetoothGattCharacteristic,
                value: ByteArray,
            ) {
                if (characteristic.uuid == SENSOR_DATA_UUID) {
                    val frame = SensorFrame.fromBytes(value)
                    _sensorData.tryEmit(frame)
                }
            }
        }, BluetoothDevice.TRANSPORT_LE)

        if (connected.await()) Result.success(Unit)
        else Result.failure(IOException("Connection failed"))
    }
}

Critical: BluetoothGattCallback runs on dedicated Android binder thread, not main thread. All gatt.writeCharacteristic() calls must be sequential — parallel GATT operations cause GATT_BUSY (133) and random disconnects.

Managing GATT Operation Queue

Most common crash source with BLE — parallel GATT requests. Android BLE stack doesn't support concurrent operations. Need a queue:

class GattOperationQueue {
    private val queue = Channel<GattOperation>(capacity = Channel.UNLIMITED)
    private val executor = CoroutineScope(Dispatchers.IO + SupervisorJob())

    init {
        executor.launch {
            for (operation in queue) {
                operation.execute()
                // Wait for callback before next operation
                operation.awaitCompletion()
            }
        }
    }

    suspend fun enqueue(operation: GattOperation) {
        queue.send(operation)
    }
}

Without this queue, multi-device project guarantees onCharacteristicWrite with status=133 on some devices.

Buffered Data Synchronization

Wearable writes to internal buffer (flash or SRAM) when phone unavailable. On connection, must read entire buffer — sometimes thousands of records, 20 bytes each.

Read protocol via Indicate characteristic:

suspend fun syncBufferedData(): List<SensorRecord> {
    val records = mutableListOf<SensorRecord>()
    var offset = 0

    do {
        // Request data chunk via Control Point command
        writeControlPoint(ReadBufferCommand(offset = offset, count = 50))

        // Wait for Indicate response
        val chunk = awaitIndicate(BUFFERED_DATA_UUID, timeout = 5.seconds)
        val parsed = SensorRecord.parseChunk(chunk)
        records.addAll(parsed)
        offset += parsed.size

        // Last chunk — end flag in header
    } while (!SensorRecord.isLastChunk(chunk))

    // Confirm sync — device can clear buffer
    writeControlPoint(AckSyncCommand(recordsReceived = records.size))
    return records
}

MTU negotiation before sync (requestMtu(512)) speeds transfer: instead of 20 bytes per notification get up to 509 bytes. On iOS MTU 185 bytes default, negotiate to 512 on Bluetooth 5.0+.

iOS: CoreBluetooth and Permissions

On iOS, all BLE via CoreBluetooth. Background mode requires bluetooth-central in Info.plist. Without it, Notify subscription breaks when app backgrounded — device data lost.

class WearableManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
    private var centralManager: CBCentralManager!
    private var peripheral: CBPeripheral?

    override init() {
        super.init()
        // CBCentralManagerOptionRestoreIdentifierKey — restore after app kill
        centralManager = CBCentralManager(delegate: self,
            queue: DispatchQueue(label: "ble.queue"),
            options: [CBCentralManagerOptionRestoreIdentifierKey: "WearableSession"])
    }

    func centralManager(_ central: CBCentralManager,
                        willRestoreState dict: [String: Any]) {
        // Restore connection after OS process restart
        if let peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey]
            as? [CBPeripheral], let p = peripherals.first {
            peripheral = p
            peripheral?.delegate = self
        }
    }
}

CBCentralManagerOptionRestoreIdentifierKey — without this, iOS process restart loses BLE session and device needs manual reconnection.

Firmware Update Over-the-Air (DFU)

For Nordic nRF chips — iOSDFULibrary (Swift) and Android-DFU-Library (Kotlin). For STM32WB — ST BLE Mesh DFU. Show progress with bytes and percentages, block other operations during DFU, handle interruption — device must resume from break point (DFU resume).

Developing mobile companion for custom wearable IoT device with BLE GATT, buffer sync and DFU: 8–14 weeks depending on GATT profile complexity and background work requirements. Cost calculated individually after studying device specification.