Изменился ли уровень заряда батареи, изменивший эквивалент уведомления для kIOPSCurrentCapacityKey на macOS?
Я создаю приложение Swift, которое отслеживает процент заряда батареи, а также состояние зарядки батареи ноутбука Mac. На iOS есть batteryLevelDidChange
уведомление, которое отправляется при изменении процента заряда батареи устройства, а также batteryStateDidChange
уведомление, которое отправляется, когда устройство подключено, отключено и полностью заряжено.
Что такое macOS-эквивалент этих двух уведомлений в Swift или, более конкретно, для kIOPSCurrentCapacityKey
а также kIOPSIsChargingKey
? Я прочитал документацию по уведомлению и не видел никаких уведомлений ни для одного из них. Вот код, который у меня есть для получения текущего уровня заряда аккумулятора и состояния зарядки:
import Cocoa
import IOKit.ps
class MainViewController: NSViewController {
enum BatteryError: Error { case error }
func getMacBatteryPercent() {
do {
guard let snapshot = IOPSCopyPowerSourcesInfo()?.takeRetainedValue()
else { throw BatteryError.error }
guard let sources: NSArray = IOPSCopyPowerSourcesList(snapshot)?.takeRetainedValue()
else { throw BatteryError.error }
for powerSource in sources {
guard let info: NSDictionary = IOPSGetPowerSourceDescription(snapshot, ps as CFTypeRef)?.takeUnretainedValue()
else { throw BatteryError.error }
if let name = info[kIOPSNameKey] as? String,
let state = info[kIOPSIsChargingKey] as? Bool,
let capacity = info[kIOPSCurrentCapacityKey] as? Int,
let max = info[kIOPSMaxCapacityKey] as? Int {
print("\(name): \(capacity) of \(max), \(state)")
}
}
} catch {
print("Unable to get mac battery percent.")
}
}
override func viewDidLoad() {
super.viewDidLoad()
getMacBatteryPercent()
}
}
2 ответа
(Я отвечаю на этот вопрос почти трехлетней давности, поскольку это третий результат, который появляется в поиске Google «быстрое уведомление iokit».)
Функции, которые вы ищете, - это и
IOPSCreateLimitedPowerNotification
.
Простейшее использование
IOPSNotificationCreateRunLoopSource
:
import IOKit
let loop = IOPSNotificationCreateRunLoopSource({ _ in
// Perform usual battery status fetching
}, nil).takeRetainedValue() as CFRunLoopSource
CFRunLoopAddSource(CFRunLoopGetCurrent(), loop, .defaultMode)
Обратите внимание, что второй параметр
context
передается как единственный параметр в функции обратного вызова, который можно использовать для передачи экземпляра в качестве указателя на закрытие, поскольку функции C не захватывают контекст. (См. Ссылку ниже для фактической реализации.)
Вот мой код, который преобразует API в стиле C в более удобный для Swift, используя шаблон наблюдателя: (не знаю, какое преимущество в производительности он будет иметь для удаления циклов выполнения)
import Cocoa
import IOKit
// Swift doesn't support nested protocol(?!)
protocol BatteryInfoObserverProtocol: AnyObject {
func batteryInfo(didChange info: BatteryInfo)
}
class BatteryInfo {
typealias ObserverProtocol = BatteryInfoObserverProtocol
struct Observation {
weak var observer: ObserverProtocol?
}
static let shared = BatteryInfo()
private init() {}
private var notificationSource: CFRunLoopSource?
var observers = [ObjectIdentifier: Observation]()
private func startNotificationSource() {
if notificationSource != nil {
stopNotificationSource()
}
notificationSource = IOPSNotificationCreateRunLoopSource({ _ in
BatteryInfo.shared.observers.forEach { (_, value) in
value.observer?.batteryInfo(didChange: BatteryInfo.shared)
}
}, nil).takeRetainedValue() as CFRunLoopSource
CFRunLoopAddSource(CFRunLoopGetCurrent(), notificationSource, .defaultMode)
}
private func stopNotificationSource() {
guard let loop = notificationSource else { return }
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), loop, .defaultMode)
}
func addObserver(_ observer: ObserverProtocol) {
if observers.count == 0 {
startNotificationSource()
}
observers[ObjectIdentifier(observer)] = Observation(observer: observer)
}
func removeObserver(_ observer: ObserverProtocol) {
observers.removeValue(forKey: ObjectIdentifier(observer))
if observers.count == 0 {
stopNotificationSource()
}
}
// Functions for retrieving different properties in the battery description...
}
Применение:
class MyBatteryObserver: BatteryInfo.ObserverProtocol {
init() {
BatteryInfo.shared.addObserver(self)
}
deinit {
BatteryInfo.shared.removeObserver(self)
}
func batteryInfo(didChange info: BatteryInfo) {
print("Changed")
}
}
Кредиты на этот пост и ответ Коэна .
Я бы использовал эту ссылку, чтобы получить процент (выглядит чище) Получить состояние батареи моего MacBook с Swift
И чтобы найти изменения в состоянии, используйте timer
повторное объявление состояния батареи каждые 5 секунд, а затем установка его в качестве новой переменной var OldBattery:Int
повторно объявите это и установите это как NewBattery
затем напишите этот код:
if (OldBattery =! NewBattery) {
print("battery changed!")
// write the function you want to happen here
}