Hi everyone,

After years in iOS development, I decided to launch this newsletter to share hands-on insights, practical code, and real-world lessons from building within Apple’s ecosystem.

Each edition will cover:

📱 Practical Swift examples — From UIKit to SwiftUI, CoreBluetooth, ARKit, Vision, and beyond

💻 Copy-paste-ready code snippets — Real implementations you can use in your projects

🏭 Industry applications — How iOS frameworks power everything from IoT to media apps

📚 Personal lessons — What I've learned building apps and experimenting with Apple technologies

🎤 Interviews — Conversations with senior Swift developers sharing their expertise

First Topic: Bluetooth Low Energy (BLE) in Swift

For our kickoff, let's dive into Bluetooth Low Energy. It's everywhere—headphones, wearables, health devices, smart homes—yet many developers never explore it beyond the basics.

Here's what you might not realize: With just a few lines of Swift code, you can:

✓ Detect nearby devices automatically ✓ Connect and exchange data securely ✓ Build proximity-aware experiences ✓ Control IoT hardware directly from your app

Ever wondered how:

  • A museum app knows you're standing in front of Van Gogh and offers audio commentary?
  • A retail app pushes a coupon when you're near a specific product?
  • Your phone can navigate you through a hospital or airport without GPS?
  • Smart home apps control lights, locks, and sensors?

The answer is BLE + CoreBluetooth. And I'm going to show you exactly how to build these experiences.

Bluetooth Low Energy (BLE) & Swift — The Complete Guide

Real-world examples: museums, shops, indoor navigation, hardware control

What BLE Gives You (in Plain Language)

Low-power communication — Perfect for small data exchanges, not music streaming. Ideal for beacons, sensors, locks, buttons, or control commands

Two roles — Your phone is usually the central (scans & connects). Devices like beacons, sensors, or microcontrollers are peripherals (advertise and expose data)

GATT model — Think of Services as folders and Characteristics as files. You can read, write, or subscribe to these values

Advertising — Lightweight broadcast packets that let you detect proximity without a full connection. Perfect for quick triggers

What You'll Learn to Build

By the end of this guide, you'll understand how to:

📡 Scan & Connect — A BluetoothManager singleton to centralize BLE operations

🔄 Complete Flow — Scan → discover → connect → discover services → read/write/subscribe

📢 Advertise — Create a peripheral that broadcasts data (like a promo beacon)

📍 Proximity Detection — iBeacon ranging for museum-style experiences

🗺️ Indoor Positioning — RSSI smoothing and fingerprinting basics for indoor mapping

🔧 Hardware Control — Control lights, locks, and sensors via GATT characteristics

🎵 Audio Metadata — When to use BLE for control vs. actual audio streaming

Setting Up the project:

Before writing code, configure your project properly:

1. Info.plist Privacy Keys:

Plain text
//xml
<key>NSBluetoothAlwaysUsageDescription</key>
<string>We use Bluetooth to detect nearby beacons and provide location-based content in the museum</string>

2. Background Modes (Optional - Use Sparingly)

Only if you need background BLE behavior:

Plain text
//xml
<key>UIBackgroundModes</key>
<array>
    <string>bluetooth-central</string>
    <!-- Add bluetooth-peripheral if acting as a peripheral -->
</array>

3. Capabilities

Enable Background Modes in your Xcode project target if needed.

4. Privacy Best Practices

  • Make your usage strings specific and honest
  • Explain what proximity/location data you collect
  • State how long you'll retain the data
  • Provide opt-out mechanisms where appropriate

The Central Pattern: A Single Bluetooth Manager

Why This Matters

CoreBluetooth works best with a single CBCentralManager instance. Multiple managers cause delegate conflicts and weird state issues.

Best practices:

  • Create a shared manager (singleton or dependency-injected service).
  • Let your UI components subscribe to updates instead of managing BLE directly.
  • Stop scanning after finding devices or after a timeout.
  • To avoid repeated callbacks use:
Plain text
BCentralManagerScanOptionAllowDuplicatesKey: false

Why BLE Matters Today:

Bluetooth Low Energy (BLE) is everywhere because it enables short-range, low-power wireless communication between phones and devices. Use cases include wearables, beacons, sensors, smart locks, audio devices, retail beacons, proximity-driven content, and local control of hardware.

