Notice
Recent Posts
Recent Comments
Link
투케이2K
155. (TWOK/UTIL) [Ios/Swift] C_Bluetooth_Advertising_Scan_Module : 블루투스 스캔 및 신호 활성 클래스 본문
투케이2K 유틸파일
155. (TWOK/UTIL) [Ios/Swift] C_Bluetooth_Advertising_Scan_Module : 블루투스 스캔 및 신호 활성 클래스
투케이2K 2025. 1. 12. 10:47[설 명]
프로그램 : Ios / Swift
설 명 : C_Bluetooth_Advertising_Scan_Module : 블루투스 스캔 및 신호 활성 클래스
[소스 코드]
import Foundation
import UIKit
// -----------------------------------------
// [블루투스 기능 사용을 위한 import]
import CoreBluetooth
// -----------------------------------------
class C_Bluetooth_Advertising_Scan_Module: NSObject, CBCentralManagerDelegate, CBPeripheralManagerDelegate {
/**
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* TODO [클래스 설명]
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* 1. 블루투스 스캔 및 신호 활성 클래스
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* 2. 필요 퍼미션 권한 설정 : 블루투스 사용 권한
*
* - Privacy - Bluetooth Always Usage Description
* - Privacy - Bluetooth Peripheral Usage Description
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* 3. 필요 import 호출 선언 :
*
* - import CoreBluetooth
* - import UIKit
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* */
/**
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* // TODO [빠른 로직 찾기 : 주석 로직 찾기]
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* [SEARCH FAST] : observableBluetoothScanList : 블루투스 실시간 목록 스캔 결과 반환
*
* >> Android 에서 Advertising 한 Name 정보로 구분 필요
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* [SEARCH FAST] : observableBluetoothAdvertising : 블루투스 실시간 신호 활성 수행
*
* >> Android 에서 블루투스 스캔 시 Name 정보 확인 가능
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* [SEARCH FAST] : stopBluetoothAdvertising : 블루투스 실시간 신호 활성 종료
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
*
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
*
* // -----------------------------------------------------------------------------------------------------------------------------------------------------------------
* */
// -----------------------------------------------------------------------------------------
// MARK: - [전역 변수 선언]
// -----------------------------------------------------------------------------------------
public static let ACTIVITY_NAME = "C_Bluetooth_Advertising_Scan_Module"
let SEARCH_BLUETOOTH_NAME = "2K-BLUETOOTH" // MARK: [찾으려는 블루투스 이름]
var CALL_PROC = "" // [블루투스 스캔 및 신호 활성 구분자]
public static let CALL_SCAN = "CALL_SCAN"
public static let CALL_ADVERTISING = "CALL_ADVERTISING"
// -----------------------------------------------------------------------------------------
// MARK: - [SEARCH FAST] : observableBluetoothScanList : 블루투스 실시간 목록 스캔 결과 반환
// -----------------------------------------------------------------------------------------
// [필요 info plist / import 및 delegate]
// -----------------------------------------------------------------------------------------
/*
// [필요 info plist]
Privacy - Bluetooth Always Usage Description
Privacy - Bluetooth Peripheral Usage Description
// [필요 import 및 delegate]
import CoreBluetooth / CBCentralManagerDelegate / CBPeripheralManagerDelegate
*/
// -----------------------------------------------------------------------------------------
// [소스 코드 사용 방법]
// -----------------------------------------------------------------------------------------
/*
C_Bluetooth_Advertising_Scan_Module().observableBluetoothScanList(){(result) in
S_Log._D_(description: "실시간 블루투스 리스트 스캔 결과", data: ["\(result)"])
}
*/
// -----------------------------------------------------------------------------------------
var scanBluetoothList: Array<Dictionary<String, String>> = []
private let scanBluetoothOperationQueue = OperationQueue() // [블루투스 스캔 작업 큐 정의]
public static let BLE_SCAN_TIME_OUT = 15.0 // [블루투스 스캔 타임 아웃 시간]
var scanBluetoothCentralManager: CBCentralManager! // [블루투스 활성 상태 확인 및 실시간 블루투스 신호 스캔]
func observableBluetoothScanList(callback: @escaping (Array<Dictionary<String, String>>) -> ()) {
S_Log._D_(description: "\(C_Bluetooth_Advertising_Scan_Module.ACTIVITY_NAME) :: observableBluetoothScanList :: 실시간 블루투스 리스트 스캔 실시", data: nil)
// ---------------------------------------------
// [CALL 호출 값 지정]
// ---------------------------------------------
self.CALL_PROC = C_Bluetooth_Advertising_Scan_Module.CALL_SCAN
// ---------------------------------------------
// [초기 값 초기화]
// ---------------------------------------------
self.scanBluetoothList = []
// ---------------------------------------------
// [작업 큐에 추가]
// ---------------------------------------------
self.scanBluetoothOperationQueue.isSuspended = true
let block = { callback(self.scanBluetoothList) }
self.scanBluetoothOperationQueue.addOperation(block)
// ---------------------------------------------
// [비동기 작업 수행 실시]
// ---------------------------------------------
DispatchQueue.main.async {
// [실시간 블루투스 권한 확인]
self.scanBluetoothCentralManager = CBCentralManager(delegate: self, queue: nil)
}
}
// -----------------------------------------------------------------------------------------
// MARK: - [SEARCH FAST] : observableBluetoothAdvertising : 블루투스 실시간 신호 활성 수행
// -----------------------------------------------------------------------------------------
// [필요 info plist / import 및 delegate]
// -----------------------------------------------------------------------------------------
/*
// [필요 info plist]
Privacy - Bluetooth Always Usage Description
Privacy - Bluetooth Peripheral Usage Description
// [필요 import 및 delegate]
import CoreBluetooth / CBCentralManagerDelegate / CBPeripheralManagerDelegate
*/
// -----------------------------------------------------------------------------------------
// [소스 코드 사용 방법]
// -----------------------------------------------------------------------------------------
/*
// 랜덤 : 0000ff00-0000-cccc-8000-00805f9b34fb
// 캐릭터1 : 0000ff01-0000-1000-8000-00805f9b34fb READ WRITE (디바이스 정보)
// 캐릭터2 : 0000ff02-0000-1000-8000-00805f9b34fb 디바이스 이름
// 캐릭터3 : 0000ff03-0000-1000-8000-00805f9b34fb Notification
let name = "TWOK_BLUETOOTH"
let uuid = "0000FF01-0000-1000-8000-00805F9B34FB"
C_Bluetooth_Advertising_Scan_Module().observableBluetoothAdvertising(bleName: name, bleUuid:uuid){(result) in
S_Log._D_(description: "실시간 블루투스 신호 활성 결과", data: ["\(result)"])
DispatchQueue.main.asyncAfter(deadline: .now() + 10) { // [신호 활성 종료 타임 아웃 지정]
if C_Bluetooth_Advertising_Scan_Module.peripheralManager != nil {
// [블루투스 신호 활성 종료]
C_Bluetooth_Advertising_Scan_Module().stopBluetoothAdvertising()
}
}
}
*/
// -----------------------------------------------------------------------------------------
var advertisingResult = false
var advertisingName = ""
var advertisingUuid = ""
private let advertisingBluetoothOperationQueue = OperationQueue() // [블루투스 신호 활성 작업 큐 정의]
var advertisingBluetoothCentralManager: CBCentralManager! // [블루투스 활성 상태 확인]
public static var peripheralManager: CBPeripheralManager! // [실시간 블루투스 신호 활성]
func observableBluetoothAdvertising(bleName: String, bleUuid:String, callback: @escaping (Bool) -> ()) {
S_Log._D_(description: "\(C_Bluetooth_Advertising_Scan_Module.ACTIVITY_NAME) :: observableBluetoothAdvertising :: 실시간 블루투스 신호 활성 실시", data: [
"BLE_NAME :: \(bleName)",
"BLE_UUID :: \(bleUuid)"
])
// ---------------------------------------------
// [CALL 호출 값 지정]
// ---------------------------------------------
self.CALL_PROC = C_Bluetooth_Advertising_Scan_Module.CALL_ADVERTISING
// ---------------------------------------------
// [초기 값 초기화]
// ---------------------------------------------
self.advertisingResult = false
self.advertisingName = "\(bleName)"
self.advertisingUuid = "\(bleUuid)"
// ---------------------------------------------
// [작업 큐에 추가]
// ---------------------------------------------
self.advertisingBluetoothOperationQueue.isSuspended = true
let block = { callback(self.advertisingResult) }
self.advertisingBluetoothOperationQueue.addOperation(block)
// ---------------------------------------------
// [비동기 작업 수행 실시]
// ---------------------------------------------
DispatchQueue.main.async {
// [실시간 블루투스 권한 확인 수행]
self.advertisingBluetoothCentralManager = CBCentralManager(delegate: self, queue: nil)
}
}
// -----------------------------------------------------------------------------------------
// MARK: - [SEARCH FAST] : stopBluetoothAdvertising : 블루투스 실시간 신호 활성 종료
// -----------------------------------------------------------------------------------------
// [필요 info plist / import 및 delegate]
// -----------------------------------------------------------------------------------------
/*
// [필요 info plist]
Privacy - Bluetooth Always Usage Description
Privacy - Bluetooth Peripheral Usage Description
// [필요 import 및 delegate]
import CoreBluetooth / CBCentralManagerDelegate / CBPeripheralManagerDelegate
*/
// -----------------------------------------------------------------------------------------
// [소스 코드 사용 방법]
// -----------------------------------------------------------------------------------------
/*
C_Bluetooth_Advertising_Scan_Module().stopBluetoothAdvertising()
*/
// -----------------------------------------------------------------------------------------
func stopBluetoothAdvertising() {
S_Log._D_(description: "\(C_Bluetooth_Advertising_Scan_Module.ACTIVITY_NAME) :: stopBluetoothAdvertising :: 실시간 블루투스 신호 활성 종료 수행", data: nil)
// ---------------------------------------------
// [비동기 작업 수행 실시]
// ---------------------------------------------
DispatchQueue.main.async {
if C_Bluetooth_Advertising_Scan_Module.peripheralManager != nil {
// [블루투스 신호 활성 종료]
C_Bluetooth_Advertising_Scan_Module.peripheralManager.stopAdvertising()
C_Bluetooth_Advertising_Scan_Module.peripheralManager.removeAllServices()
C_Bluetooth_Advertising_Scan_Module.peripheralManager = nil
}
}
}
// -----------------------------------------------------------------------------------------
// MARK: - [블루투스 권한 부여 상태 응답 확인] : CBCentralManagerDelegate : Delegate
// -----------------------------------------------------------------------------------------
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
// ---------------------------------------------------------
case .unknown:
S_Log._E_(description: "\(C_Bluetooth_Advertising_Scan_Module.ACTIVITY_NAME) :: 블루투스 권한 :: unknown :: 블루투스 상태 알수 없음", data: ["\(self.CALL_PROC)"])
if self.CALL_PROC == C_Bluetooth_Advertising_Scan_Module.CALL_SCAN { // [스캔 수행]
self.scanBluetoothOperationQueue.isSuspended = false // [블루투스 스캔 큐 해제]
}
else { // [신호 활성]
self.advertisingBluetoothOperationQueue.isSuspended = false // [블루투스 신호 활성 큐 해제]
}
// ---------------------------------------------------------
case .resetting:
S_Log._E_(description: "\(C_Bluetooth_Advertising_Scan_Module.ACTIVITY_NAME) :: 블루투스 권한 :: resetting :: 블루투스 서비스 리셋", data: ["\(self.CALL_PROC)"])
if self.CALL_PROC == C_Bluetooth_Advertising_Scan_Module.CALL_SCAN { // [스캔 수행]
self.scanBluetoothOperationQueue.isSuspended = false // [블루투스 스캔 큐 해제]
}
else { // [신호 활성]
self.advertisingBluetoothOperationQueue.isSuspended = false // [블루투스 신호 활성 큐 해제]
}
// ---------------------------------------------------------
case .unsupported:
S_Log._E_(description: "\(C_Bluetooth_Advertising_Scan_Module.ACTIVITY_NAME) :: 블루투스 권한 :: unsupported :: 기기가 블루투스를 지원하지 않습니다", data: ["\(self.CALL_PROC)"])
if self.CALL_PROC == C_Bluetooth_Advertising_Scan_Module.CALL_SCAN { // [스캔 수행]
self.scanBluetoothOperationQueue.isSuspended = false // [블루투스 스캔 큐 해제]
}
else { // [신호 활성]
self.advertisingBluetoothOperationQueue.isSuspended = false // [블루투스 신호 활성 큐 해제]
}
// ---------------------------------------------------------
case .unauthorized:
S_Log._E_(description: "\(C_Bluetooth_Advertising_Scan_Module.ACTIVITY_NAME) :: 블루투스 권한 :: unauthorized :: 블루투스 사용 권한 확인 필요", data: ["\(self.CALL_PROC)"])
if self.CALL_PROC == C_Bluetooth_Advertising_Scan_Module.CALL_SCAN { // [스캔 수행]
self.scanBluetoothOperationQueue.isSuspended = false // [블루투스 스캔 큐 해제]
}
else { // [신호 활성]
self.advertisingBluetoothOperationQueue.isSuspended = false // [블루투스 신호 활성 큐 해제]
}
// MARK: [앱 블루투스 권한 사용 설정창 이동 및 확인 필요]
// ---------------------------------------------------------
case .poweredOff:
S_Log._E_(description: "\(C_Bluetooth_Advertising_Scan_Module.ACTIVITY_NAME) :: 블루투스 권한 :: poweredOff :: 블루투스 비활성 상태", data: ["\(self.CALL_PROC)"])
if self.CALL_PROC == C_Bluetooth_Advertising_Scan_Module.CALL_SCAN { // [스캔 수행]
self.scanBluetoothOperationQueue.isSuspended = false // [블루투스 스캔 큐 해제]
}
else { // [신호 활성]
self.advertisingBluetoothOperationQueue.isSuspended = false // [블루투스 신호 활성 큐 해제]
}
// MARK: [자동으로 시스템에서 비활성 상태 알림 및 팝업창 호출 실시]
// ---------------------------------------------------------
case .poweredOn:
S_Log._W_(description: "\(C_Bluetooth_Advertising_Scan_Module.ACTIVITY_NAME) :: 블루투스 권한 :: poweredOn :: 블루투스 활성 상태", data: ["\(self.CALL_PROC)"])
if self.CALL_PROC == C_Bluetooth_Advertising_Scan_Module.CALL_SCAN { // [스캔 수행]
// ---------------------------------------------------------
// MARK: [블루투스 실시간 스캔 시작] : centralManager : didDiscover : 함수에서 확인
// ---------------------------------------------------------
if self.scanBluetoothCentralManager != nil {
//self.scanBluetoothCentralManager?.scanForPeripherals(withServices: nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey:false])
self.scanBluetoothCentralManager?.stopScan() // [사전 블루투스 스캔 종료]
self.scanBluetoothCentralManager?.scanForPeripherals(withServices: nil, options: nil)
}
// MARK: [타이머 동작 : 특정 시간 이후 스캔 종료 처리]
DispatchQueue.main.asyncAfter(deadline: .now() + C_Bluetooth_Advertising_Scan_Module.BLE_SCAN_TIME_OUT) { // [스캔 종료 타임 아웃 지정]
if self.scanBluetoothCentralManager != nil {
S_Log._D_(description: "\(C_Bluetooth_Advertising_Scan_Module.ACTIVITY_NAME) :: poweredOn :: 블루투스 스캔 타임 아웃 완료", data: nil)
// [블루투스 스캔 종료]
self.scanBluetoothCentralManager?.stopScan()
// [블루투스 스캔 리스트 콜백 반환]
self.scanBluetoothOperationQueue.isSuspended = false // [블루투스 스캔 큐 해제]
}
}
}
else { // [신호 활성]
// ---------------------------------------------------------
// MARK: [블루투스 실시간 신호 활성 시작] : CBPeripheralManager : peripheralManagerDidUpdateState : 함수에서 확인
// ---------------------------------------------------------
C_Bluetooth_Advertising_Scan_Module.peripheralManager = CBPeripheralManager(delegate: self, queue: nil, options: nil)
}
// ---------------------------------------------------------
@unknown default:
S_Log._E_(description: "\(C_Bluetooth_Advertising_Scan_Module.ACTIVITY_NAME) :: 블루투스 권한 :: default :: 상태", data: ["\(self.CALL_PROC)"])
if self.CALL_PROC == C_Bluetooth_Advertising_Scan_Module.CALL_SCAN { // [스캔 수행]
self.scanBluetoothOperationQueue.isSuspended = false // [블루투스 스캔 큐 해제]
}
else { // [신호 활성]
self.advertisingBluetoothOperationQueue.isSuspended = false // [블루투스 신호 활성 큐 해제]
}
}
// ---------------------------------------------------------
}
// -----------------------------------------------------------------------------------------
// MARK: - [블루투스 장치를 찾았을 때 실행되는 이벤트] : CBCentralManagerDelegate : didDiscover
// -----------------------------------------------------------------------------------------
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber){
if peripheral.name == self.SEARCH_BLUETOOTH_NAME{
print("🔵 :: didDiscover :: 실시간 블루투스 스캔 정보 >> NAME - \(String(peripheral.name ?? "")) / UUID - \(String(peripheral.identifier.uuidString)) / RSSI - \(String(RSSI.intValue))")
}
else {
print("🔴 :: didDiscover :: 실시간 블루투스 스캔 정보 >> NAME - \(String(peripheral.name ?? "")) / UUID - \(String(peripheral.identifier.uuidString)) / RSSI - \(String(RSSI.intValue))")
}
// -------------------------------------------------
// [특정 UUID 를 찾은 경우 : 샘플 코드]
// -------------------------------------------------
// if String(peripheral.identifier.uuidString).contains("DB809C5F-9598-16BA-563B-E329FDFA17A3") == true { }
// -------------------------------------------------
// MARK: [스캔 리턴 리스트에 저장]
// -------------------------------------------------
var bleScanDic : Dictionary<String, String> = [String : String]()
bleScanDic["NAME"] = "\(String(peripheral.name ?? ""))"
bleScanDic["UUID"] = "\(String(peripheral.identifier.uuidString))"
//bleScanDic["RSSI"] = "\(String(RSSI.intValue))"
// MARK: [android 에서 블루투스 advertising 시 NAME 정보는 표시 됨]
if self.scanBluetoothList != nil
&& self.scanBluetoothList.contains(bleScanDic) == false {
self.scanBluetoothList.append(bleScanDic) // [배열에 추가]
}
}
// -----------------------------------------------------------------------------------------
// MARK: - [실시간 블루투스 신호 활성 수행 부분] : CBPeripheralManager : peripheralManagerDidUpdateState
// -----------------------------------------------------------------------------------------
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
// [블루투스 활성 상태 체크 실시 및 블루투스 신호 활성 수행]
if peripheral.state == .poweredOn {
// MARK: [신호 활성 수행] : [android 에서 블루투스 LE 스캔 시 UUID 는 표시 되지 않고 NAME 정보는 표시 됨]
peripheral.startAdvertising([
CBAdvertisementDataLocalNameKey: self.advertisingName,
CBAdvertisementDataServiceUUIDsKey: [CBUUID(string: self.advertisingUuid)],
])
S_Log._W_(description: "\(C_Bluetooth_Advertising_Scan_Module.ACTIVITY_NAME) :: peripheralManagerDidUpdateState :: startAdvertising :: Success", data: [
"NAME :: \(self.advertisingName)",
"UUID :: \(self.advertisingUuid)"
])
self.advertisingResult = true // MARK: [리턴 값 변경]
self.advertisingBluetoothOperationQueue.isSuspended = false // [큐 해제]
}
else if peripheral.state == .poweredOff {
S_Log._E_(description: "\(C_Bluetooth_Advertising_Scan_Module.ACTIVITY_NAME) :: peripheralManagerDidUpdateState :: poweredOff", data: nil)
if C_Bluetooth_Advertising_Scan_Module.peripheralManager != nil {
C_Bluetooth_Advertising_Scan_Module.peripheralManager.stopAdvertising()
}
self.advertisingBluetoothOperationQueue.isSuspended = false // [큐 해제]
}
}
} // [클래스 종료]
반응형
'투케이2K 유틸파일' 카테고리의 다른 글
Comments