[iOS/Swift] Core Bluetooth Ble 블루투스 통신
Core Bluetooth iOS 블루투스 통신
오늘은 iOS에서 Bluetooth 통신 방법을 알아보자
Apple Developer Document 를 참조하여 작성
Chap1. Info.plist 에서 CoreBluetooth 설명 작성
Apple Developer Document의 Core Bluetooth 문서를 보면
https://developer.apple.com/documentation/corebluetooth
Overview에 적혀 있는 경고? 문구를 확인 할 수 있다
그 밑에 Important로 강조하는 부분을 살표보면, 반드시 포함해야한는 정보가 적혀있다
🌏 번역 🌏
: 액세스해야 하는 데이터 유형에 대한 사용 설명 키가 포함되어 있지 않으면 앱이 충돌합니다 . iOS 13 이후에 연결된 앱에서 Core Bluetooth API에 액세스하려면 키를 포함합니다. iOS 12 및 이전 버전에서는 Bluetooth 주변 장치 데이터에 액세스하기 위해 포함합니다.Info.plist NSBluetoothAlwaysUsageDescriptionNSBluetoothPeripheralUsageDescription
➡️ Info.plist에 Core Bluetooth API 사용에 대한 설명을 작성해야한다
"Info.plist"는 실행 패키지에 대한 설명을 적는 파일이며,
Core Bluetooth를 사용하려면 Info.plist에 해당 패키지를 사용하는 이유를 적어야한다
Info.plist에 아래 Key 값을 추가하면 된다
Info.plist
Privacy - Bluetooth Peripheral Usage Description |
Privacy - Bluetooth Always Usage Description |
~ 아래와 같이 추가하면 설정 완료 ~
Chap2. CoreBluetooth 라이브러리 Import
블루투스를 사용하려는 ViewController에 CoreBluetooth 라이브러리 import 한다
import CoreBluetooth
Chap3. CBCentralManager, CBPeripheral 변수 생성
먼저 CBCentralManager, CBPeripheral 가 어떤 역할을 하는지 알아보자
✔️ CBCentralManager
CBCentralManager 주변 장치 검색 및 연결을 포함하여 검색되거나 연결된 원격 주변 장치를 관리합니다 .
메서드 를 호출하기 전에 사용하려는 장치의 블루투스를 전원이 켜진 상태로 설정합니다 .
이 상태는 중앙 장치(예: iPhone 또는 iPad)가 Bluetooth를 지원하고 Bluetooth가 켜져 있고 사용할 수 있음을 나타냅니다.
✔️ CBPeripheral
CBPeripheral클래스는 앱을 통해 검색하는 원격 주변 장치를 나타냅니다. 주변 장치는 개체로 표시되는 UUID(Universally Unique Identifier)를 사용하여 자신을 식별합니다. 주변 장치는 하나 이상의 서비스를 포함하거나 연결된 신호 강도에 대한 유용한 정보를 제공할 수 있습니다
쉽게 설명하면, CBCentralManager는 블루투스를 제어하는 역할이고 CBPeripheral는 검색되는 블루투스 기기이다
아래 코드처럼 선언하여 간단하게 사용가능!
import CoreBluetooth
class ViewController: UIViewController {
var peripherals: [CBPeripheral] = []
var centralManager: CBCentralManager!
override func viewDidLoad() {
super.viewDidLoad()
}
}
Chap4. CBPeripheralDelegate, CBCentralManagerDelegate 생성
CBCentralManager, CBPeripheral 사용하려면 Delegate를 선언해야한다
CBCentralManager ➡️ CBCentralManagerDelegate
CBPeripheral ➡️ CBPeripheralDelegate
CBCentralManagerDelegate는 centralManagerDidUpdateState 함수가 꼭 필요하다
func centralManagerDidUpdateState(_ central: CBCentralManager)
: centralManagerDidUpdateState 함수는 CBCentralManager의 블루투스 상태가 업데이트되면 호출되는 함수
import UIKit
import CoreBluetooth
class ViewController: UIViewController {
var peripherals: [CBPeripheral] = []
var centralManager: CBCentralManager!
override func viewDidLoad() {
super.viewDidLoad()
centralManager = CBCentralManager(delegate: self, queue: nil)
}
}
extension ViewController: CBPeripheralDelegate, CBCentralManagerDelegate{
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .unknown:
print("unknown")
case .resetting:
print("restting")
case .unsupported:
print("unsupported")
case .unauthorized:
print("unauthorized")
case .poweredOff:
print("power Off")
case .poweredOn:
print("power on")
@unknown default:
fatalError()
}
}
}
● 코드 설명
CBPeripheralDelegate, CBCentralManagerDelegate 추가하고 위에서 선언한 centralManager에 대리자를 위임하였다
centralManagerDidUpdateState 함수가 호출 될 때 마다 현재 블루투스 상태를 print 한다
Chap5. 블루투스 검색
이제 본격적으로 블루투스 검색을 해보자
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
let check: Bool = false
if !check {
peripherals.append(peripheral)
print("Discover : ", peripheral.name ?? peripheral.identifier.uuidString)
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber)
: CBCentralManager가 주변 블루투스 장치를 검색 하였을 때 호출된다
Chap6. 블루투스 검색 ViewController 만들기
아래 코드는 검색 기기의 블루투스 On,Off 상태를 나타내고
"검색 시작" 클릭시 검색된 서비스를 표시 "검색 종료" 클릭 시 블루투스 검색을 종료한다
import UIKit
import CoreBluetooth
class ViewController: UIViewController {
lazy var baseView: UIView = {
var view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .white
return view
}()
lazy var topStackView: UIStackView = {
var view = UIStackView()
view.translatesAutoresizingMaskIntoConstraints = false
view.axis = .vertical
view.distribution = .fillEqually
view.alignment = .fill
view.spacing = 8
return view
}()
lazy var stateLb1: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "블루투스 상태"
return label
}()
lazy var stateLb2: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = ":"
return label
}()
lazy var buttonStackView: UIStackView = {
var view = UIStackView()
view.translatesAutoresizingMaskIntoConstraints = false
view.axis = .horizontal
view.distribution = .fillEqually
view.alignment = .fill
view.spacing = 14
return view
}()
lazy var startBtn: UIButton = {
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("검색 시작", for: .normal)
return button
}()
lazy var stopBtn: UIButton = {
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("검색 중단", for: .normal)
return button
}()
lazy var searchLb: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "검색된 블루투스 기기"
return label
}()
lazy var bottomBaseView: UIView = {
var view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .white
return view
}()
lazy var scrollView: UIScrollView = {
var view = UIScrollView()
view.translatesAutoresizingMaskIntoConstraints = false
view.showsVerticalScrollIndicator = false
view.keyboardDismissMode = .onDrag
return view
}()
lazy var bottomStackView: UIStackView = {
var view = UIStackView()
view.translatesAutoresizingMaskIntoConstraints = false
view.axis = .vertical
view.distribution = .fillEqually
view.alignment = .fill
view.spacing = 8
return view
}()
private var peripherals: [CBPeripheral] = []
private var centralManager: CBCentralManager!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
addViews()
applyConstraints()
addTarget()
centralManager = CBCentralManager(delegate: self, queue: nil)
}
fileprivate func addViews() {
view.addSubview(baseView)
baseView.addSubview(topStackView)
baseView.addSubview(bottomBaseView)
bottomBaseView.addSubview(scrollView)
scrollView.addSubview(bottomStackView)
topStackView.addArrangedSubview(stateLb1)
topStackView.addArrangedSubview(stateLb2)
topStackView.addArrangedSubview(buttonStackView)
topStackView.addArrangedSubview(searchLb)
buttonStackView.addArrangedSubview(startBtn)
buttonStackView.addArrangedSubview(stopBtn)
}
fileprivate func applyConstraints() {
let baseViewConstraints = [
baseView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
baseView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
baseView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
baseView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
]
let topStackViewConstraints = [
topStackView.leadingAnchor.constraint(equalTo: baseView.leadingAnchor, constant: 30),
topStackView.trailingAnchor.constraint(equalTo: baseView.trailingAnchor, constant: -30),
topStackView.topAnchor.constraint(equalTo: baseView.topAnchor, constant: 30),
]
let bottomBaseViewConstraints = [
bottomBaseView.leadingAnchor.constraint(equalTo: baseView.leadingAnchor, constant: 30),
bottomBaseView.trailingAnchor.constraint(equalTo: baseView.trailingAnchor, constant: -30),
bottomBaseView.topAnchor.constraint(equalTo: topStackView.bottomAnchor, constant: 30),
bottomBaseView.bottomAnchor.constraint(equalTo: baseView.bottomAnchor, constant: -30),
]
let scrollViewConstraints = [
scrollView.frameLayoutGuide.leadingAnchor.constraint(equalTo: bottomBaseView.leadingAnchor),
scrollView.frameLayoutGuide.trailingAnchor.constraint(equalTo: bottomBaseView.trailingAnchor),
scrollView.frameLayoutGuide.topAnchor.constraint(equalTo: bottomBaseView.topAnchor),
scrollView.frameLayoutGuide.bottomAnchor.constraint(equalTo: bottomBaseView.bottomAnchor)
]
let bottomStackViewConstraints = [
bottomStackView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
bottomStackView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
bottomStackView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
bottomStackView.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor),
bottomStackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
]
NSLayoutConstraint.activate(baseViewConstraints)
NSLayoutConstraint.activate(topStackViewConstraints)
NSLayoutConstraint.activate(bottomBaseViewConstraints)
NSLayoutConstraint.activate(scrollViewConstraints)
NSLayoutConstraint.activate(bottomStackViewConstraints)
}
fileprivate func addTarget() {
startBtn.addTarget(self, action: #selector(didTapStartButton(_:)), for: .touchUpInside)
stopBtn.addTarget(self, action: #selector(didTapStopButton(_:)), for: .touchUpInside)
}
@objc func didTapStartButton(_ sender: UIButton) {
print("검색 시작")
if(!centralManager.isScanning){
centralManager?.scanForPeripherals(withServices: nil, options: nil)
}
}
@objc func didTapStopButton(_ sender: UIButton) {
print("검색 종료")
centralManager.stopScan()
}
fileprivate func addPeripheral(serial: String) {
lazy var serialLb: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = serial
return label
}()
bottomStackView.addArrangedSubview(serialLb)
}
}
extension ViewController : CBPeripheralDelegate, CBCentralManagerDelegate{
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .unknown:
print("unknown")
case .resetting:
print("restting")
case .unsupported:
print("unsupported")
case .unauthorized:
print("unauthorized")
case .poweredOff:
print("power Off")
stateLb2.text = ": 블루투스 OFF"
case .poweredOn:
print("power on")
stateLb2.text = ": 블루투스 ON"
@unknown default:
fatalError()
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
let check: Bool = false
if !check {
peripherals.append(peripheral)
addPeripheral(serial: peripheral.name ?? peripheral.identifier.uuidString)
}
}
// 기기 연결가 연결되면 호출되는 메서드입니다.
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.delegate = self
}
// characteristic 검색에 성공 시 호출되는 메서드입니다.
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
}
// writeType이 .withResponse일 때, 블루투스 기기로부터의 응답이 왔을 때 호출되는 함수입니다.
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
// writeType이 .withResponse인 블루투스 기기로부터 응답이 왔을 때 필요한 코드를 작성합니다.(필요하다면 작성해주세요.)
}
// 블루투스 기기의 신호 강도를 요청하는 peripheral.readRSSI()가 호출하는 함수입니다.
func peripheral(_ peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: Error?) {
// 신호 강도와 관련된 코드를 작성합니다.(필요하다면 작성해주세요.)
}
}
✨ 완성 ✨
👇 깃허브에서 전체 소스코드 보기 👇
https://github.com/hyeebin/ex-bluetooth-ios
참조
https://developer.apple.com/documentation/corebluetooth%EF%BB%BF
https://staktree.github.io/ios/iOS-Bluetooth-02-search-peripheral/