Unlike classic Bluetooth (for streaming audio), BLE is optimized for small bursts of data and long battery life — perfect for interactions where latency and energy efficiency matter.

High-Level Concepts (Non-Technical)

Peripheral — A device that advertises and offers data (e.g., beacon, heart-rate monitor, smart bulb)

Central — Usually a phone or tablet that scans for and connects to peripherals

GATT (Generic Attribute Profile) — How BLE structures data: Services group Characteristics. A characteristic is a value you can read, write, or subscribe to

Advertising — A peripheral broadcasts small packets (IDs, short data). Centrals can listen and react without connecting

RSSI (Received Signal Strength Indicator) — Signal strength (indicator of proximity; noisy and not an exact distance)

iBeacon / Eddystone — Higher-level beacon ecosystems built on BLE advertising (iBeacon is Apple's format — widely used in museums/retail for proximity triggers)

Apple Platform Basics: CoreBluetooth

On iOS, BLE is implemented using CoreBluetooth. Key classes:

  • CBCentralManager — Manage scanning/connecting as a central
  • CBPeripheral — Represents a discovered remote peripheral
  • CBPeripheralManager — Allows your app to act as a peripheral (advertise services/characteristics)
  • CBService / CBCharacteristic — GATT entities

Important Platform Rules:

  • Use NSBluetoothAlwaysUsageDescription in Info.plist (descriptions must be user-facing and clear)
  • Starting iOS 13, location permissions may be required if you use BLE for location-like features (beacons/indoor navigation). Explain in your privacy text how you use data
  • Background modes: For prolonged BLE background tasks, you must declare appropriate background modes (e.g., bluetooth-central, bluetooth-peripheral) and respect Apple's energy & privacy guidelines

Minimal but Practical Swift Examples

Note: Keep a single CBCentralManager instance in your app and reuse it to avoid state conflicts and delegate issues. Delegate patterns are important. Below are compact examples — adapt them into your app architecture (MVC, MVVM, Coordinator).

1) Central: Scan & Simple Discovery

Swift
//swift//
import CoreBluetooth
import UIKit

class BLECentralController: NSObject {
    private var centralManager: CBCentralManager!
    private(set) var discoveredPeripherals: [CBPeripheral] = []

    override init() {
        super.init()
        centralManager = CBCentralManager(
            delegate: self,
            queue: DispatchQueue(label: "ble.central.queue")
        )
    }

    func startScan() {
        guard centralManager.state == .poweredOn else { return }
        // nil => all; you can filter with specific service UUIDs for efficiency
        centralManager.scanForPeripherals(
            withServices: nil,
            options: [CBCentralManagerScanOptionAllowDuplicatesKey: false]
        )
        print("Scanning started")
    }

    func stopScan() {
        centralManager.stopScan()
        print("Scanning stopped")
    }
}

extension BLECentralController: CBCentralManagerDelegate {
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        switch central.state {
        case .poweredOn:
            print("Bluetooth ON")
        default:
            print("Bluetooth unavailable: \(central.state.rawValue)")
        }
    }

    func centralManager(_ central: CBCentralManager,
                        didDiscover peripheral: CBPeripheral,
                        advertisementData: [String : Any],
                        rssi RSSI: NSNumber) {
        if !discoveredPeripherals.contains(peripheral) {
            discoveredPeripherals.append(peripheral)
        }
        print("Discovered: \(peripheral.name ?? "Unknown") rssi:\(RSSI)")
    }
}

2) Connect, Discover Services & Read a Characteristic

Swift
//swift//

extension BLECentralController: CBPeripheralDelegate {
    func connect(to peripheral: CBPeripheral) {
        peripheral.delegate = self
        centralManager.connect(peripheral, options: nil)
    }

    func centralManager(_ central: CBCentralManager,
                       didConnect peripheral: CBPeripheral) {
        print("Connected to \(peripheral.name ?? "device")")
        // Pass nil or specific service UUIDs
        peripheral.discoverServices(nil)
    }

    func peripheral(_ peripheral: CBPeripheral,
                   didDiscoverServices error: Error?) {
        guard error == nil else { return }
        for service in peripheral.services ?? [] {
            peripheral.discoverCharacteristics(nil, for: service)
        }
    }

