Skip to content

[Feat] BLE 통신 iOS 초안 구현#5

Merged
ParkSeongGeun merged 3 commits intodevfrom
feat/BLE
Oct 16, 2025
Merged

[Feat] BLE 통신 iOS 초안 구현#5
ParkSeongGeun merged 3 commits intodevfrom
feat/BLE

Conversation

@ParkSeongGeun
Copy link
Copy Markdown
Contributor

@ParkSeongGeun ParkSeongGeun commented Oct 16, 2025

개요

  • ESP32 모듈과의 블루투스 연결을 위한 초안입니다.

스크린샷

image image 스크린샷 2025-10-16 오전 11 20 22

수정사항

  • CoreBluetooth 기반 BLE 통신 구조
    • CBCentralManager를 사용한 중앙 디바이스 역할 수행
    • @published 속성으로 스캔 상태 및 블루투스 상태 관리
    • ObservableObject 패턴으로 SwiftUI와 연동
  • 기기 스캔 및 연결 메커니즘
    • scanForPeripherals()로 주변 BLE 기기 검색
    • 10초 타임아웃 설정 (기기를 찾지 못하면 자동 실패 처리)
    • 블루투스 상태 감지 (poweredOn/Off, unauthorized, unsupported)
  • 배려석 알림 전송 메서드
    • sendCourtesySeatNotification(busNumber:completion:) 공개 API
    • 비동기 콜백 기반 성공/실패 처리
    • 블루투스 비활성화 시 즉시 실패 반환

TODO

아직까지 ESP32 모듈 세팅에 관해 수행되지 않았기 때문에 블루투스 기능 테스트를 위한 코드 작성을 했습니다.

  • 무차별 기기 연결: 첫 번째 발견된 BLE 기기에 무조건 연결 시도
  • 실제 데이터 전송 없음: 연결 성공만으로 전송 성공 처리
  • 버스 기기 식별 불가: 모든 주변 BLE 기기를 대상으로 스캔

해당 부분들은 ESP32 모듈 설정 이후 개선할 예정입니다.

  • TODO 1: 버스 기기 필터링 로직
  • TODO 2: 현재는 첫 번째 기기에 연결을 시도하는데, 사용자가 선택한 버스 번호와 매칭 -> 버스 기기 검증 후에만 연결할 수 있도록 개선
  • 실제 데이터 전송 완료 + 버스 응답 확인까지 구현

Summary by CodeRabbit

  • New Features

    • Added the ability to send a courtesy seat notification to nearby buses via Bluetooth.
    • Introduced a confirmation dialog before sending, showing the selected bus name.
    • Added clear success and failure alerts after attempting to send the notification.
  • Chores

    • Updated app permissions to request Bluetooth access with user-facing explanations for connecting to nearby devices.

@ParkSeongGeun ParkSeongGeun self-assigned this Oct 16, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 16, 2025

Walkthrough

Introduces a BluetoothManager to handle BLE scanning/connection and a courtesy-seat notification flow. HomeView integrates this manager, adds confirmation/success/failure alerts, and triggers notifications when a route is selected. Info.plist adds Bluetooth permission usage descriptions required for scanning and peripheral access.

Changes

Cohort / File(s) Change summary
UI: Home alert and action flow
ComfortableMove/ComfortableMove/Core/Home/HomeView.swift
Adds BluetoothManager as a StateObject; introduces confirm/success/failure alerts; computes selectedBusName; implements sendCourtesySeatNotification call with completion handling; wires button to show confirm alert and present outcome alerts.
Bluetooth core logic
ComfortableMove/ComfortableMove/Core/Manager/BluetoothManager.swift
New ObservableObject managing CBCentralManager lifecycle; published isScanning and bluetoothState; implements sendCourtesySeatNotification with 10s timeout; delegate methods for state updates, discovery, connect, failure, and disconnect; placeholders/TODOs for filtering and GATT operations.
App permissions
ComfortableMove/ComfortableMove/Info.plist
Adds NSBluetoothAlwaysUsageDescription and NSBluetoothPeripheralUsageDescription strings explaining nearby Bluetooth device connectivity.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant HomeView
  participant BluetoothManager
  participant CBCentralManager as CoreBluetooth

  User->>HomeView: Tap "Send courtesy seat" (route selected)
  HomeView->>HomeView: Show confirm alert
  User-->>HomeView: Confirm
  HomeView->>BluetoothManager: sendCourtesySeatNotification(busNumber)
  BluetoothManager->>CBCentralManager: startScan()
  Note over BluetoothManager,CBCentralManager: Timeout set (10s)

  CBCentralManager-->>BluetoothManager: didDiscover(peripheral)
  BluetoothManager->>CBCentralManager: connect(peripheral)
  CBCentralManager-->>BluetoothManager: didConnect(peripheral)
  Note over BluetoothManager: Placeholder: discover services/characteristics<br/>and write "courtesy seat" signal
  BluetoothManager->>CBCentralManager: stopScan()/disconnect()

  alt Success
    BluetoothManager-->>HomeView: completion(true)
    HomeView->>HomeView: Show success alert
  else Failure/Timeout
    BluetoothManager-->>HomeView: completion(false)
    HomeView->>HomeView: Show failure alert
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I hopped through code with Bluetooth cheer,
Beep-beep! A bus draws near.
Alerts pop up—confirm, succeed—
A courteous seat for every need.
With plist whispers granting rights,
I scan the air on city nights. 🐇📶

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "[Feat/ble] 블루투스" (translating to "[Feat/ble] Bluetooth") is directly related to the main change in this PR, which implements Bluetooth/BLE communication functionality through the new BluetoothManager class, HomeView integration, and required permission configurations. The title accurately reflects the core technical component being added to the codebase. However, the title is quite minimal and somewhat overly broad—it identifies the technology but doesn't convey the specific functionality (courtesy seat notifications) or the purpose of the integration, which means a teammate scanning the commit history might need additional context to understand the complete scope of the change.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/BLE

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ParkSeongGeun
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 16, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7c9e1c0 and 042c9a2.

