Back to Blog
Development Tips11 min read

IoT App Development 2026: Complete Guide for Mobile Integration

Complete guide to IoT mobile app development. Learn Bluetooth, WiFi, MQTT protocols, smart home integration, and building companion apps for connected devices.

Hevcode Team
January 18, 2026

IoT (Internet of Things) apps connect smartphones to smart devices. This guide covers protocols, implementation patterns, and best practices for building IoT companion apps.

IoT App Architecture

Architecture Overview:
┌─────────────────────────────────────────────────────────────┐
│                     Mobile App                               │
├─────────────────────────────────────────────────────────────┤
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐   │
│  │   UI     │  │ Business │  │ Device   │  │  Cloud   │   │
│  │  Layer   │  │  Logic   │  │ Manager  │  │  Sync    │   │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘   │
└─────────────────────────────────────────────────────────────┘
          │                          │                │
          ▼                          ▼                ▼
┌─────────────────┐    ┌─────────────────┐    ┌──────────────┐
│    Bluetooth    │    │      WiFi       │    │    Cloud     │
│   BLE / Classic │    │  Local Network  │    │   Platform   │
└─────────────────┘    └─────────────────┘    └──────────────┘
          │                    │                      │
          ▼                    ▼                      │
┌─────────────────────────────────────────┐           │
│            IoT Device                    │◄──────────┘
│  ┌──────────┐  ┌──────────┐  ┌────────┐ │
│  │ Sensors  │  │ Actuators│  │ MCU    │ │
│  └──────────┘  └──────────┘  └────────┘ │
└─────────────────────────────────────────┘

Communication Protocols

1. Bluetooth Low Energy (BLE)

Most common for wearables and small devices.

// iOS Core Bluetooth Implementation
import CoreBluetooth

class BLEManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
    private var centralManager: CBCentralManager!
    private var connectedPeripheral: CBPeripheral?

    // UUIDs for your device
    let serviceUUID = CBUUID(string: "FFE0")
    let characteristicUUID = CBUUID(string: "FFE1")

    override init() {
        super.init()
        centralManager = CBCentralManager(delegate: self, queue: nil)
    }

    // Central Manager State
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        switch central.state {
        case .poweredOn:
            startScanning()
        case .poweredOff:
            print("Bluetooth is off")
        case .unauthorized:
            print("Bluetooth not authorized")
        default:
            break
        }
    }

    // Start scanning for devices
    func startScanning() {
        centralManager.scanForPeripherals(
            withServices: [serviceUUID],
            options: [CBCentralManagerScanOptionAllowDuplicatesKey: false]
        )
    }

    // Device discovered
    func centralManager(_ central: CBCentralManager,
                       didDiscover peripheral: CBPeripheral,
                       advertisementData: [String: Any],
                       rssi RSSI: NSNumber) {
        print("Found device: \(peripheral.name ?? "Unknown")")
        connectedPeripheral = peripheral
        centralManager.connect(peripheral, options: nil)
    }

    // Connected to device
    func centralManager(_ central: CBCentralManager,
                       didConnect peripheral: CBPeripheral) {
        peripheral.delegate = self
        peripheral.discoverServices([serviceUUID])
    }

    // Services discovered
    func peripheral(_ peripheral: CBPeripheral,
                   didDiscoverServices error: Error?) {
        guard let services = peripheral.services else { return }

        for service in services {
            peripheral.discoverCharacteristics([characteristicUUID], for: service)
        }
    }

    // Characteristics discovered
    func peripheral(_ peripheral: CBPeripheral,
                   didDiscoverCharacteristicsFor service: CBService,
                   error: Error?) {
        guard let characteristics = service.characteristics else { return }

        for characteristic in characteristics {
            if characteristic.properties.contains(.notify) {
                peripheral.setNotifyValue(true, for: characteristic)
            }
        }
    }

    // Send data to device
    func sendData(_ data: Data) {
        guard let peripheral = connectedPeripheral,
              let service = peripheral.services?.first,
              let characteristic = service.characteristics?.first else { return }

        peripheral.writeValue(data, for: characteristic, type: .withResponse)
    }

    // Receive data from device
    func peripheral(_ peripheral: CBPeripheral,
                   didUpdateValueFor characteristic: CBCharacteristic,
                   error: Error?) {
        guard let data = characteristic.value else { return }
        // Process received data
        print("Received: \(data.hexEncodedString())")
    }
}
// Android BLE Implementation
import android.bluetooth.*
import android.bluetooth.le.*