    func peripheral(_ peripheral: CBPeripheral,
                   didDiscoverCharacteristicsFor service: CBService,
                   error: Error?) {
        for char in service.characteristics ?? [] {
            // Example: read if readable
            if char.properties.contains(.read) {
                peripheral.readValue(for: char)
            }
            // Subscribe to notifications if needed
            if char.properties.contains(.notify) {
                peripheral.setNotifyValue(true, for: char)
            }
        }
    }

    func peripheral(_ peripheral: CBPeripheral,
                   didUpdateValueFor characteristic: CBCharacteristic,
                   error: Error?) {
        guard let data = characteristic.value else { return }
        // Parse data according to the device spec
        print("Characteristic \(characteristic.uuid) value: \(data as NSData)")
    }
}

3) Peripheral (Advertising) — E.g., for In-Store Promotions or Beacons

Swift
//swift//
import CoreBluetooth

class BLEPeripheralController: NSObject, CBPeripheralManagerDelegate {
    private var peripheralManager: CBPeripheralManager!
    private var transferCharacteristic: CBMutableCharacteristic!

    override init() {
        super.init()
        peripheralManager = CBPeripheralManager(delegate: self, queue: nil)
    }

    func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
        if peripheral.state == .poweredOn {
            setupServiceAndAdvertise()
        }
    }

    private func setupServiceAndAdvertise() {
        let serviceUUID = CBUUID(string: "1234")
        let characteristicUUID = CBUUID(string: "ABCD")

        transferCharacteristic = CBMutableCharacteristic(
            type: characteristicUUID,
            properties: [.read, .write],
            value: nil,
            permissions: [.readable, .writeable]
        )

        let service = CBMutableService(type: serviceUUID, primary: true)
        service.characteristics = [transferCharacteristic]
        peripheralManager.add(service)

        // Advertise with a local name and service UUIDs
        peripheralManager.startAdvertising([
            CBAdvertisementDataLocalNameKey: "PromoBeacon",
            CBAdvertisementDataServiceUUIDsKey: [serviceUUID]
        ])
    }
}

Practical Architecture & Patterns:

Single BLE Manager — Create a singleton or shared service to manage scanning/connections so your app components don't create conflicting managers

Reactive Layer — Wrap CoreBluetooth with Combine / RxSwift / async/await-friendly adapters to avoid delegate hell

State Machine — Track peripheral states (discovered → connecting → connected → discovering → ready)

Timeouts & Retries — BLE is error-prone; implement connection timeouts and exponential backoff

Abstract Device Profiles — Map remote GATT specs to strongly-typed Swift models (e.g., struct HeartRateSensor)

Testing Harness — Use hardware emulators (e.g., Nordic dev boards, LightBlue, physical beacons) and unit test what you can (parsing, state transitions)

Use-Case Examples — Concrete, Practical, and Design-Minded

1) Museums — Context-Aware Exhibits & Accessibility

What you want: Provide exhibit content (audio, text, AR overlays, translations) when a visitor approaches an artwork. Offer personalized tours and accessibility cues.

How BLE is used:

  • Deploy BLE beacons (iBeacon or Eddystone) near exhibits. These broadcast small packets containing IDs
  • Visitor app scans for beacons and maps beacon ID → exhibit metadata
  • Fetch or preload high-res content on first proximity detection
  • Use RSSI to manage content trigger thresholds (e.g., only show if RSSI > -65 dBm and stable for 2 seconds)
  • Use peripheral advertising from interactive exhibits to accept simple user actions (e.g., "play demo", "request a docent call")
  • Use analytics (anonymized) to understand visitor paths (which exhibits get more engagement)

Example flow (museum app):

  1. App detects beacon for "Impressionist Wing – Painting #42"
  2. Show title + summary + "Listen" button; if user taps, stream a short audio file
  3. If user subscribes to notifications, push next-guide tips as they move

Considerations:

  • Battery life of beacons: configure advertising intervals (balancing battery vs responsiveness)
  • Accessibility: provide audio transcripts & large-text options
  • Privacy: be explicit about collection; avoid per-user tracking unless consented

2) Shops / Promotions — Location-Triggered Offers

What you want: Send targeted promotions when customers browse specific departments or products.