📒 Files selected for processing (3)
  • ComfortableMove/ComfortableMove/Core/Home/HomeView.swift (3 hunks)
  • ComfortableMove/ComfortableMove/Core/Manager/BluetoothManager.swift (1 hunks)
  • ComfortableMove/ComfortableMove/Info.plist (1 hunks)
🧰 Additional context used
🪛 SwiftLint (0.57.0)
ComfortableMove/ComfortableMove/Core/Manager/BluetoothManager.swift

[Warning] 78-78: TODOs should be resolved (버스 기기만 필터링하는 로직 추가 필요)

(todo)


[Warning] 83-83: TODOs should be resolved (임시 코드 - 첫 번째 발견된 기기에 무조건 연결 시도)

(todo)


[Warning] 95-95: TODOs should be resolved (실제 배려석 알림 신호 전송 로직 구현 필요)

(todo)


[Warning] 103-103: TODOs should be resolved (임시 코드 - 연결만 성공해도 전송 성공으로 간주)

(todo)

Comment on lines +39 to +44
DispatchQueue.main.asyncAfter(deadline: .now() + 10) { [weak self] in
guard let self = self else { return }
if self.isScanning {
self.stopScanning()
self.onTransmitComplete?(false)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Reset connection state on failures/timeouts.

When the timeout fires or didFailToConnect runs, targetPeripheral stays non-nil. A subsequent scan won’t connect to anything because the guard in didDiscover keeps the stale peripheral, and onTransmitComplete can be invoked multiple times across retries. Clear the peripheral reference (and the completion) whenever we abandon a transmission.

         DispatchQueue.main.asyncAfter(deadline: .now() + 10) { [weak self] in
             guard let self = self else { return }
             if self.isScanning {
                 self.stopScanning()
+                self.targetPeripheral = nil
                 self.onTransmitComplete?(false)
+                self.onTransmitComplete = nil
             }
         }
@@
     func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
         print("기기 연결 실패: \(error?.localizedDescription ?? "알 수 없는 오류")")
         stopScanning()
+        targetPeripheral = nil
         onTransmitComplete?(false)
+        onTransmitComplete = nil
     }

Also applies to: 111-115

🤖 Prompt for AI Agents
In ComfortableMove/ComfortableMove/Core/Manager/BluetoothManager.swift around
lines 39-44 (and also apply same change to lines ~111-115), the
timeout/failed-connect paths abandon a transmission but leave targetPeripheral
and onTransmitComplete set, causing stale peripheral guards and duplicate
completion calls; update the timeout closure and didFailToConnect handler to
reset state by setting targetPeripheral = nil and capturing & clearing
onTransmitComplete (call it once then set to nil) before invoking it, and ensure
stopScanning()/disconnect logic still runs so subsequent scans can pick new
peripherals and retries won't double-invoke the completion.

Comment on lines +83 to +88
// TODO: 임시 코드 - 첫 번째 발견된 기기에 무조건 연결 시도
// 실제로는 버스 기기인지 확인 후 연결해야 함
if targetPeripheral == nil {
targetPeripheral = peripheral
centralManager.connect(peripheral, options: nil)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Stop scanning before initiating a connection attempt.

isScanning stays true until stopScanning() runs, so the 10 s timeout closure still sees the scan as active while centralManager.connect is in-flight. The timeout then fires, forces a failure callback, and a later didConnect/didFailToConnect triggers a second callback, leaving the UI in an inconsistent state. Stop the scan as soon as you select a target peripheral so the timeout can no longer trip the failure path.

         if targetPeripheral == nil {
             targetPeripheral = peripheral
-            centralManager.connect(peripheral, options: nil)
+            stopScanning()
+            centralManager.connect(peripheral, options: nil)
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// TODO: 임시 코드 - 첫 번째 발견된 기기에 무조건 연결 시도
// 실제로는 버스 기기인지 확인 후 연결해야 함
if targetPeripheral == nil {
targetPeripheral = peripheral
centralManager.connect(peripheral, options: nil)
}
// TODO: 임시 코드 - 첫 번째 발견된 기기에 무조건 연결 시도
// 실제로는 버스 기기인지 확인 후 연결해야 함
if targetPeripheral == nil {
targetPeripheral = peripheral
stopScanning()
centralManager.connect(peripheral, options: nil)
}
🧰 Tools
🪛 SwiftLint (0.57.0)

[Warning] 83-83: TODOs should be resolved (임시 코드 - 첫 번째 발견된 기기에 무조건 연결 시도)

(todo)

🤖 Prompt for AI Agents
In ComfortableMove/ComfortableMove/Core/Manager/BluetoothManager.swift around
lines 83–88, stop the scanner immediately before calling centralManager.connect
to prevent the existing scan timeout from still being active and triggering a
spurious failure; call centralManager.stopScan() (and update any isScanning
state) right after selecting targetPeripheral and, if you have a scan timeout
timer/closure, invalidate or cancel it before initiating connect so only the
real connect callbacks (didConnect/didFailToConnect) drive UI state.

@ParkSeongGeun ParkSeongGeun merged commit 63faeff into dev Oct 16, 2025
2 checks passed
@ParkSeongGeun ParkSeongGeun changed the title [Feat/ble] 블루투스 [Feat] BLE 통신 iOS 초안 구현 Oct 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant