The XestiMonitors framework provides more thansixtyfully-functional monitor classes right out of the box that make it easy for your app to detect and respond to many common system-generated events.
Among other things, you can think of XestiMonitors as a better way to manage
the most common notifications (primarily on iOS and tvOS). At present,
XestiMonitors provides “wrappers” around nearly allUIKit
notifications (see
UIKit Monitors) and manyFoundation
notifications (see
Foundation Monitors).
XestiMonitors also provides convenient “wrappers” around several frameworks and programming interfaces to make them easier for your app to use:
- It wraps the Core Location framework to make it easier for your app to make easier for your app to determine the device’s geographic location, altitude, or orientation; or its position relative to a nearby iBeacon. See Core Location Monitorsfor details.
- It wraps the Core Motion framework to make it easier for your app to obtain both raw and processed motion measurements from the device. See Core Motion Monitorsfor details.
- It wraps the
SCNetworkReachability
programming interface to make it super easy for your app to determine the reachability of a target host. See Other Monitorsfor details.
Additional monitors targeting more parts of all four platforms will be rolled out in future releases of XestiMonitors!
Finally, XestiMonitors isextensible—you can easily create your owncustom monitors. SeeCustom Monitorsfor details.
Fullreference documentationis available courtesy ofJazzy.
- iOS 9.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+
- Xcode 9.0+
- Swift 4.0+
CocoaPodsis a dependency manager for Cocoa projects. You can install it with the following command:
$ gem install cocoapods
To integrate XestiMonitors into your Xcode project using CocoaPods, specify it
in yourPodfile
:
source 'https://github /CocoaPods/Specs.git'
platform:ios, '9.0'
use_frameworks!
target '<Your Target Name>' do
pod 'XestiMonitors'
end
Then, run the following command:
$ pod install
Carthageis a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
You can install Carthage withHomebrewusing the following commands:
$ brew update
$ brew install carthage
To integrate XestiMonitors into your Xcode project using Carthage, specify it
in yourCartfile
:
github "eBardX/XestiMonitors"
Then, run the following command:
$ carthage update
Finally, drag the builtXestiMonitors.framework
into your Xcode project.
TheSwift Package Manageris a tool for automating the distribution of Swift code and is integrated into the swift compiler. It is in early development, but XestiMonitors does support its use on supported platforms.
Once you have your Swift package set up, adding XestiMonitors as a dependency
is as easy as adding it to thedependencies
value of yourPackage.swift
.
dependencies: [
.Package(url: "https://github /eBardX/XestiMonitors.git" )
]
All monitor classes conform to theMonitorprotocol, thus enabling you to create arrays of monitors that can be started or stopped uniformly—fewer lines of code!
For example, in a view controller, you can lazily instantiate several monitors and, in addition, lazily instantiate an array variable containing these monitors:
import XestiMonitors
lazy var keyboardMonitor = KeyboardMonitor { [unowned self] in
// do something…
}
lazy var memoryMonitor = MemoryMonitor { [unowned self] in
// do something…
}
lazy var orientationMonitor = OrientationMonitor { [unowned self] in
// do something…
}
lazy var monitors: [Monitor] = [keyboardMonitor,
memoryMonitor,
orientationMonitor]
Then, in theviewWillAppear(_:)
andviewWillDisappear(_:)
methods, you can
simply start or stop all these monitors with a single line of code:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
monitors.forEach { $0.startMonitoring() }
}
override func viewWillDisappear(_ animated: Bool) {
monitors.forEach { $0.stopMonitoring() }
super.viewWillDisappear(animated)
}
Easy peasy!
XestiMonitors provides seven monitor classes wrapping the Core Location framework that you can use to determine the device’s geographic location, altitude, or orientation; or its position relative to a nearby iBeacon:
- BeaconRangingMonitorto monitor a region for changes to the ranges (i.e.,the relative proximity) to the Bluetooth low-energy beacons within.(iOS)
- HeadingMonitorto monitor the device for changes to its current heading.(iOS)
- LocationAuthorizationMonitorto monitor the app for updates to its authorization to use location services.(iOS, macOS, tvOS, watchOS)
- RegionMonitorto monitor a region for changes to its state (which indicate boundary transitions).(iOS, macOS)
- SignificantLocationMonitorto monitor the device forsignificantchanges to its current location.(iOS, macOS)
- StandardLocationMonitorto monitor the device for changes to its current location.(iOS, macOS, tvOS, watchOS)
- VisitMonitorto monitor for locations that the user stops at for a “noteworthy” amount of time.(iOS)
XestiMonitors provides seven monitor classes wrapping the Core Motion framework that you can use to obtain raw and processed motion measurements from the device:
- AccelerometerMonitorto monitor the device’s accelerometer for periodic raw measurements of the acceleration along the three spatial axes.(iOS, watchOS)
- AltimeterMonitorto monitor the device for changes in relative altitude.(iOS, watchOS)
- DeviceMotionMonitorto monitor the device’s accelerometer, gyroscope, and magnetometer for periodic raw measurements which are processed into device motion measurements.(iOS, watchOS)
- GyroscopeMonitorto monitor the device’s gyroscope for periodic raw measurements of the rotation rate around the three spatial axes. (iOS, watchOS)
- MagnetometerMonitorto monitor the device’s magnetometer for periodic raw measurements of the magnetic field around the three spatial axes.(iOS, watchOS)
- MotionActivityMonitorto monitor the device for live and historic motion data.(iOS, watchOS)
- PedometerMonitorto monitor the device for live and historic walking data.(iOS, watchOS)
XestiMonitors provides seventeen monitors wrapping
Foundation
notifications:
- BundleClassLoadMonitorto monitor a bundle for dynamic loads of classes.(iOS, macOS, tvOS, watchOS)
- BundleResourceRequestMonitorto monitor the system to detect if the amount of available disk space for on-demand resources is getting low.(iOS, tvOS, watchOS)
- CalendarDayMonitorto monitor the system for changes to the calendar day.(iOS, macOS, tvOS, watchOS)
- CurrentLocaleMonitorto monitor the system for changes to the user’s locale.(iOS, macOS, tvOS, watchOS)
- ExtensionHostMonitorto monitor an extension context for changes to the runtime state of the extension’s host app. (iOS, tvOS, watchOS)
- [HTTPCookiesStorageMonitor][http_cookies_storage_monitor] instance to monitor an HTTP cookie storage object for changes to its acceptance policy or to its cookies.(iOS, macOS, tvOS, watchOS)
- MetadataQueryMonitorto monitor a metadata query for changes to its results.(iOS, macOS, tvOS, watchOS)
- PortMonitorto monitor a port for changes to its validity. (iOS, macOS, tvOS, watchOS)
- ProcessInfoPowerStateMonitorto monitor the device for changes to its power state (Low Power Mode is enabled or disabled).(iOS, tvOS, watchOS)
- ProcessInfoThermalStateMonitorto monitor the system for changes to the thermal state. (iOS, macOS, tvOS, watchOS)
- SystemClockMonitorto monitor the system for changes to the clock.(iOS, macOS, tvOS, watchOS)
- SystemTimeZoneMonitorto monitor the system for changes to the currently used time zone.(iOS, macOS, tvOS, watchOS)
- UbiquitousKeyValueStoreMonitorto monitor the iCloud ( “ubiquitous” ) key-value store for changes due to incoming data pushed from iCloud.(iOS, macOS, tvOS)
- UbiquityIdentityMonitorto monitor the system for changes to the iCloud (” ubiquity” ) identity.(iOS, macOS, tvOS, watchOS)
- UndoManagerMonitorto monitor an undo manager for changes to its recording of operations.(iOS, macOS, tvOS, watchOS)
- URLCredentialStorageMonitorto monitor the shared URL credential storage object for changes to its stored credentials. (iOS, macOS, tvOS, watchOS)
- UserDefaultsMonitorto monitor a user defaults object for changes to its data.(iOS, macOS, tvOS, watchOS)
XestiMonitors provides numerous monitors wrapping
UIKit
notifications.
XestiMonitors provides three monitor classes that you can use to observe accessibility events generated by the system:
- AccessibilityAnnouncementMonitorto monitor the system for accessibility announcements that VoiceOver has finished outputting.(iOS, tvOS)
- AccessibilityElementMonitorto monitor the system for changes to element focus by an assistive technology.(iOS, tvOS)
- AccessibilityStatusMonitorto monitor the system for changes to the status of various accessibility settings. (iOS, tvOS)
XestiMonitors provides seven monitor classes that you can use to observe common events generated by the system about the app:
- ApplicationStateMonitorto monitor the app for changes to its runtime state.(iOS, tvOS)
- BackgroundRefreshMonitorto monitor the app for changes to its status for downloading content in the background.(iOS)
- MemoryMonitorto monitor the app for memory warnings from the operating system.(iOS, tvOS)
- ProtectedDataMonitorto monitor the app for changes to the accessibility of protected files.(iOS, tvOS)
- ScreenshotMonitorto monitor the app for screenshots. (iOS, tvOS)
- StatusBarMonitorto monitor the app for changes to the orientation of its user interface or to the frame of the status bar.(iOS)
- TimeMonitorto monitor the app for significant changes in time.(iOS, tvOS)
XestiMonitors provides three monitor classes that you can use to detect changes in the characteristics of the device:
- BatteryMonitorto monitor the device for changes to the charge state and charge level of its battery.(iOS)
- OrientationMonitorto monitor the device for changes to its physical orientation.(iOS)
- ProximityMonitorto monitor the device for changes to the state of its proximity sensor.(iOS)
XestiMonitors provides four monitor classes that you can use to detect changes in the properties associated with a screen:
- ScreenBrightnessMonitorto monitor a screen for changes to its brightness level.(iOS, tvOS)
- ScreenCapturedMonitorto monitor a screen for changes to its captured status.(iOS, tvOS)
- ScreenConnectionMonitorto monitor the device for screen connections and disconnections.(iOS, tvOS)
- ScreenModeMonitorto monitor a screen for changes to its current mode.(iOS, tvOS)
XestiMonitors provides four monitor classes that you can use to detect changes in text input mode and content:
- TextFieldTextMonitorto monitor a text field for changes to its text.(iOS, tvOS)
- TextInputModeMonitorto monitor the responder chain for changes to the current input mode.(iOS, tvOS)
- TextStorageMonitorto monitor a text storage for the processing of edits to its contents.(iOS, tvOS)
- TextViewTextMonitorto monitor a text view for changes to its text.(iOS, tvOS)
In addition, XestiMonitors provides nine otherUIKit
monitors:
- ContentSizeCategoryMonitorto monitor the app for changes to its preferred content size category.(iOS, tvOS)
- DocumentStateMonitorto monitor a document for changes to its state.(iOS)
- FocusMonitorto monitor the app for changes to the current focus in the view hierarchy.(iOS, tvOS)
- KeyboardMonitorto monitor the keyboard for changes to its visibility or to its frame.(iOS)
- MenuControllerMonitorto monitor the menu controller for changes to the visibility of the editing menu or to the frame of the editing menu.(iOS)
- PasteboardMonitorto monitor a pasteboard for changes to its contents or for its removal from the app.(iOS)
- TableViewSelectionMonitorto monitor a table view for changes to its selected row.(iOS, tvOS)
- ViewControllerShowDetailTargetMonitor to monitor the app for changes to a split view controller’s display mode in the view hierarchy.(iOS, tvOS)
- WindowMonitorto monitor a window for changes to its visibility or key status.(iOS, tvOS)
KeyboardMonitoris especially handy in removing lots of boilerplate code from your app. This is how keyboard monitoring is typically handled in a custom view controller:
func keyboardWillHide(_ notification: Notification) {
let userInfo = notification.userInfo
var animationDuration: TimeInterval = 0
if let value = (userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue {
animationDuration = value
}
constraint.constant = 0
UIView.animate(withDuration: animationDuration) {
self.view.layoutIfNeeded()
}
}
func keyboardWillShow(_ notification: Notification) {
let userInfo = notification.userInfo
var animationDuration: TimeInterval = 0
if let value = (userInfo?[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue {
animationDuration = value
}
var frameEnd = CGRect.zero
if let value = (userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
frameEnd = value
}
constraint.constant = frameEnd.height
UIView.animate(withDuration: animationDuration) {
self.view.layoutIfNeeded()
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let nc = NotificationCenter.`default`
nc.addObserver(self, selector: #selector(keyboardWillHide(_:)),
name:.UIKeyboardWillHide, object: nil)
nc.addObserver(self, selector: #selector(keyboardWillShow(_:)),
name:.UIKeyboardWillShow, object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
NotificationCenter.`default`.removeObserver(self)
super.viewWillDisappear(animated)
}
And this is the XestiMonitors way usingKeyboardMonitor:
import XestiMonitors
lazy var keyboardMonitor = KeyboardMonitor { [unowned self] event in
guard let constraint = self?.constraint,
let view = self?.view else { return }
switch event {
case let.willHide(info):
constraint.constant = 0
UIView.animate(withDuration: info.animationDuration) {
view.layoutIfNeeded()
}
case let.willShow(info):
constraint.constant = info.frameEnd.height
UIView.animate(withDuration: info.animationDuration) {
view.layoutIfNeeded()
}
default:
break
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
keyboardMonitor.startMonitoring()
}
override func viewWillDisappear(_ animated: Bool) {
keyboardMonitor.stopMonitoring()
super.viewWillDisappear(animated)
}
What’s inyourwallet?
In addition, XestiMonitors provides two other monitors:
- FileSystemObjectMonitorto monitor a file-system object for changes.(iOS, macOS, tvOS, watchOS)
- NetworkReachabilityMonitorto monitor a network node name or address for changes to its reachability. (iOS, macOS, tvOS)
Best of all, the XestiMonitors framework provides several ways to create your own custom monitors quite easily.
You can create a new class, or extend an existing class, that conforms to the Monitorprotocol. You need only implement the startMonitoring()and stopMonitoring()methods, as well as the isMonitoringproperty:
import XestiMonitors
extension MegaHoobieWatcher: Monitor {
var isMonitoring: Bool { return watchingForHoobiesCount() > 0 }
func startMonitoring() -> Bool {
guard!isMonitoring else { return }
beginWatchingForHoobies()
}
func stopMonitoring() -> Bool {
guard isMonitoring else { return }
endWatchingForHoobies()
}
}
Note:The guard statements in bothstartMonitoring() andstopMonitoring()protect against starting or stopping the monitor if it is in the incorrect state. This is considered good coding practice.
Typically, you will want to create a subclass ofBaseMonitor.
The advantage of using this abstract base class is that the basic guard logic
is taken care of for you. Specifically, the
startMonitoring()method does not attempt to start the
monitor if it is already active, and thestopMonitoring()
method does not attempt to stop the monitor if it isnotactive. Instead of
directly implementing the required protocol methods and properties, you need
only override theconfigureMonitor()and
cleanupMonitor()methods of this base class. In fact, you
willnotbe able to override thestartMonitoring()and
stopMonitoring()methods or the
isMonitoringproperty—they are declaredfinal
in
BaseMonitor.
import XestiMonitors
class GigaHoobieMonitor: BaseMonitor {
let handler: (Float) -> Void
@objc let hoobie: GigaHoobie
private var observation: NSKeyValueObservation?
init(_ hoobie: GigaHoobie, handler: @escaping (Float) -> Void) {
self.handler = handler
self.hoobie = hoobie
}
override func configureMonitor() -> Bool {
super.configureMonitor()
observation = hoobie.observe(\.nefariousActivityLevel) { [unowned self] hoobie, _ in
self.handler(hoobie.nefariousActivityLevel) }
}
override func cleanupMonitor() -> Bool {
observation?.invalidate()
observation = nil
super.cleanupMonitor()
}
}
Note:Be sure to invoke the superclass implementations of both configureMonitor()and cleanupMonitor().
If your custom monitor determines events by observing notifications, you should
consider creating a subclass of
BaseNotificationMonitorinstead. In most cases you
need only override the
addNotificationObservers(_:)method. You can
also override the
removeNotificationObservers(_:)method if
you require extra cleanup when the notification observers are removed upon
stopping the monitor. Although this base class inherits from
BaseMonitor,you willnotbe able to override the
configureMonitor()and
cleanupMonitor()methods—they are declaredfinal
in
BaseNotificationMonitor.
import XestiMonitors
class TeraHoobieMonitor: BaseNotificationMonitor {
let handler: (Bool) -> Void
let hoobie: TeraHoobie
init(hoobie: TeraHoobie, queue: OperationQueue =.main,
handler: @escaping (Bool) -> Void) {
self.handler = handler
self.hoobie = hoobie
super.init(queue: queue)
}
override func addNotificationObservers() -> Bool {
super.addNotificationObservers()
observe(.teraHoobieDidChange) { [unowned self] _ in
self.handler(self.hoobie.value) }
}
}
Note:Be sure to invoke the superclass implementations of both addNotificationObservers(_:)and removeNotificationObservers(_:)in your overrides.
J. G. Pusey (ebardx@gmail)
XestiMonitors is available underthe MIT license.