Latest 2.9.0
Homepage https://github.com/eBardX/XestiMonitors
License MIT
Platforms ios 9.0, osx 10.10, tvos 9.0, watchos 2.0, requires ARC
Authors

Swift 4.x
License
Platform

Build Status
Code Coverage
Documented

CocoaPods
Carthage
Swift Package Manager

Overview

The XestiMonitors framework provides more than three dozen 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 most UIKit notifications (see
UIKit Monitors) and some 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.

Reference Documentation

Full reference documentation is available courtesy of Jazzy.

Requirements

  • iOS 9.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+
  • Xcode 9.0+
  • Swift 4.0+

Installation

CocoaPods

CocoaPods is 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 your Podfile:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
use_frameworks!

target '<Your Target Name>' do
    pod 'XestiMonitors'
end

Then, run the following command:

$ pod install

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 command:

$ brew update
$ brew install carthage

To integrate XestiMonitors into your Xcode project using Carthage, specify it
in your Cartfile:

github "eBardX/XestiMonitors"

Run carthage update to build the framework and 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 your Package.swift.

dependencies: [
    .Package(url: "https://github.com/eBardX/XestiMonitors.git")
]

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:

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:

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!

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:

  • 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)

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:

  • 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)

Foundation Monitors

XestiMonitors provides three monitors wrapping Foundation
notifications:

  • MetadataQueryMonitor to monitor a metadata query
    for changes to its results. (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)

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:

  • ApplicationStateMonitor to monitor the app for
    changes to its runtime state. (iOS, tvOS)
  • BackgroundRefreshMonitor to monitor the app for
    changes to its status for downloading content in the background. (iOS)
  • MemoryMonitor to monitor the app for memory warnings from
    the operating system. (iOS, tvOS)
  • ProtectedDataMonitor to monitor the app for changes
    to the accessibility of protected files. (iOS, tvOS)
  • ScreenshotMonitor to monitor the app for screenshots.
    (iOS, tvOS)
  • StatusBarMonitor to monitor the app for changes to the
    orientation of its user interface or to the frame of the status bar. (iOS)
  • TimeMonitor to monitor the app for significant changes in
    time. (iOS, tvOS)

Device Monitors

XestiMonitors provides three monitor classes that you can use to detect changes
in the characteristics of the device:

  • BatteryMonitor to monitor the device for changes to the
    charge state and charge level of its battery. (iOS)
  • OrientationMonitor to monitor the device for changes
    to its physical orientation. (iOS)
  • ProximityMonitor to monitor the device for changes to
    the state of its proximity sensor. (iOS)

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:

  • ContentSizeCategoryMonitor to monitor the
    app for changes to its preferred content size category. (iOS, tvOS)
  • DocumentStateMonitor to monitor a document for
    changes to its state. (iOS)
  • FocusMonitor to monitor the app for changes to the current
    focus in the view hierarchy. (iOS, tvOS)
  • KeyboardMonitor to monitor the keyboard for changes to
    its visibility or to its frame. (iOS)
  • MenuControllerMonitor to monitor the menu
    controller for changes to the visibility of the editing menu or to the frame
    of the editing menu. (iOS)
  • PasteboardMonitor to monitor a pasteboard for changes
    to its contents or for its removal from the app. (iOS)
  • TableViewSelectionMonitor to 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)
  • 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)
}

And this is the XestiMonitors way using KeyboardMonitor:

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 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:

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 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.

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().

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.

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.

Credits

J. G. Pusey ([email protected])

License

XestiMonitors is available under the MIT license.

Latest podspec

{
    "name": "XestiMonitors",
    "version": "2.9.0",
    "swift_version": "4.0",
    "authors": {
        "J. G. Pusey": "[email protected]"
    },
    "license": {
        "type": "MIT",
        "file": "LICENSE.md"
    },
    "homepage": "https://github.com/eBardX/XestiMonitors",
    "source": {
        "git": "https://github.com/eBardX/XestiMonitors.git",
        "tag": "v2.9.0"
    },
    "summary": "An extensible monitoring framework written in Swift.",
    "documentation_url": "https://ebardx.github.io/XestiMonitors/",
    "platforms": {
        "ios": "9.0",
        "osx": "10.10",
        "tvos": "9.0",
        "watchos": "2.0"
    },
    "requires_arc": true,
    "ios": {
        "frameworks": [
            "CoreLocation",
            "CoreMotion",
            "Foundation",
            "SystemConfiguration",
            "UIKit"
        ]
    },
    "osx": {
        "frameworks": [
            "CoreLocation",
            "Foundation",
            "SystemConfiguration"
        ]
    },
    "tvos": {
        "frameworks": [
            "CoreLocation",
            "Foundation",
            "SystemConfiguration",
            "UIKit"
        ]
    },
    "watchos": {
        "frameworks": [
            "CoreLocation",
            "CoreMotion",
            "Foundation"
        ]
    },
    "default_subspecs": "Core",
    "subspecs": [
        {
            "name": "Core",
            "source_files": "Sources/**/*.swift"
        }
    ]
}

Pin It on Pinterest

Share This