• Stars
    star
    708
  • Rank 61,414 (Top 2 %)
  • Language
    Objective-C
  • License
    Apache License 2.0
  • Created over 8 years ago
  • Updated almost 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Modern thread-safe and type-safe key-value observing for Swift and Objective-C

Now Archived and Forked

PMKVObserver will not be maintained in this repository going forward. Please use, create issues on, and make PRs to the fork of PMKVObserver located here.

PMKVObserver

Version Platforms Languages License Carthage compatible CocoaPods

PMKVObserver provides a safe block-based wrapper around Key-Value Observing, with APIs for both Obj-C and Swift. Features include:

  • Thread-safety. Observers can be registered on a different thread than KVO notifications are sent on, and can be cancelled on yet another thread. An observer can even be cancelled from two threads simultaneously.
  • Automatic unregistering when the observed object deallocates.
    • Note: nested key paths are not supported at this time and manual cancellation should be used; more info here
  • Support for providing an observing object that is given to the block, and automatic unregistering when this observing object deallocates. This lets you call methods on self without retaining it or dealing with a weak reference.
  • Thread-safety for the automatic deallocation. This protects against receiving messages on another thread while the object is deallocating.
  • First-class support for both Obj-C and Swift, including strong typing in the Swift API.

Examples

Swift

// Observe an object for as long as the object is alive.
_ = KVObserver(object: user, keyPath: \User.fullName) { object, _, _ in
    // `object` has the same type as `user`
    print("User's full name changed to \(object.fullName)")
}

// Convenience methods for working with the change dictionary
_ = KVObserver(object: user, keyPath: \User.fullName, options: [.old, .new]) { _, change, _ in
    // unfortunately we don't know what the type of fullName is, so change uses Any
    let old = change.old as? String
    let new = change.new as? String
    if old != new {
        print("User's full name changed to \(new ?? "nil")")
    }
}

// Unregistering can be done from within the block, even in an .initial callback
_ = KVObserver(object: user, keyPath: \User.fullName, options: [.initial]) { object, _, kvo in
    guard !object.fullName.isEmpty else { return }
    print("User's full name is \(object.fullName)")
    kvo.cancel()
}

// Or you can unregister externally
let token = KVObserver(object: user, keyPath: \User.fullName) { object, _, _ in
    print("User's full name changed to \(object.fullName)")
}
// ... sometime later ...
token.cancel()

// You can also pass an observing object and KVO will be unregistered when that object deallocates
_ = KVObserver(observer: self, object: user, keyPath: \User.fullName) { observer, object, _, _ in
    // `observer` has the same type as `self`
    observer.nameLabel.text = object.fullName
}

Objective-C

Objective-C provides all the same functionality as Swift, albeit without the strong-typing of the observer/object.

// Observe an object for as long as the object is alive.
[PMKVObserver observeObject:self.user keyPath:@"fullName" options:0
                      block:^(id  _Nonnull object, NSDictionary<NSKeyValueChangeKey,id> * _Nullable change, PMKVObserver * _Nonnull kvo) {
    NSLog(@"User's full name changed to %@", [object fullName]);
}];

// Change dictionary is provided, but without the convenience methods.
[PMKVObserver observeObject:self.user keyPath:@"fullName"
                    options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew
                      block:^(id  _Nonnull object, NSDictionary<NSKeyValueChangeKey,id> * _Nullable change, PMKVObserver * _Nonnull kvo) {
    NSString *old = change[NSKeyValueChangeOldKey];
    NSString *new = change[NSKeyValueChangeNewKey];
    if (old != new && (new == nil || ![old isEqualToString:new])) {
        NSLog(@"User's full name changed to %@", new);
    }
}];

// Unregistering and observing object support is also provided (see Swift examples).

Requirements

Installing as a framework requires a minimum of iOS 8, OS X 10.9, watchOS 2.0, or tvOS 9.0.

If you install by copying the source into your project, it should work on iOS 7 or later (iOS 6 if you remove KVObserver.swift), and OS X 10.7 or later. Please note that it has not been tested on these versions.

PMKVObserver requires Xcode 9 or higher.

Installation

After installing with any mechanism, you can use this by adding import PMKVObserver (Swift) or @import PMKVObserver; (Objective-C) to your code.

Carthage

To install using Carthage, add the following to your Cartfile:

github "postmates/PMKVObserver" ~> 4.0

CocoaPods

To install using CocoaPods, add the following to your Podfile:

pod 'PMKVObserver', '~> 4.0'

Manual Installation

You may also install manually by adding the framework to your workspace, or by adding the 3 files KVObserver.h, KVObserver.m, and (optionally) KVObserver.swift to your project.

License

Licensed under either of

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be dual licensed as above, without any additional terms or conditions.

Version History

v4.1.3 (2020-04-01)

  • Work around bug with Swift compiler 5.1.5 and above relating to properties of type Any? (#25, SR-12486).

v4.1.2 (2019-01-24)

  • Fix compatibility with Swift compiler versions other than 4.2 when observing optional values and KVO delivers an NSNull value.

v4.1.1 (2018-11-28)

  • Add .object and .keyPath properties to PMKVObserver. The .keyPath property is named .objcKeyPath in Swift.

v4.1.0 (2018-11-27)

  • Handle observing optional values better. We now make stronger guarantees about when .old and .new are non-nil.

v4.0.0 (2018-09-05)

  • Set Swift version to 4.

  • Work around Xcode 10 issue with value conversions involving Any in the KVObserver.Change object (SR-8704).

  • Handle RawRepresentable types in KVObserver.Change's .old and .new properties.

    Note: Optional RawRepresentable types (such as those produced by a path like \.foo?.bar) are only supported in Swift 4.1 and later.

v3.0.2 (2018-05-22)

  • Squelch some warnings when building with CocoaPods.

v3.0.1 (2017-09-13)

  • Set Swift version back to Swift 3.2. The new KeyPath stuff is usable from 3.2 so there's no reason to require 4. PMVKObserver will still compile with Swift 3.1, but the KeyPath APIs won't be available unless compiling with Swift 3.2.

v3.0.0 (2017-09-12)

  • Convert to Swift 4.
  • Add new initializers that use Swift 4 KeyPaths.
  • Make Change.rawDict and Change.kind non-optional.

v2.0.2 (2017-07-25)

  • Switch to an unfair lock in teardown. This uses os_unfair_lock on platforms that support it, otherwise it uses an unfair mutex.

v2.0.1 (2016-09-15)

  • Fix CocoaPods.

v2.0.0 (2016-09-08)

  • Update for Swift 3.0.

v1.0.5 (2016-09-08)

  • Update for Swift 2.3.

v1.0.4 (2016-03-02)

  • Update CocoaPods podspec to split Swift support into a subspec.

v1.0.3 (2015-01-28)

  • Add property cancelled to PMKVObserver.

v1.0.2 (2016-01-26)

  • Switch to dual-licensed as MIT or Apache 2.0.

v1.0.1 (2015-12-17)

  • Stop leaking our pthread_mutex_ts.

v1.0 (2015-12-17)

Initial release.

More Repositories

1

PMHTTP

Swift/Obj-C HTTP framework with a focus on REST and JSON
Swift
503
star
2

PMJSON

Pure Swift JSON encoding/decoding library
Swift
360
star
3

cernan

telemetry aggregation and shipping, last up the ladder
Rust
312
star
4

quantiles

Approximations to Histograms
Rust
62
star
5

configurable-hpa

Configurable Horizontal Pod Autoscaler - CRD for k8s
Go
59
star
6

hopper

A Rust mpsc with unbounded members in bounded memory
Rust
49
star
7

centersliderview-android

Re-centering slider widget for Android
Java
12
star
8

consul_mut_ex

An Elixir module for acquiring and releasing locks with Consul and other backends.
Elixir
10
star
9

sparkles-android

A Graph library which can leverage missing data points with dotted lines!
Kotlin
10
star
10

go-triton

Kinesis-backed Data Pipeline
Go
9
star
11

DLNValidation

Validation function for the format of drivers license numbers in the USA
Python
9
star
12

vault-plugin-oauth

OAuth / OIDC auth method for Hashicorp Vault
8
star
13

evans

A cernan load-generating tool
Rust
7
star
14

opencensus_ecto

Create Opencensus spans from Ecto queries
Elixir
6
star
15

avrogato-erl

An OTP application that wraps the erlavro library, while providing it with access to a schema registry. Supports both Confluent and OCF message formats.
Erlang
5
star
16

viewfinder

JavaScript
5
star
17

gdal

C++
5
star
18

salesforce-ersa

Utilities for Lightning Components in Visualforce
3
star
19

ex-stats-tracker

StatsD Client for Elixir
Elixir
3
star
20

pystatsd

Thread Safe Statsd Python Library
Python
1
star
21

triton-python

Glue Library for Postmates data processing backbone
Python
1
star
22

docs-webpack-plugin

documentation and style guide extraction for javascript classes
JavaScript
1
star