Swift Bluetooth 사용법

ios 디바이스에 내장된 기능들을 연구하던 도중, bluetooth 또한 공부를 해봐야겠다는 생각이 들었습니다. 그래서 이번 시간에는 BlueTooth를 사용하는 방법에 대해 정리하려고 합니다.

 

 

blueTooth의 두 가지 모드

제가 이해한 바로는 bluetooth는 두 가지 모드를 가집니다.

하나는 데이터를 수신할 상태인 centralMode 이고

다른 하나는 데이터를 보낼 상태인 `peripheralMode 입니다.

 

그러니까 블루투스는 보내는 사람과 받는 사람을 명확히 해줘야 한다는 뜻이 되겠습니다.

물론 어떤 장치는 centralMode와 peripheralMode를 번갈아가며 사용할 수 있습니다.

 

우리는 이번에 어떤 장치로부터 데이터를 받아올 겁니다.

그래서 데이터를 수신할 상태인 centralMode에서 peripheral 기기로부터 데이터를 받아오도록 하겠습니다.

 

기본 준비사항 (CBCentral Manager)

먼저 bluetooth 기능을 사용할 viewController에 CoreBluetooth를 import 합니다.

그리고 블루투스 기능들을 관리할 CBCentralManager 를 하나 생성합니다.

var centralManager = CBCentralManger 처럼요.

그런 다음 viewDidLoad() 함수 내에 다음과 같이 적어줍니다.

centralManager = CBCentralManager(delegate: self, queue: nil)

 

그리고 CNCentralManagerDelegate 를 받는 extension을 하나 만들어줍니다.

그런 다음 해당 블록 안에 func centralManagerDidUpdateState(_ central: CBCentralManager) {} 를 하나 선언해줍시다.

블록 안에는 central의 상태에 따라 로직을 처리할 switch-case 문을 하나 적어줄 겁니다.

가장 간단한 방법은 switch central.state {} 만 적어두면 옆에 뜨는 오류메시지에서 fix 를 누르는 것입니다.

 

여기까지 잘 따라하셨다면 코드는 다음과 같아야 합니다.

import CoreBluetooth
import UIKit

class BlueToothViewController: UIViewController {


    var centralManager: CBCentralManager!

    override func viewDidLoad() {
        super.viewDidLoad()

        centralManager = CBCentralManager(delegate: self, queue: nil)
    }

}

extension BlueToothViewController: CBCentralManagerDelegate {


    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        switch central.state {

        case .unknown:
            print("central.state is unknown")
        case .resetting:
            print("central.state is resetting")
        case .unsupported:
            print("central.state is unsupported")
        case .unauthorized:
            print("central.state is unauthorized")
        case .poweredOff:
            print("central.state is poweredOff")
        case .poweredOn:
            print("central.state is poweredOn")
        @unknown default:
            print("central.state default case")
        }
    }   
}

 

또한 애플 공식문서 를 보면 반드시 info.plist에 다음의 사진에 나와있는 것들을 추가하라고 나와있습니다.

ios 버젼에 따라 둘 중 하나를 쓸 것입니다. (말투가 왜이렇게 번역체 같을까요?)

 

 

이대로 실행해보면 다음과 같은 메시지가 콘솔창에 출력됩니다.

central.state is poweredOn

 

 

CBCentralManager로 Peripheral 출력하기

이제 poweredOn case에 다음 한 줄을 적어줍니다.

centralManager.scanForPeripherals(withServices: nil)

이 메서드는 주변의 peripheral device(데이터를 보낼 준비가 돼 있는 장치)들을 스캔합니다.

여기서 서비스에 특정 기기만 적용하고 싶다면 다음 글을 참고해주세요 : 링크

이 정보를 받아오기 위해서는 다음의 메서드를 써야 하는데, 너무 길기 때문에 그냥 device만 치면 나오는 auto-complete를 이용합시다.

 

// extension 안에 적어줍니다.
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
    print(peripheral)
}

 

build and run을 해봅시다.

다음과 같이 주변에 사용할 수 있는 장치들이 검색됩니다.

<CBPeripheral: 0x281aa5540, identifier = DB469E0F-2CDE-D87B-BD7E-6D4191D6A646, name = 최강훈’s MacBook Pro, state = disconnected>
<CBPeripheral: 0x281aa1680, identifier = FF8294B9-AB4A-1FE3-C4B5-968429F58B52, name = (null), state = disconnected>
<CBPeripheral: 0x281aa1680, identifier = EE3EF6D1-A08E-8BAE-538F-017D839CFB62, name = LE_WH-1000XM4, state = disconnected>

 

 

peripheral 연결하기

여기서 제 맥북의 정보만 얻어오고 싶다고 가정하겠습니다.

이를 위해 위에서 출력한 제 맥북의 identifer 복사헤두고,

func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) 함수 안에

다음의 조건문을 적어줍시다.

 

if peripheral.identifier.uuidString == "your macbook pro's identifier"

 

그럼 이제 받아온 peripheral을 연결해야 하는데, 연결 이후 정보를 얻어오기 위해서는 아무래도 자체 peripheral 변수가 필요할 것 같습니다.

그래서 우리가 선언한

var centralManager: CBCentralManager! 밑에

var macbookPeripheral: CBPeripheral! 변수를 추가해주겠습니다.

그리고

 

if peripheral.identifier.uuidString == "your macbook pro's identifier" 조건문 내에

다음을 적어주겠습니다.

 

if peripheral.identifier.uuidString == "DB469E0F-2CDE-D87B-BD7E-6D4191D6A646" {
    self.macbookPeripheral = peripheral
    // macbookPeripheral.delegate = self // 아직 안 씀.  
    centralManager.stopScan()
    centralManager.connect(macbookPeripheral)
}

 

여기까지 했다면 이제 블루투스가 연결이 될 겁니다.

이를 테스트 하기 위해

func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) 함수를

func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) 함수

밑에 추가해줍니다.

 

그리고 그 브라켓 안에 code에 print("Connected")로 교체하여 실행하면

정상적으로 Connected라는 문구가 출력됩니다.

 

service discover하기

블루투스에는 discover 라는 용어를 쓰는 것 같습니다.

그래서 방금 print("Connected") 라고 적어준 코드를 다음 코드로 바꿔줍시다.

macbookPeripheral.discoverServices(nil)

참고로 여기서 nil 이라는 옵션은 모든 서비스를 검색하는 것입니다.

 

discover한 service는 for - in 문을 이용해 출력할 수 있습니다.

그러나

func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) 함수 안에서

for - in 문을 호출하면 제대로 출력되지 않습니다.

connect 되기 전에 함수가 돌아가기 때문입니다.

이를 위해 우리는 CBPeripheralDelegate라는 프로토콜을 하나 더 구현할 것입니다.

extension 하나를 다음과 같이 추가해줍시다.

 

extension BlueToothViewController: CBPeripheralDelegate {
    code
}

 

그리고 if peripheral.identifier.uuidString =="" 영역 안에서 delegate를 self로 바꿔줍시다.

다음과 같이요.

     self.macbookPeripheral = peripheral
  macbookPeripheral.delegate = self // 이 부분.
  centralManager.stopScan()    
  centralManager.connect(macbookPeripheral)    

 

이제 service를 출력해봅시다.

방금 만든 extension 내에 다음 함수를 추가합니다.

func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {

}

 

그리고 내용을 다음과 같이 줍시다.

guard let services = macbookPeripheral.services else {return}
for service in services {
    print(service)
    peripheral.discoverCharacteristics(nil, for: service)
}

 

여기까지 한다면 services 하나하나를 출력해볼 수 있습니다.

build and run을 해봅시다.

<CBService: 0x283501b00, isPrimary = YES, UUID = Device Information>
<CBService: 0x283501bc0, isPrimary = YES, UUID = Continuity>
<CBService: 0x283501c00, isPrimary = YES, UUID = 9FA480E0-4967-4542-9390-D343DC5D04AE>

 

이번 글에서 bluetooth를 테스트해보는 것은 여기가 끝입니다.

전체 코드는 글의 맨 아래에 첨부해두겠습니다.

 

추가로 확인할 수 있는 것들

방금 출력한 Service들은 macbook 정보를 출력해서 그렇지 블루투스를 목적으로 하는 기기에 대한 것들은 조금 다르게 생겼습니다.

이것에 대해 자세히 확인하고 싶다면 다음 링크를 참고해주세요 : 링크

 

전체 코드

 

import CoreBluetooth
import UIKit

class BlueToothViewController: UIViewController {


    var centralManager: CBCentralManager!
    var macbookPeripheral: CBPeripheral!

    override func viewDidLoad() {
        super.viewDidLoad()

        centralManager = CBCentralManager(delegate: self, queue: nil)
    }

}

extension BlueToothViewController: CBCentralManagerDelegate {


    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        switch central.state {

        case .unknown:
            print("central.state is unknown")
        case .resetting:
            print("central.state is resetting")
        case .unsupported:
            print("central.state is unsupported")
        case .unauthorized:
            print("central.state is unauthorized")
        case .poweredOff:
            print("central.state is poweredOff")
        case .poweredOn:
            print("central.state is poweredOn")
            centralManager.scanForPeripherals(withServices: nil)
        @unknown default:
            print("central.state default case")
        }
    }

    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {

        if peripheral.identifier.uuidString == "DB469E0F-2CDE-D87B-BD7E-6D4191D6A646" {
            self.macbookPeripheral = peripheral
            macbookPeripheral.delegate = self
            centralManager.stopScan()
            centralManager.connect(macbookPeripheral)

        }
    }

    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        print("connected!")

        // 아래 파라미터가 nil이면 모든 서비스를 검색.
        macbookPeripheral.discoverServices(nil)
        // 연결 끊기
//        centralManager.cancelPeripheralConnection(peripheral)
    }

}

extension BlueToothViewController: CBPeripheralDelegate {
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        guard let services = macbookPeripheral.services else {return}
        for service in services {
            print(service)
            peripheral.discoverCharacteristics(nil, for: service)
        }
    }

    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        guard let characteristics = service.characteristics else {return}
        for characteristic in characteristics {
            print("characteristic: \(characteristic)")
            if characteristic.properties.contains(.read) {
                print("readable")
                peripheral.readValue(for: characteristic)
            }
        }
    }

    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        print("didUpdateValueFor characteristic")
        print(characteristic.value ?? "can't get value")
    }
}
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기
// custom