iOS Swift/iOS Swift - 예제

[iOS/Swift] Core Bluetooth Ble 블루투스 통신

주니어코더 2023. 7. 26. 16:12

 

 

 

 

 

 

Core Bluetooth  iOS 블루투스 통신

 

 

 

오늘은 iOS에서 Bluetooth 통신 방법을 알아보자

Apple Developer Document 를 참조하여 작성

 

 

 

 

 

Chap1.  Info.plist 에서 CoreBluetooth 설명 작성


 

Apple Developer Document의 Core Bluetooth 문서를 보면

https://developer.apple.com/documentation/corebluetooth

 

Core Bluetooth | Apple Developer Documentation

Communicate with Bluetooth low energy and BR/EDR (“Classic”) Devices.

developer.apple.com

 

 

 

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

 

 

CBCentralManagerDelegatecentralManagerDidUpdateState 함수가 꼭 필요하다

 

 

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

 

GitHub - hyeebin/ex-bluetooth-ios: ios로 블루투스 연결하기 with Core Bluetooth

ios로 블루투스 연결하기 with Core Bluetooth. Contribute to hyeebin/ex-bluetooth-ios development by creating an account on GitHub.

github.com

 

 

 

참조   

https://developer.apple.com/documentation/corebluetooth%EF%BB%BF

https://staktree.github.io/ios/iOS-Bluetooth-02-search-peripheral/

반응형