class BLEManager(private val context: Context) {
    private val bluetoothAdapter: BluetoothAdapter? =
        (context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter

    private var bluetoothGatt: BluetoothGatt? = null

    companion object {
        val SERVICE_UUID: UUID = UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb")
        val CHARACTERISTIC_UUID: UUID = UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb")
    }

    // Scan for devices
    fun startScan() {
        val scanner = bluetoothAdapter?.bluetoothLeScanner
        val filters = listOf(
            ScanFilter.Builder()
                .setServiceUuid(ParcelUuid(SERVICE_UUID))
                .build()
        )
        val settings = ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
            .build()

        scanner?.startScan(filters, settings, scanCallback)
    }

    private val scanCallback = object : ScanCallback() {
        override fun onScanResult(callbackType: Int, result: ScanResult) {
            val device = result.device
            Log.d("BLE", "Found: ${device.name ?: "Unknown"}")
            connectToDevice(device)
        }
    }

    // Connect to device
    fun connectToDevice(device: BluetoothDevice) {
        bluetoothGatt = device.connectGatt(context, false, gattCallback)
    }

    private val gattCallback = object : BluetoothGattCallback() {
        override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                gatt.discoverServices()
            }
        }

        override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
            val service = gatt.getService(SERVICE_UUID)
            val characteristic = service?.getCharacteristic(CHARACTERISTIC_UUID)

            // Enable notifications
            characteristic?.let {
                gatt.setCharacteristicNotification(it, true)
                val descriptor = it.getDescriptor(
                    UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
                )
                descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
                gatt.writeDescriptor(descriptor)
            }
        }

        override fun onCharacteristicChanged(
            gatt: BluetoothGatt,
            characteristic: BluetoothGattCharacteristic
        ) {
            val data = characteristic.value
            // Process received data
            Log.d("BLE", "Received: ${data.toHexString()}")
        }
    }

    // Send data
    fun sendData(data: ByteArray) {
        val service = bluetoothGatt?.getService(SERVICE_UUID)
        val characteristic = service?.getCharacteristic(CHARACTERISTIC_UUID)
        characteristic?.value = data
        bluetoothGatt?.writeCharacteristic(characteristic)
    }
}

2. WiFi Direct Communication

For high-bandwidth local communication.

// React Native with local network discovery
import { NetworkInfo } from 'react-native-network-info';
import dgram from 'react-native-udp';

class LocalDeviceDiscovery {
  constructor() {
    this.socket = dgram.createSocket('udp4');
    this.discoveredDevices = [];
  }

  // mDNS-style discovery
  async discoverDevices() {
    const localIP = await NetworkInfo.getIPV4Address();
    const subnet = localIP.substring(0, localIP.lastIndexOf('.'));

    // Broadcast discovery message
    this.socket.bind(5353);

    this.socket.on('message', (msg, rinfo) => {
      const device = JSON.parse(msg.toString());
      this.discoveredDevices.push({
        ...device,
        ip: rinfo.address
      });
    });

    // Send discovery broadcast
    const discoveryMessage = Buffer.from(JSON.stringify({
      type: 'DISCOVER',
      appId: 'com.myapp.iot'
    }));

    this.socket.send(
      discoveryMessage,
      0,
      discoveryMessage.length,
      5353,
      `${subnet}.255`
    );
  }

  // Connect to discovered device
  async connectToDevice(deviceIP, port = 8080) {
    return new Promise((resolve, reject) => {
      const client = net.createConnection({ host: deviceIP, port }, () => {
        resolve(client);
      });

      client.on('error', reject);
    });
  }
}

