Как указывали другие, следует избегать использования глобальных переменных. Если вы сделаете его свойством какого-то класса, это позволит избежать проблемы.
FWIW, проблема, строго говоря, не в использовании глобального, а скорее в конкретном имени переменной, которое вы выбрали. Например, измените имя этой глобальной переменной, она компилируется нормально:
import UIKit
import CoreBluetooth
var manager: CBCentralManager!
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
manager = CBCentralManager(delegate: self, queue: nil)
}
}
extension ViewController: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
print("BLE powered on")
} else {
print("Something wrong with BLE")
}
}
}
Проблема в том, что у компилятора есть проблемы с разрешением этого глобального символа, centralManager
, при наличии нескольких методов протокола то же имя (например, centralManager(_:didConnect:)
, centralManager(_:didDisconnectPeripheral:error:)
, и т. д. c.). Можно было бы ожидать, что компилятор сможет устранить неоднозначность между глобальными методами и методами протокола с учетом контекста, но это не так. (Эту проблему можно также выявить, определив глобальный контроллер с именем tableView
в представлении, который соответствует UITableViewDataSource
.)
В итоге техническая проблема заключается в выборе имени глобальной переменной. Но правильный ответ - это вообще не должно быть глобальным.
Как только у вас возникнут непосредственные проблемы, вы можете пересмотреть, стоит ли вообще помещать этот код Bluetooth в контроллер представления. Конечно, в простых демонстрационных приложениях мы часто будем использовать подобные ярлыки, но по мере роста вашего приложения контроллер представления станет громоздким. Кроме того, в конечном итоге вы захотите добавить функции Bluetooth к другим контроллерам представления, и вы собираетесь дублировать код.
Вместо этого создайте свой собственный класс менеджера, абстрагируя детали CBCentralManager
и CBPeripheral
вдали от контроллеров просмотра. Затем вы можете предоставить своим контроллерам представления красивый простой интерфейс для взаимодействия с менеджером.
Например, у вас может быть что-то вроде:
class BluetoothManager: NSObject {
var isAvailableHandler: ((Bool) -> Void)?
private var manager: CBCentralManager? = nil
override init() {
super.init()
}
func start() {
guard manager == nil else { return }
manager = CBCentralManager(delegate: self, queue: nil)
}
}
extension BluetoothManager: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
isAvailableHandler?(central.state == .poweredOn)
}
}
И тогда вы можете делать такие вещи, как :
class ViewController: UIViewController {
private let deviceManager = BluetoothManager()
override func viewDidLoad() {
super.viewDidLoad()
deviceManager.isAvailableHandler = { [weak self] isAvailable in
print(isAvailable ? "Powered on" : "Not available")
}
deviceManager.start()
}
}
Теперь в этом примере я обрабатываю только centralManagerDidUpdateState
, но вы бы добавили дополнительные замыкания или свой собственный протокол для обработки любых событий, которые вы хотите предоставить своим контроллерам представления. Но, надеюсь, это иллюстрирует идею.