How BLE is used:

  • Place beacons at aisles or product displays; app listens for proximity and triggers a promotion card or coupon
  • Alternatively, use in-store iPads or kiosks that act as BLE peripherals (advertising discount tokens) — the app reads tokens and redeems at checkout
  • Use "geofencing + BLE" to combine coarse location (GPS) with precise BLE proximity

Example features:

  • "Welcome offer" when the customer enters the store (advertising + push)
  • Product-specific micro-campaigns: when a user is near running shoes, show 15% off coupon
  • Queue management: devices at checkout advertise a request for assistance to staff apps

Considerations & anti-abuse:

  • Frequency capping: prevent spamming by storing last-triggered timestamp per beacon
  • Security: coupons should be server-validated tokens to avoid easy forging
  • GDPR: opt-in for promotional push notifications; allow easy unsubscribe

3) Indoor Navigation (Indoor Positioning)

What you want: Help visitors/employees navigate large indoor spaces (museums, airports, hospitals, warehouses).

How BLE is used:

A grid of BLE beacons provides signal samples. Approaches:

  • Proximity-based: Show nearest beacon's associated location. Simple, coarse accuracy (room-level)
  • Fingerprinting: Map RSSI vectors to known locations (requires site survey and ML model)
  • Trilateration: Estimate distance from RSSI to multiple beacons and compute position (RSSI → distance model; noisy)
  • Sensor fusion: Combine BLE with IMU (dead reckoning), Wi-Fi and map constraints for robust results

Example architecture:

  • Mobile app collects RSSI from N nearest beacons and sends to a local or cloud service for position estimation
  • Display turn-by-turn within the building overlaid on a floor plan

Practical tips:

  • RSSI fluctuation: smooth with moving average / Kalman filter
  • Calibration: collect fingerprints during off-hours and update regularly
  • Floor detection: use barometric pressure + beacon groups per floor

4) Hardware Control (Lights, Locks, Sensors)

What you want: Mobile UI to control devices (e.g., smart lights, doors, kiosks). This typically requires a writeable characteristic on the peripheral device.

How BLE is used:

  • Peripheral exposes a service with characteristics for commands and state
  • Example service UUID: 0xFFF0, characteristic 0xFFF1 for commands (write without response or write with response), 0xFFF2 for state notifications
  • App connects securely, writes commands, and subscribes to notifications for status updates

Command flow example:

  1. Connect to peripheral
  2. Discover service/characteristic
  3. Write 0x01 for ON, 0x00 for OFF (or use JSON for richer payloads)
  4. Subscribe to state characteristic to receive confirmation

Security:

  • Use BLE Secure Connections (LE Secure Connections) or application-layer authentication (HMAC tokens) if the device or app needs to be secure
  • Avoid transmitting sensitive credentials in plain text
  • Consider pairing/bonding to keep trusted devices recognized

Performance, Reliability & Battery Best Practices

  • Filter during scan: Scan with specific service UUIDs rather than nil to reduce CPU and battery usage
  • Use scanForPeripherals options: CBCentralManagerScanOptionAllowDuplicatesKey: false avoids repeated delegate calls
  • Avoid long background scans: iOS limits background BLE scanning; design for short scans and server-assisted workflows if you need continuous location
  • Batch updates: Schedule periodic scans and batch processing of results to minimize context switching
  • RSSI smoothing: Average multiple RSSI samples across a short window to reduce false triggers
  • Peripheral advertising intervals: For beacons, use longer intervals (e.g., 500ms–1000ms) to increase battery life; for interactive experiences, shorter intervals may be acceptable

Security, Privacy & App Store Rules

  • Explicit permission strings in Info.plist with clear purpose for Bluetooth usage
  • Data minimization: only collect what's necessary and explain retention
  • Consent for marketing communications (shops)
  • Local authentication: for critical operations (unlock doors), require Face ID / passcode before sending commands
  • Encryption & pairing: for sensitive control, require bonded connections or provide authenticated application-layer protocols
  • App Store review: avoid background behaviors not permitted by iOS guidelines (e.g., continuous background scanning without good reason)