3. MQTT Protocol

Standard for IoT cloud communication.

// MQTT Client Implementation
import mqtt from 'mqtt';

class MQTTClient {
  constructor(config) {
    this.config = config;
    this.client = null;
    this.subscriptions = new Map();
  }

  connect() {
    return new Promise((resolve, reject) => {
      this.client = mqtt.connect(this.config.brokerUrl, {
        clientId: `mobile_${Date.now()}`,
        username: this.config.username,
        password: this.config.password,
        clean: true,
        reconnectPeriod: 5000
      });

      this.client.on('connect', () => {
        console.log('MQTT Connected');
        resolve();
      });

      this.client.on('error', reject);

      this.client.on('message', (topic, message) => {
        const handler = this.subscriptions.get(topic);
        if (handler) {
          handler(JSON.parse(message.toString()));
        }
      });
    });
  }

  subscribe(topic, handler) {
    this.client.subscribe(topic, { qos: 1 });
    this.subscriptions.set(topic, handler);
  }

  publish(topic, data, options = {}) {
    this.client.publish(
      topic,
      JSON.stringify(data),
      { qos: options.qos || 1, retain: options.retain || false }
    );
  }

  disconnect() {
    this.client?.end();
  }
}

// Usage
const mqttClient = new MQTTClient({
  brokerUrl: 'mqtt://broker.example.com',
  username: 'user',
  password: 'password'
});

await mqttClient.connect();

// Subscribe to device data
mqttClient.subscribe('devices/thermostat/temperature', (data) => {
  console.log(`Temperature: ${data.value}°C`);
});

// Send command to device
mqttClient.publish('devices/thermostat/commands', {
  action: 'setTemperature',
  value: 22
});

4. HTTP REST API

For cloud-connected devices.

// Device Cloud API Client
class DeviceAPIClient {
  constructor(baseUrl, apiKey) {
    this.baseUrl = baseUrl;
    this.apiKey = apiKey;
  }

