Automated Minew iBeacon Configuration via OCR and QR Scanning
π§© Simplify and automate multiple beacons to accelerate your workflow.
An iOS app that automates beacon writing using Vision and CoreBluetooth
- Project Duration: 2025.08 - 2025.10 (v1.0.0)
- Project Tech Stack: SwiftUI 5.0 + TCA(The Composable Architecture) 1.22.3
- Hardware & Perception APIs: CoreBluetooth, Vision, AVFoundation
Time Comparison Simulation
- Existing Problem: Setting up 100β200 beacons with the current manufacturer app is time-consuming, labor-intensive.
- Approach: Created an automation app using OCR/QR vision technologies and a customized MinewBeacon SDK.
- Business Impact: Achieved a time reduction from 1m 30s to 15s (β83% faster) β
Hardware-Integrated TCA Architecture
- Existing Problem: Managing multiple hardware states (Bluetooth, Camera, Vision) simultaneously leads to race conditions and inconsistent UI updates.
- Approach: Implemented TCA's unidirectional data flow with centralized state management for all hardware interactions.
- Technical Impact: Eliminated state synchronization issues and achieved 100% predictable hardware state transitions β
Obj-C Delegate β AsyncStream β TCA Action Bridging
- Existing Problem: MinewSDK's Obj-C delegate callbacks could not be directly received within TCA's Swift Concurrency-based unidirectional data flow.
- Approach: Bridged hardware events to TCA Actions via a 3-step conversion (Delegate β Closure β AsyncStream), monitoring 3 streams concurrently with
async let. - Technical Impact: SDK callbacks operate as first-class citizens within TCA's unidirectional flow, managing beacon discovery, detection, and connection in a single Reducer β
Step 1. Delegate β Closure (BeaconManager)
BeaconManager.swift
var onBeaconNotFound: (() -> Void)?
var onBeaconFound: ((MinewBeacon) -> Void)?
var onBeaconConnect: ((ConnectionState) -> Void)?
// MinewBeaconManagerDelegate
func minewBeaconManager(_ manager: MinewBeaconManager!, didRangeBeacons beacons: [MinewBeacon]!) {
let foundBeacon = beacons.first { $0.mac == identifier }
foundBeacon.map { onBeaconFound?($0) } ?? onBeaconNotFound?()
}MinewBeaconManagerDelegate.swift
func minewBeaconManager(_ manager: MinewBeaconManager!, didRangeBeacons beacons: [MinewBeacon]!) {
let foundBeacon = beacons.first { $0.mac == identifier }
foundBeacon.map { onBeaconFound?($0) } ?? onBeaconNotFound?()
}Step 2. Closure β AsyncStream (BeaconClient)
BeaconClient.swift
var onBeaconNotFound: AsyncStream<Void>
var onBeaconFound: AsyncStream<MinewBeacon>
var onBeaconConnect: AsyncStream<ConnectionState>
self.onBeaconFound = AsyncStream { continuation in
manager.onBeaconFound = { continuation.yield($0) }
}Step 3. AsyncStream β TCA Action (Reducer)
BeaconEditorFeature.swift
return .run { send in
async let notFound: Void = {
for await _ in client.onBeaconNotFound { await send(.increaseTimeoutCounter) }
}()
async let found: Void = {
for await beacon in client.onBeaconFound { await send(.updateBeacon(beacon)) }
}()
async let connect: Void = {
for await state in client.onBeaconConnect { await send(.updateConnectionState(state)) }
}()
_ = await (notFound, found, connect)
}Β© Minew Technology Co., Ltd. See MinewSDK for details.
Apache 2.0 Β© VestellaLab, Inc. See LICENSE for details.








