BLE Connection Flow
This guide covers the complete flow from device discovery to a successful connection, along with lifecycle management (disconnect, unbind).
Connection State Machine
The SDK models the connection process as 5 sequential states. The device is only usable once it reaches the final state:
eTSBleStateDisconnected
│
│ connectWithPeripheral: called
▼
eTSBleStateConnecting ← Physical BLE link establishing
│
│ Physical link established
▼
eTSBleStateAuthenticating ← Binding / login authentication
│
│ Authentication passed
▼
eTSBleStatePreparingData ← Fetching device base info
│
│ Info sync complete
▼
eTSBleStateConnected ← Device ready for all operations
Only after reaching eTSBleStateConnected can you call health data, settings, or other feature APIs.
Step-by-Step Code
Step 1: Scan for Devices
// Start scanning (30-second timeout)
[bleConnect startSearchPeripheral:30
discoverPeripheral:^(TSPeripheral *peripheral) {
// Called once for each device found
TSLog(@"Found: %@ | MAC: %@",
peripheral.systemInfo.bleName,
peripheral.systemInfo.macAddress);
[self.peripheralList addObject:peripheral];
[self.tableView reloadData];
} completion:^(TSScanCompletionReason reason, NSError *error) {
switch (reason) {
case eTSScanCompleteReasonTimeout:
TSLog(@"Scan timed out — found %lu device(s)", (unsigned long)self.peripheralList.count);
break;
case eTSScanCompleteReasonUserStopped:
TSLog(@"Scan stopped by user");
break;
case eTSScanCompleteReasonPermissionDenied:
TSLog(@"Bluetooth permission denied");
[self showBluetoothPermissionAlert];
break;
default:
if (error) TSLog(@"Scan error: %@", error.localizedDescription);
break;
}
}];
Step 2: Stop Scanning When User Selects a Device
- (void)didSelectPeripheral:(TSPeripheral *)peripheral {
// Stop scanning immediately to save battery
[bleConnect stopSearchPeripheral];
[self connectToPeripheral:peripheral];
}
Step 3: First-Time Connection (Binding)
First-time connection requires an authCode obtained by scanning the QR code on the device:
- (void)connectToPeripheral:(TSPeripheral *)peripheral {
TSPeripheralConnectParam *param = [[TSPeripheralConnectParam alloc] initWithUserId:self.userId];
// Required for first-time binding (scanned from device QR code)
param.authCode = self.scannedAuthCode;
// Phone info (displayed on the device)
param.brand = @"Apple";
param.model = [UIDevice currentDevice].model;
param.systemVersion = [UIDevice currentDevice].systemVersion;
// User info (for device personalization)
TSUserInfoModel *userInfo = [[TSUserInfoModel alloc] init];
userInfo.age = 28;
userInfo.gender = 1; // 1 = male, 0 = female
userInfo.height = 175;
userInfo.weight = 70;
param.userInfo = userInfo;
[bleConnect connectWithPeripheral:peripheral
param:param
completion:^(TSBleConnectionState state, NSError *error) {
if (error) {
TSLog(@"Connection failed: %@", error.localizedDescription);
[self showConnectFailedAlert:error];
return;
}
switch (state) {
case eTSBleStateConnecting:
[self showLoadingWith:@"Connecting..."];
break;
case eTSBleStateAuthenticating:
[self showLoadingWith:@"Authenticating..."];
break;
case eTSBleStatePreparingData:
[self showLoadingWith:@"Preparing..."];
break;
case eTSBleStateConnected:
TSLog(@"Device connected!");
[self hideLoading];
[self onDeviceConnected:peripheral];
break;
case eTSBleStateDisconnected:
[self hideLoading];
break;
}
}];
}
Step 4: Reconnecting (Already-Bound Device)
On subsequent app launches, use reconnectWithPeripheral: — no QR code needed:
- (void)reconnectSavedDevice {
TSPeripheral *savedDevice = [self loadSavedPeripheral];
if (!savedDevice) return;
TSPeripheralConnectParam *param = [[TSPeripheralConnectParam alloc] initWithUserId:self.userId];
[bleConnect reconnectWithPeripheral:savedDevice
param:param
completion:^(TSBleConnectionState state, NSError *error) {
if (state == eTSBleStateConnected) {
TSLog(@"Reconnected successfully");
} else if (error) {
TSLog(@"Reconnect failed: %@", error.localizedDescription);
// Typically need to re-scan and bind
}
}];
}
Monitoring Connection State Changes
Listen for disconnection events throughout the app lifecycle:
// Set in AppDelegate or a global manager
[bleConnect setKitDelegate:self];
// Implement the delegate method (see TSBleConnectInterface protocol)
- (void)bleConnectStateDidChanged:(TSBleConnectionState)state {
if (state == eTSBleStateDisconnected) {
TSLog(@"Device disconnected");
[self handleDeviceDisconnected];
} else if (state == eTSBleStateConnected) {
TSLog(@"Device reconnected");
[self handleDeviceConnected];
}
}
Disconnecting
// Disconnect while keeping binding info (can reconnect later)
[bleConnect disconnectCompletion:^(BOOL isSuccess, NSError *error) {
TSLog(@"Disconnect: %@", isSuccess ? @"success" : error.localizedDescription);
}];
Unbinding
// Full unbind — requires QR scan to rebind next time
[bleConnect unbindPeripheralCompletion:^(BOOL isSuccess, NSError *error) {
if (isSuccess) {
TSLog(@"Unbound — clearing saved device info");
[self clearSavedDevice];
[self navigateToDeviceList];
}
}];
Best Practices
- First connection: Use
connectWithPeripheral:param:completion:with the scannedauthCode - Subsequent launches: Use
reconnectWithPeripheral:param:completion:— noauthCoderequired - Enable auto-reconnect: Set
autoConnectWhenAppLaunch = YESinTSKitConfigOptions— the SDK will automatically reconnect to the last device on app launch - State check: Call
[bleConnect isConnected]before invoking any feature API - Feature support check: Not all devices support every feature — call
[featureKit isSupport]before use
Important Notes
- Scanning and connecting only works on a real device — iOS Simulator does not support BLE
- The
completionblock inconnectWithPeripheral:is called multiple times (once per state transition) - Other feature modules can only be used after
eTSBleStateConnectedis reached - The BLE connection persists when the app goes to background, but some operations (e.g., data sync) may be restricted by the system
- An app can only be connected to one device at a time