The XestiMonitors framework provides more than sixty fully-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 all UIKit notifications (see
UIKit Monitors) and many Foundation 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 Monitors for 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 Monitors for 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 Monitors for details.
Additional monitors targeting more parts of all four platforms will be rolled
out in future releases of XestiMonitors!
Finally, XestiMonitors is extensible—you can easily create your own custom
monitors. See Custom Monitors for details.
The Swift Package Manager is 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 the dependencies value of your Package.swift.
All monitor classes conform to the Monitor protocol, 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 the viewWillAppear(_:) and viewWillDisappear(_:) methods, you can
simply start or stop all these monitors with a single line of code:
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:
BeaconRangingMonitor to monitor a region for
changes to the ranges (i.e., the relative proximity) to the Bluetooth
low-energy beacons within. (iOS)
HeadingMonitor to monitor the device for changes to its
current heading. (iOS)
LocationAuthorizationMonitor to monitor the
app for updates to its authorization to use location services. (iOS, macOS,
tvOS, watchOS)
RegionMonitor to monitor a region for changes to its state
(which indicate boundary transitions). (iOS, macOS)
SignificantLocationMonitor to monitor the
device for significant changes to its current location. (iOS, macOS)
StandardLocationMonitor to monitor the device
for changes to its current location. (iOS, macOS, tvOS, watchOS)
VisitMonitor to 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:
AccelerometerMonitor to monitor the device’s
accelerometer for periodic raw measurements of the acceleration along the
three spatial axes. (iOS, watchOS)
AltimeterMonitor to monitor the device for changes in
relative altitude. (iOS, watchOS)
DeviceMotionMonitor to monitor the device’s
accelerometer, gyroscope, and magnetometer for periodic raw measurements
which are processed into device motion measurements. (iOS, watchOS)
GyroscopeMonitor to monitor the device’s gyroscope for
periodic raw measurements of the rotation rate around the three spatial axes.
(iOS, watchOS)
MagnetometerMonitor to monitor the device’s
magnetometer for periodic raw measurements of the magnetic field around the
three spatial axes. (iOS, watchOS)
MotionActivityMonitor to monitor the device for
live and historic motion data. (iOS, watchOS)
PedometerMonitor to monitor the device for live and
historic walking data. (iOS, watchOS)
BundleClassLoadMonitor to monitor a bundle for
dynamic loads of classes. (iOS, macOS, tvOS, watchOS)
BundleResourceRequestMonitor to monitor
the system to detect if the amount of available disk space for on-demand
resources is getting low. (iOS, tvOS, watchOS)
CalendarDayMonitor to monitor the system for changes
to the calendar day. (iOS, macOS, tvOS, watchOS)
CurrentLocaleMonitor to monitor the system for
changes to the user’s locale. (iOS, macOS, tvOS, watchOS)
ExtensionHostMonitor to 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)
MetadataQueryMonitor to monitor a metadata query
for changes to its results. (iOS, macOS, tvOS, watchOS)
PortMonitor to monitor a port for changes to its validity.
(iOS, macOS, tvOS, watchOS)
ProcessInfoPowerStateMonitor to monitor
the device for changes to its power state (Low Power Mode is enabled or
disabled). (iOS, tvOS, watchOS)
SystemClockMonitor to monitor the system for changes
to the clock. (iOS, macOS, tvOS, watchOS)
SystemTimeZoneMonitor to monitor the system for
changes to the currently used time zone. (iOS, macOS, tvOS, watchOS)
UbiquitousKeyValueStoreMonitor to
monitor the iCloud (“ubiquitous”) key-value store for changes due to incoming
data pushed from iCloud. (iOS, macOS, tvOS)
UbiquityIdentityMonitor to monitor the system
for changes to the iCloud (”ubiquity”) identity. (iOS, macOS, tvOS, watchOS)
UndoManagerMonitor to monitor an undo manager for
changes to its recording of operations. (iOS, macOS, tvOS, watchOS)
URLCredentialStorageMonitor to monitor the
shared URL credential storage object for changes to its stored credentials.
(iOS, macOS, tvOS, watchOS)
UserDefaultsMonitor to monitor a user defaults
object for changes to its data. (iOS, macOS, tvOS, watchOS)
WindowMonitor to monitor a window for changes to its
visibility or key status. (iOS, tvOS)
KeyboardMonitor is 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)
}
Best of all, the XestiMonitors framework provides several ways to create your
own custom monitors quite easily.
Implementing the Monitor Protocol
You can create a new class, or extend an existing class, that conforms to the
Monitor protocol. You need only implement the
startMonitoring() and
stopMonitoring() methods, as well as the
isMonitoring property:
Note: The guard statements in both startMonitoring()
and stopMonitoring() protect against starting or stopping
the monitor if it is in the incorrect state. This is considered good coding
practice.
Subclassing the BaseMonitor Class
Typically, you will want to create a subclass of BaseMonitor.
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 the stopMonitoring()
method does not attempt to stop the monitor if it is not active. Instead of
directly implementing the required protocol methods and properties, you need
only override the configureMonitor() and
cleanupMonitor() methods of this base class. In fact, you
will not be able to override the startMonitoring() and
stopMonitoring() methods or the
isMonitoring property—they are declared final in
BaseMonitor.
XestiMonitors
Overview
The XestiMonitors framework provides more than sixty fully-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 all
UIKit
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:
SCNetworkReachability
programming interface to make it super easy for your app to determine the reachability of a target host. See Other Monitors for details.Additional monitors targeting more parts of all four platforms will be rolled out in future releases of XestiMonitors!
Finally, XestiMonitors is extensible—you can easily create your own custom monitors. See Custom Monitors for details.
Reference Documentation
Full reference documentation is available courtesy of Jazzy.
Requirements
Installation
CocoaPods
CocoaPods is a dependency manager for Cocoa projects. You can install it with the following command:
To integrate XestiMonitors into your Xcode project using CocoaPods, specify it in your
Podfile
:Then, run the following command:
Carthage
Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
You can install Carthage with Homebrew using the following commands:
To integrate XestiMonitors into your Xcode project using Carthage, specify it in your
Cartfile
:Then, run the following command:
Finally, drag the built
XestiMonitors.framework
into your Xcode project.Swift Package Manager
The Swift Package Manager is 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 the
dependencies
value of yourPackage.swift
.Usage
All monitor classes conform to the Monitor protocol, 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:
Then, in the
viewWillAppear(_:)
andviewWillDisappear(_:)
methods, you can simply start or stop all these monitors with a single line of code:Easy peasy!
Core Location Monitors
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:
Core Motion Monitors
XestiMonitors provides seven monitor classes wrapping the Core Motion framework that you can use to obtain raw and processed motion measurements from the device:
Foundation Monitors
XestiMonitors provides seventeen monitors wrapping
Foundation
notifications:UIKit Monitors
XestiMonitors provides numerous monitors wrapping
UIKit
notifications.Accessibility Monitors
XestiMonitors provides three monitor classes that you can use to observe accessibility events generated by the system:
Application Monitors
XestiMonitors provides seven monitor classes that you can use to observe common events generated by the system about the app:
Device Monitors
XestiMonitors provides three monitor classes that you can use to detect changes in the characteristics of the device:
Screen Monitors
XestiMonitors provides four monitor classes that you can use to detect changes in the properties associated with a screen:
Text Monitors
XestiMonitors provides four monitor classes that you can use to detect changes in text input mode and content:
Other UIKit Monitors
In addition, XestiMonitors provides nine other
UIKit
monitors:KeyboardMonitor is especially handy in removing lots of boilerplate code from your app. This is how keyboard monitoring is typically handled in a custom view controller:
And this is the XestiMonitors way using KeyboardMonitor:
What’s in your wallet?
Other Monitors
In addition, XestiMonitors provides two other monitors:
Custom Monitors
Best of all, the XestiMonitors framework provides several ways to create your own custom monitors quite easily.
Implementing the Monitor Protocol
You can create a new class, or extend an existing class, that conforms to the Monitor protocol. You need only implement the startMonitoring() and stopMonitoring() methods, as well as the isMonitoring property:
Note: The guard statements in both startMonitoring() and stopMonitoring() protect against starting or stopping the monitor if it is in the incorrect state. This is considered good coding practice.
Subclassing the BaseMonitor Class
Typically, you will want to create a subclass of BaseMonitor. 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 the stopMonitoring() method does not attempt to stop the monitor if it is not active. Instead of directly implementing the required protocol methods and properties, you need only override the configureMonitor() and cleanupMonitor() methods of this base class. In fact, you will not be able to override the startMonitoring() and stopMonitoring() methods or the isMonitoring property—they are declared
final
in BaseMonitor.Note: Be sure to invoke the superclass implementations of both configureMonitor() and cleanupMonitor().
Subclassing the BaseNotificationMonitor Class
If your custom monitor determines events by observing notifications, you should consider creating a subclass of BaseNotificationMonitor instead. 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 will not be able to override the configureMonitor() and cleanupMonitor() methods—they are declared
final
in BaseNotificationMonitor.Note: Be sure to invoke the superclass implementations of both addNotificationObservers(_:) and removeNotificationObservers(_:) in your overrides.
Credits
J. G. Pusey (ebardx@gmail.com)
License
XestiMonitors is available under the MIT license.