  async request(method, endpoint, data = null) {
    const response = await fetch(`${this.baseUrl}${endpoint}`, {
      method,
      headers: {
        'Authorization': `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json'
      },
      body: data ? JSON.stringify(data) : null
    });

    if (!response.ok) {
      throw new Error(`API Error: ${response.status}`);
    }

    return response.json();
  }

  // Get all devices
  async getDevices() {
    return this.request('GET', '/devices');
  }

  // Get device status
  async getDeviceStatus(deviceId) {
    return this.request('GET', `/devices/${deviceId}/status`);
  }

  // Send command
  async sendCommand(deviceId, command) {
    return this.request('POST', `/devices/${deviceId}/commands`, command);
  }

  // Get historical data
  async getDeviceHistory(deviceId, startTime, endTime) {
    const params = new URLSearchParams({
      start: startTime.toISOString(),
      end: endTime.toISOString()
    });
    return this.request('GET', `/devices/${deviceId}/history?${params}`);
  }
}

Smart Home Integration

HomeKit (iOS)

import HomeKit

class HomeKitManager: NSObject, HMHomeManagerDelegate {
    private let homeManager = HMHomeManager()
    private var home: HMHome?

    override init() {
        super.init()
        homeManager.delegate = self
    }

    func homeManagerDidUpdateHomes(_ manager: HMHomeManager) {
        home = manager.primaryHome
    }

    // Get all accessories
    func getAccessories() -> [HMAccessory] {
        return home?.accessories ?? []
    }

    // Control a light
    func setLightState(accessory: HMAccessory, on: Bool) {
        let lightService = accessory.services.first { $0.serviceType == HMServiceTypeLightbulb }
        let powerChar = lightService?.characteristics.first {
            $0.characteristicType == HMCharacteristicTypePowerState
        }

        powerChar?.writeValue(on) { error in
            if let error = error {
                print("Error: \(error)")
            }
        }
    }

    // Control thermostat
    func setThermostat(accessory: HMAccessory, temperature: Double) {
        let thermostatService = accessory.services.first {
            $0.serviceType == HMServiceTypeThermostat
        }
        let targetTemp = thermostatService?.characteristics.first {
            $0.characteristicType == HMCharacteristicTypeTargetTemperature
        }

        targetTemp?.writeValue(temperature) { error in
            if let error = error {
                print("Error: \(error)")
            }
        }
    }

    // Create automation
    func createAutomation(name: String, trigger: HMTrigger, action: HMAction) {
        home?.addTrigger(trigger) { trigger, error in
            if let error = error {
                print("Error creating trigger: \(error)")
                return
            }

            // Add action to trigger
            trigger?.addActionSet(HMActionSet()) { error in
                // Handle completion
            }
        }
    }
}

Google Home / Matter

// Matter-compatible device control
import { DeviceController } from '@matter/node';

class MatterDeviceManager {
  constructor() {
    this.controller = new DeviceController();
    this.devices = new Map();
  }

  async discoverDevices() {
    const discoveredDevices = await this.controller.discoverDevices();

    for (const device of discoveredDevices) {
      await this.pairDevice(device);
    }
  }

  async pairDevice(device) {
    const pairedDevice = await this.controller.pairDevice(device.id, {
      passcode: device.setupCode
    });

    this.devices.set(device.id, pairedDevice);
  }

  async controlLight(deviceId, state) {
    const device = this.devices.get(deviceId);
    if (!device) throw new Error('Device not found');

    await device.clusters.onOff.toggle(state);
  }

  async setLightLevel(deviceId, level) {
    const device = this.devices.get(deviceId);
    await device.clusters.levelControl.moveToLevel({
      level: Math.round(level * 254),
      transitionTime: 10
    });
  }

  async getLightState(deviceId) {
    const device = this.devices.get(deviceId);
    const onOffState = await device.clusters.onOff.getOnOffState();
    const level = await device.clusters.levelControl.getCurrentLevel();

    return {
      on: onOffState,
      brightness: level / 254
    };
  }
}

Device Provisioning (Setup Flow)

// Device Setup/Provisioning Flow
class DeviceProvisioner {
  constructor() {
    this.currentStep = 0;
    this.steps = [
      'detect',
      'connect',
      'configure_wifi',
      'register',
      'complete'
    ];
  }

  // Step 1: Detect device in setup mode
  async detectDevice() {
    // Device typically broadcasts on BLE or creates WiFi AP
    const devices = await BLEManager.scanForSetupDevices();
    return devices.filter(d => d.isInSetupMode);
  }

  // Step 2: Connect to device
  async connectToDevice(device) {
    if (device.setupType === 'ble') {
      await BLEManager.connect(device);
    } else if (device.setupType === 'wifi-ap') {
      // Device creates its own WiFi network
      await WiFiManager.connectToNetwork(device.apSSID, device.apPassword);
    }
  }

  // Step 3: Configure device WiFi
  async configureDeviceWiFi(ssid, password) {
    const config = {
      ssid,
      password,
      security: 'WPA2'
    };

    // Send WiFi credentials to device
    await this.sendToDevice({
      command: 'configure_wifi',
      data: config
    });

    // Wait for device to connect
    await this.waitForDeviceOnline();
  }

  // Step 4: Register device with cloud
  async registerDevice(deviceInfo) {
    const response = await fetch(`${API_URL}/devices/register`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${userToken}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        deviceId: deviceInfo.id,
        deviceType: deviceInfo.type,
        name: deviceInfo.name,
        location: deviceInfo.room
      })
    });

    return response.json();
  }

  // Step 5: Complete setup
  async completeSetup(device) {
    // Send completion command to device
    await this.sendToDevice({
      command: 'setup_complete',
      data: {
        cloudEndpoint: API_URL,
        authToken: device.cloudToken
      }
    });

    // Disconnect from setup mode
    await this.disconnect();
  }

  async sendToDevice(message) {
    // Implementation depends on connection type
    const data = JSON.stringify(message);
    await BLEManager.sendData(data);
  }

  async waitForDeviceOnline(timeout = 60000) {
    const startTime = Date.now();

    while (Date.now() - startTime < timeout) {
      try {
        const status = await this.checkDeviceStatus();
        if (status.online) return true;
      } catch (e) {
        // Device not ready yet
      }
      await sleep(2000);
    }

    throw new Error('Device setup timeout');
  }
}

Real-Time Data Visualization

// Real-time sensor data display
import React, { useState, useEffect } from 'react';
import { LineChart, Line, XAxis, YAxis, Tooltip } from 'recharts';

function SensorDashboard({ deviceId }) {
  const [data, setData] = useState([]);
  const [currentValue, setCurrentValue] = useState(null);

  useEffect(() => {
    // Subscribe to real-time updates
    const subscription = mqttClient.subscribe(
      `devices/${deviceId}/telemetry`,
      (message) => {
        const reading = {
          time: new Date().toLocaleTimeString(),
          value: message.temperature
        };

        setCurrentValue(message.temperature);
        setData(prev => [...prev.slice(-29), reading]);
      }
    );

    return () => subscription.unsubscribe();
  }, [deviceId]);

  return (
    <View style={styles.dashboard}>
      <Text style={styles.currentValue}>
        {currentValue?.toFixed(1)}°C
      </Text>

      <LineChart width={350} height={200} data={data}>
        <XAxis dataKey="time" />
        <YAxis domain={['auto', 'auto']} />
        <Tooltip />
        <Line
          type="monotone"
          dataKey="value"
          stroke="#007AFF"
          strokeWidth={2}
          dot={false}
        />
      </LineChart>

      <View style={styles.controls}>
        <Button
          title="Set Target: 20°C"
          onPress={() => sendCommand(deviceId, { target: 20 })}
        />
        <Button
          title="Set Target: 22°C"
          onPress={() => sendCommand(deviceId, { target: 22 })}
        />
      </View>
    </View>
  );
}

Security Best Practices

IoT Security Checklist:
├── Device Authentication
│   ├── Unique device certificates
│   ├── Secure key storage (TEE/HSM)
│   └── Token-based authentication
├── Communication Security
│   ├── TLS 1.3 for all connections
│   ├── Certificate pinning
│   └── End-to-end encryption for sensitive data
├── Firmware Security
│   ├── Signed firmware updates
│   ├── Secure boot chain
│   └── Rollback protection
├── App Security
│   ├── Secure credential storage
│   ├── No hardcoded secrets
│   └── Proper session management
└── Cloud Security
    ├── Per-device access control
    ├── Rate limiting
    └── Audit logging

Testing IoT Apps

Testing Strategy:
├── Unit Tests
│   ├── Protocol parsers
│   ├── Data transformations
│   └── Business logic
├── Integration Tests
│   ├── Device simulators
│   ├── Cloud API mocking
│   └── BLE mock services
├── End-to-End Tests
│   ├── Real device testing
│   ├── Network condition testing
│   └── Setup flow testing
└── Performance Tests
    ├── Connection latency
    ├── Data throughput
    └── Battery impact

Conclusion

IoT app development requires understanding:

  1. Protocols: BLE for nearby devices, MQTT for cloud, HTTP for APIs
  2. Provisioning: Smooth device setup is critical for UX
  3. Real-time: Handle live data updates efficiently
  4. Security: IoT devices are high-value targets
  5. Reliability: Handle disconnections gracefully

The IoT ecosystem is complex but offers tremendous opportunities.

Need help building an IoT companion app? Contact Hevcode for expert guidance on connected device applications.

Related Articles

Tags:IoTMobile DevelopmentBluetoothSmart HomeConnected Devices

Need help with your project?

We've helped 534+ clients build successful apps. Let's discuss yours.

Ready to Build Your App?

534+ projects delivered • 4.9★ rating • 6+ years experience

Let's discuss your project — no obligations, just a straightforward conversation.