Debugging & Tooling

  • nRF Connect (by Nordic Semiconductor) — Inspect advertising, services, characteristics, and perform reads/writes
  • LightBlue — Handy for quick manual tests on iOS/Android
  • Bluetooth Explorer (part of Additional Tools for Xcode) — Deep platform-level debugging
  • Hardware dev kits — Nordic nRF52 series, Particle, or Arduino BLE boards for prototyping
  • Logs & analytics — Instrument your app to log BLE events (scan start/stop, connect/disconnect) to diagnose flaky devices

Common Gotchas & How to Handle Them

  • Missing Info.plist keys → App won't be allowed to access BLE
  • Multiple CBCentralManager instances → Unpredictable behavior: centralize access
  • RSSI variability → Never use raw RSSI as a single truth; smooth and threshold
  • Background limits → iOS restricts long-running BLE tasks; use server/edge architectures where necessary
  • Bonding vs. pairing confusion → Pairing is session-based; bonding stores long-term keys. If a device requires bonding, implement it carefully for predictable UX
  • Vendor-specific GATT → Read the device vendor documentation for characteristic formats

Example Project Ideas & Feature Mapping

1. Museum Companion App

  • Beacons per exhibit (proximity triggers)
  • Offline caching of audio/text
  • Accessibility modes (audio transcription)
  • Analytics dashboard (heatmaps of visitor movement)

2. Retail Promo Engine

  • Beacons + backend rules engine to deliver coupons
  • Coupon validation via server-issued tokens
  • Loyalty integration (link to user profile with consent)

3. Indoor Navigation

  • Site survey & fingerprint database
  • On-device ML model for localization
  • Multi-floor support with fluid UI

4. Device Controller

  • Pairing-based secure control of hardware
  • OTA firmware updates over BLE (DFU patterns like Nordic DFU)
  • Event-based logging & alerts (battery low, state changed)

Suggested APIs & Server Architecture for Production

  • Edge handshake: The mobile app reads ephemeral advertisement → requests signed token from backend → uses token to enable promotion or advanced operation. Prevents fake beacons from granting privileges
  • Sync & caching: Keep a local cache of beacon → content mappings to minimize latency and network dependency
  • Device registry: For hardware fleets, maintain a registry with firmware version, GATT schema, and security keys
  • Analytics pipelines: Event stream ingestion (e.g., when a beacon triggers) → anonymized sessionization → heatmaps/time-series

Example Production Checklist (Before Launch)

  1. Validate beacon placement and coverage
  2. Run site survey for RSSI maps
  3. Test with many smartphone models & OS versions
  4. Implement rate limiting & frequency capping for promotions
  5. Validate App Store privacy strings & permissions
  6. Build a monitoring dashboard for device health (beacons battery levels, connectivity rates)
  7. Set fallback UX: if BLE unavailable, provide QR-code-based content or simple map

Further Reading & Tools

  • Apple Developer: CoreBluetooth documentation (developer.apple.com)
  • @Nordic Semiconductor: BLE fundamentals & DFU examples
  • Tools: nRF Connect, LightBlue, Bluetooth Explorer (Apple Additional Tools)
  • Research on indoor localization: Fingerprinting & sensor fusion literature

TL;DR — Key Takeaways

  • BLE is perfect for low-energy, local interactions: beacons, sensors, hardware control
  • On iOS use CoreBluetooth (CBCentralManager, CBPeripheral, CBPeripheralManager)For museums/shops: beacons trigger content & promotions; respect privacy & frequency caps
  • For indoor navigation: combine fingerprinting, trilateration and sensor fusion for best results
  • For hardware control: design secure GATT profiles and use pairing/bonding or app-level authentication
  • Test early and often on real hardware; plan for unreliable radio environments

What's Next

This article is the first in a short series where I'll deep-dive into one BLE topic per edition. Next time I'll share a production-ready Swift module that wraps CoreBluetooth into an async/await-friendly API, plus a sample app that implements museum-style proximity-triggered audio tours.

If you found this useful:

  • Comment below with questions or your own BLE experiences
  • Share with someone building location-aware or IoT apps

If you’re interested in iOS development (whether you’re a beginner, indie dev, or just curious about Apple’s frameworks), hit Subscribe to follow along.

Let’s build cool things together!

☕ Enjoying Swift in Practice? It's free and reader-supported. If an edition saved you some debugging time, you can buy me a coffee: https://buymeacoffee.com/swiftinpractice

Originally published as a Swift in Practice edition on LinkedIn. Comments and discussion happen there.