How to use Combine framework NSObject.KeyValueObservingPublisher? - ios

I'm trying to use the Combine framework NSObject.KeyValueObservingPublisher. I can see how to produce this publisher by calling publisher(for:options:) on an NSObject. But I'm having two problems:
I can include .old in the options, but no .old value ever arrives. The only values that appear are the .initial value (when we subscribe) and the .new value (each time the observed property changes). I can suppress the .initial value but I can't suppress the .new value or add the .old value.
If the options are [.initial, .new] (the default), I see no way to distinguish whether the value I'm receiving is .initial or .new. With "real" KVO I get an NSKeyValueChangeKey or an NSKeyValueObservedChange that tells me what I'm getting. But with the Combine publisher, I don't. I just get unmarked values.
It seems to me that these limitations make this publisher all but unusable except in the very simplest cases. Are there any workarounds?

I don't have much to add to TylerTheCompiler's answer, but I want to note a few things:
NSObject.KeyValueObservingPublisher doesn't use the change dictionary internally. It always uses the key path to get the value of the property.
If you pass .prior, the publisher will publish both the before and the after values, separately, each time the property changes. This is due to how KVO is implemented by Objective-C. It's not specific to KeyValueObservingPublisher.
A shorter way to get the before and after values of the property is by using the scan operator:
extension Publisher {
func withPriorValue() -> AnyPublisher<(prior: Output?, new: Output), Failure> {
return self
.scan((prior: Output?.none, new: Output?.none)) { (prior: $0.new, new: $1) }
.map { (prior: $0.0, new: $0.1!) }
.eraseToAnyPublisher()
}
}
If you also use .initial, then the first output of withPriorValue will be be (prior: nil, new: currentValue).

For getting the old value, the only workaround I was able to find was to use .prior instead of .old, which causes the publisher to emit the current value of the property before it is changed, and then combine that value with the next emission (which is the new value of the property) using collect(2).
For determining what's an initial value vs. a new value, the only workaround I found was to use first() on the publisher.
I then merged these two publishers and wrapped it all up in a nice little function that spits out a custom KeyValueObservation enum that lets you easily determine whether it's an initial value or not, and also gives you the old value if it's not an initial value.
Full example code is below. Just create a brand new single-view project in Xcode and replace the contents of ViewController.swift with everything below:
import UIKit
import Combine
/// The type of value published from a publisher created from
/// `NSObject.keyValueObservationPublisher(for:)`. Represents either an
/// initial KVO observation or a non-initial KVO observation.
enum KeyValueObservation<T> {
case initial(T)
case notInitial(old: T, new: T)
/// Sets self to `.initial` if there is exactly one element in the array.
/// Sets self to `.notInitial` if there are two or more elements in the array.
/// Otherwise, the initializer fails.
///
/// - Parameter values: An array of values to initialize with.
init?(_ values: [T]) {
if values.count == 1, let value = values.first {
self = .initial(value)
} else if let old = values.first, let new = values.last {
self = .notInitial(old: old, new: new)
} else {
return nil
}
}
}
extension NSObjectProtocol where Self: NSObject {
/// Publishes `KeyValueObservation` values when the value identified
/// by a KVO-compliant keypath changes.
///
/// - Parameter keyPath: The keypath of the property to publish.
/// - Returns: A publisher that emits `KeyValueObservation` elements each
/// time the property’s value changes.
func keyValueObservationPublisher<Value>(for keyPath: KeyPath<Self, Value>)
-> AnyPublisher<KeyValueObservation<Value>, Never> {
// Gets a built-in KVO publisher for the property at `keyPath`.
//
// We specify all the options here so that we get the most information
// from the observation as possible.
//
// We especially need `.prior`, which makes it so the publisher fires
// the previous value right before any new value is set to the property.
//
// `.old` doesn't seem to make any difference, but I'm including it
// here anyway for no particular reason.
let kvoPublisher = publisher(for: keyPath,
options: [.initial, .new, .old, .prior])
// Makes a publisher for just the initial value of the property.
//
// Since we specified `.initial` above, the first published value will
// always be the initial value, so we use `first()`.
//
// We then map this value to a `KeyValueObservation`, which in this case
// is `KeyValueObservation.initial` (see the initializer of
// `KeyValueObservation` for why).
let publisherOfInitialValue = kvoPublisher
.first()
.compactMap { KeyValueObservation([$0]) }
// Makes a publisher for every non-initial value of the property.
//
// Since we specified `.initial` above, the first published value will
// always be the initial value, so we ignore that value using
// `dropFirst()`.
//
// Then, after the first value is ignored, we wait to collect two values
// so that we have an "old" and a "new" value for our
// `KeyValueObservation`. This works because we specified `.prior` above,
// which causes the publisher to emit the value of the property
// _right before_ it is set to a new value. This value becomes our "old"
// value, and the next value emitted becomes the "new" value.
// The `collect(2)` function puts the old and new values into an array,
// with the old value being the first value and the new value being the
// second value.
//
// We then map this array to a `KeyValueObservation`, which in this case
// is `KeyValueObservation.notInitial` (see the initializer of
// `KeyValueObservation` for why).
let publisherOfTheRestOfTheValues = kvoPublisher
.dropFirst()
.collect(2)
.compactMap { KeyValueObservation($0) }
// Finally, merge the two publishers we created above
// and erase to `AnyPublisher`.
return publisherOfInitialValue
.merge(with: publisherOfTheRestOfTheValues)
.eraseToAnyPublisher()
}
}
class ViewController: UIViewController {
/// The property we want to observe using our KVO publisher.
///
/// Note that we need to make this visible to Objective-C with `#objc` and
/// to make it work with KVO using `dynamic`, which means the type of this
/// property must be representable in Objective-C. This one works because it's
/// a `String`, which has an Objective-C counterpart, `NSString *`.
#objc dynamic private var myProperty: String?
/// The thing we have to hold on to to cancel any further publications of any
/// changes to the above property when using something like `sink`, as shown
/// below in `viewDidLoad`.
private var cancelToken: AnyCancellable?
override func viewDidLoad() {
super.viewDidLoad()
// Before this call to `sink` even finishes, the closure is executed with
// a value of `KeyValueObservation.initial`.
// This prints: `Initial value of myProperty: nil` to the console.
cancelToken = keyValueObservationPublisher(for: \.myProperty).sink {
switch $0 {
case .initial(let value):
print("Initial value of myProperty: \(value?.quoted ?? "nil")")
case .notInitial(let oldValue, let newValue):
let oldString = oldValue?.quoted ?? "nil"
let newString = newValue?.quoted ?? "nil"
print("myProperty did change from \(oldString) to \(newString)")
}
}
// This prints:
// `myProperty did change from nil to "First value"`
myProperty = "First value"
// This prints:
// `myProperty did change from "First value" to "Second value"`
myProperty = "Second value"
// This prints:
// `myProperty did change from "Second value" to "Third value"`
myProperty = "Third value"
// This prints:
// `myProperty did change from "Third value" to nil`
myProperty = nil
}
}
extension String {
/// Ignore this. This is just used to make the example output above prettier.
var quoted: String { "\"\(self)\"" }
}

Related

Computed property not updating with mutating function values

I seem to have a very perplexing Swift problem.
I have a struct called "Graph" which has a number of mutating functions that change its stored properties. The struct's initializer reads from a text file and saves the data in various stored properties.
struct Graph {
var dists: [[Int]]
.
.
.
func tourValue() -> Int {
return dists.count
}
mutating func swapHeuristic() {
dists = [[0], [1]]
}
mutating func twoOpt() {
dists = [[1]]
}
.
.
.
}
I also have a function makeFile() which creates a text file for the graph to read from.
The main issues lie with my ContentView.swift file, where I create and use instances of Graph.
ContentView.swift
import SwiftUI
struct ContentView: View {
var nodes: Int
var graph: Graph { // Graph object is initialized and stored perfectly
set {
makeFile(nodes: nodes, selection: .Euclidean) // this works perfectly
}
get {
Graph(flag: -1, filename: "\(nodes)nodesEUCLIDEAN.txt") // this works perfectly
}
}
var originalTour: Double {
graph.tourValue() // saves the original tour value perfectly
}
var swapValue: Double {
set {
graph.swapHeuristic() // does not change Graph's dists property like it should
}
get {
return graph.tourValue() // as a result of dists not changing, the tourValue is also unchanged
}
}
var body: some View {
Text("Hello, World!")
}
}
Thus, the initialized graph instance never has its property changed, and I can't derive values from it.
How do I solve this?
Your graph variable is computed, so unless its mutated form is reflected somewhere else (other objects/properties, a file, etc) the mutation is inevitably lost.
When you set swapValue = 0, first the getter for var graph is called, creating a new Graph instance. Then that instance executes swapHeuristic(). Since that function mutates the value, it triggers the setter for var graph. The serious red flag here is that your setter ignores newValue. newValue in the setter is at that point the only piece of your code which has the mutated form of your graph. But it does nothing with that value, so when the setter finishes it's simply deallocated.
The question is already answered, but in attempt to be helpful, let me come at the same point from a slightly different direction.
The line
var graph: Graph { // Graph object is initialized and stored perfectly
is a form of self delusion. There is no Graph object that is initialized and stored. This is, as you've been told, a computed property. So consider it in full:
var graph: Graph { // Graph object is initialized and stored perfectly
set {
makeFile(nodes: nodes, selection: .Euclidean) // this works perfectly
}
get {
Graph(flag: -1, filename: "\(nodes)nodesEUCLIDEAN.txt") // this works perfectly
}
}
What we see in the get part (the getter) is not the retrieval of a stored Graph. It is the creation of a new graph. Every single time we ask for graph, a completely new and different Graph is created at that moment and passed out to the caller.
Thus there is no existing object to mutate. The line graph.swapHeuristic() causes a completely new Graph to come into existence, calls the method, and throws that Graph away.

self captured by a closure before all members were initialized - but I did initialize them

This is a toy example but it reduces exactly the situation I'm in:
class MyDataSource: UITableViewDiffableDataSource<String,String> {
var string : String?
init(string:String?) {
self.string = string
super.init(tableView: UITableView()) { (_, _, _) -> UITableViewCell? in
print(self.string) // error
return nil
}
}
}
I'm trying to make my table view data source self-contained, and my way of doing that (so far) is to subclass UITableViewDiffableDataSource. This works fine except when I try to give my subclass a custom initializer. The toy example shows the problem.
The way I want to populate the cell absolutely depends upon a value that can change later in the life of the data source. Therefore it cannot be hard-coded into the cell provider function. I cannot refer here simply to string, the value that was passed in the initializer; I must refer to self.string because other code is going to have the power to change this data source's string instance property later on, and I want the cell provider to use that new value when that happens.
However, I'm getting the error "self captured by a closure before all members were initialized". That seems unfair. I did initialize my string instance property before calling super.init. So it does have a value at the earliest moment when the cell provider method can possibly be called.
While I'm not entirely sure why Swift doesn't allow this (something to do with capturing self to create the closure before the actual call to super.init is made), I do at least know of a workaround for it. Capture a weak local variable instead, and after the call to super.init set that local variable to self:
class MyDataSource: UITableViewDiffableDataSource<String,String> {
var string : String?
init(string:String?) {
self.string = string
weak var selfWorkaround: MyDataSource?
super.init(tableView: UITableView()) { (_, _, _) -> UITableViewCell? in
print(selfWorkaround?.string)
return nil
}
selfWorkaround = self
}
}
The only problem with this, though, is that if the closure is executed during the call to super.init, then selfWorkaround would be nil inside the closure and you may get unexpected results. (In this case, though, I don't think it is - so you should be safe to do this.)
Edit: The reason we make the local variable weak is to prevent the self object from being leaked.
you can access self via tableView.datasource and it will sort most of the problem.
Expanding on Abhiraj Kumar's answer from Feb 16 2020, here's an example of using the TableView provided to "reach back" to get the data source you attached to the table... i.e. "self":
class MyDataSource: UITableViewDiffableDataSource<String,String> {
var string : String?
init(string:String?) {
self.string = string
super.init(tableView: UITableView()) { (tableView, _, _) -> UITableViewCell? in
// Very sketchy reach-through to get "self", forced by API design where
// super.init() requires closure as a parameter
let hack_self = tableView.dataSource! as! MyDataSource
let selfDotStr = hack_self.string
print("In closure, self.string is \(selfDotStr)")
return nil // would return a real cell here in real application
}
}
}

Multiple bindings and disposal using "Boxing"-style

This is a very specific and long question, but I'm not smart enough to figure it out by myself..
I was very intrigued by this YouTube-video from raywenderlich.com, which uses the "boxing" method to observe a value.
Their Box looks like this:
class Box<T> {
typealias Listener = T -> Void
var listener: Listener?
var value: T {
didSet {
listener?(value)
}
init(_ value: T) {
self.value = value
}
func bind(listener: Listener?) {
self.listener = listener
listener?(value)
}
}
It's apparent that only one listener is allowed per "box".
let bindable:Box<String> = Box("Some text")
bindable.bind { (text) in
//This will only be called once (from initial setup)
}
bindable.bind { (text) in
// - because this listener has replaced it. This one will be called when value changes.
}
Whenever a bind like this is set up, the previous binds would be disposed, because Box replaces the listener with the new listener.
I need to be able to observe the same value from different places. I have reworked the Box like this:
class Box<T> {
typealias Listener = (T) -> Void
var listeners:[Listener?] = []
var value:T{
didSet{
listeners.forEach({$0?(value)})
}
}
init(_ value:T){
self.value = value
}
func bind(listener:Listener?){
self.listeners.append(listener)
listener?(value)
}
}
However - this is also giving me trouble, obviously.. There are places where I want the new binding to remove the old binding. For example, if I observe a value in an object from a reusable UITableViewCell, it will be bound several times when scrolling. I now need a controlled way to dispose specific bindings.
I attempted to solve this by adding this function to Box:
func freshBind(listener:Listener?){
self.listeners.removeAll()
self.bind(listener)
}
This worked in a way, I could now use freshBind({}) whenever I wanted to remove the old listeners, but this isn't exactly what I want either. I'd have to use this when observing a value from UITableViewCells, but I also need to observe the same value from elsewhere. As soon as a cell was reused, I removed the old observers as well as the other observers I needed.
I am now confident that I need a way to retain a disposable object wherever I would later want to dispose them.
I'm not smart enough to solve this on my own, so I need help.
I've barely used some of the reactive-programming frameworks out there (like ReactiveCocoa), and I now understand why their subscriptions return Disposable objects which I have to retain and dispose of when I need. I need this functionality.
What I want is this: func bind(listener:Listener?)->Disposable{}
My plan was to create a class named Disposable which contained the (optional) listener, and turn listeners:[Listener?] into listeners:[Disposable], but I ran into problems with <T>..
Cannot convert value of type 'Box<String?>.Disposable' to expected argument type 'Box<Any>.Disposable'
Any smart suggestions?
The way I like to solve this problem is to give each observer a unique identifier (like a UUID) and use that to allow the Disposable to remove the observer when it's time. For example:
import Foundation
// A Disposable holds a `dispose` closure and calls it when it is released
class Disposable {
let dispose: () -> Void
init(_ dispose: #escaping () -> Void) { self.dispose = dispose }
deinit { dispose() }
}
class Box<T> {
typealias Listener = (T) -> Void
// Replace your array with a dictionary mapping
// I also made the Observer method mandatory. I don't believe it makes
// sense for it to be optional. I also made it private.
private var listeners: [UUID: Listener] = [:]
var value: T {
didSet {
listeners.values.forEach { $0(value) }
}
}
init(_ value: T){
self.value = value
}
// Now return a Disposable. You'll get a warning if you fail
// to retain it (and it will immediately be destroyed)
func bind(listener: #escaping Listener) -> Disposable {
// UUID is a nice way to create a unique identifier; that's what it's for
let identifier = UUID()
// Keep track of it
self.listeners[identifier] = listener
listener(value)
// And create a Disposable to clean it up later. The Disposable
// doesn't have to know anything about T.
// Note that Disposable has a strong referene to the Box
// This means the Box can't go away until the last observer has been removed
return Disposable { self.listeners.removeValue(forKey: identifier) }
}
}
let b = Box(10)
var disposer: Disposable? = b.bind(listener: { x in print(x)})
b.value = 5
disposer = nil
b.value = 1
// Prints:
// 10
// 5
// (Doesn't print 1)
This can be nicely expanded to concepts like DisposeBag:
// Nothing but an array of Disposables.
class DisposeBag {
private var disposables: [Disposable] = []
func append(_ disposable: Disposable) { disposables.append(disposable) }
}
extension Disposable {
func disposed(by bag: DisposeBag) {
bag.append(self)
}
}
var disposeBag: DisposeBag? = DisposeBag()
b.bind { x in print("bag: \(x)") }
.disposed(by: disposeBag!)
b.value = 100
disposeBag = nil
b.value = 500
// Prints:
// bag: 1
// bag: 100
// (Doesn't print "bag: 500")
It's nice to build some of these things yourself so you get how they work, but for serious projects this often is not really sufficient. The main problem is that it isn't thread-safe, so if something disposes on a background thread, you may corrupt the entire system. You can of course make it thread-safe, and it's not that difficult.
But then you realize you really want to compose listeners. You want a listener that watches another listener and transforms it. So you build map. And then you realize you want to filter cases where the value was set to its old value, so you build a "only send me distinct values" and then you realize you want filter to help you there, and then....
you realize you're rebuilding RxSwift.
This isn't a reason to avoid writing your own at all, and RxSwift includes a ton of features (probably too many features) that many projects never need, but as you go along you should keep asking yourself "should I just switch to RxSwift now?"

Does "let _ = ..." (let underscore equal) have any use in Swift?

Does using let _ = ... have any purpose at all?
I've seen question and answers for What's the _ underscore representative of in Swift References? and I know that the underscore can be used to represent a variable that isn't needed.
This would make sense if I only needed one value of a tuple as in the example from the above link:
let (result, _) = someFunctionThatReturnsATuple()
However, I recently came across this code:
do {
let _ = try DB.run( table.create(ifNotExists: true) {t in
t.column(teamId, primaryKey: true)
t.column(city)
t.column(nickName)
t.column(abbreviation)
})
} catch _ {
// Error throw if table already exists
}
I don't get any compiler warnings or errors if I just remove the let _ =. It seems to me like this is simpler and more readable.
try DB.run( table.create(ifNotExists: true) {t in
t.column(teamId, primaryKey: true)
t.column(city)
t.column(nickName)
t.column(abbreviation)
})
The author of the code has written a book and a blog about Swift. I know that authors aren't infallible, but it made me wonder if there is something I am missing.
You will get a compiler warning if the method has been marked with a warn_unused_result from the developer documentation:
Apply this attribute to a method or function declaration to have the compiler emit a warning when the method or function is called without using its result.
You can use this attribute to provide a warning message about incorrect usage of a nonmutating method that has a mutating counterpart.
Using let _ = ... specifically tells the compiler that you know that the expression on the right returns a value but that you don't care about it.
In instances where the method has been marked with warn_unused_result, if you don't use the underscore then the compiler will complain with a warning. (Because in some cases it could be an error to not use the return value.)
Sometimes it is simple and cleaner to use try? than do-catch, when you call something that throws, but decided not to handle any errors. If you leave call with try? as-is, compiler will warn you about unused result, which is not good. So you can discard results using _.
Example:
let _ = try? NSFileManager.defaultManager().moveItemAtURL(url1, toURL: url2)
Also, let _ = or _ = can be used when right side of expression is lazy variable and you want it calculated right now, but have no use for the value of this variable yet.
A lazy stored property is a property whose initial value is not
calculated until the first time it is used. You indicate a lazy stored
property by writing the lazy modifier before its declaration.
Lazy properties are useful when the initial value for a property is
dependent on outside factors whose values are not known until after an
instance’s initialization is complete. Lazy properties are also useful
when the initial value for a property requires complex or
computationally expensive setup that should not be performed unless or
until it is needed.
Example:
final class Example {
private func deepThink() -> Int {
// 7.5 million years of thinking
return 42
}
private(set) lazy var answerToTheUltimateQuestionOfLifeTheUniverseAndEverything: Int = deepThink()
func prepareTheAnswer() {
_ = answerToTheUltimateQuestionOfLifeTheUniverseAndEverything
}
func findTheQuestion() -> (() -> Int) {
// 10 millions of thinking
let theQuestion = {
// something
return self.answerToTheUltimateQuestionOfLifeTheUniverseAndEverything
}
return theQuestion
}
}
let example = Example()
// And you *want* to get the answer calculated first, but have no use for it until you get the question
example.prepareTheAnswer()
let question = example.findTheQuestion()
question()
It's also useful to print() something in SwiftUI.
struct ContentView: View {
var body: some View {
let _ = print("View was refreshed")
Text("Hi")
}
}
View was refreshed
You can use it to see the current value of properties:
struct ContentView: View {
#State var currentValue = 0
var body: some View {
let _ = print("View was refreshed, currentValue is \(currentValue)")
Button(action: {
currentValue += 1
}) {
Text("Increment value")
}
}
}
View was refreshed, currentValue is 0
View was refreshed, currentValue is 1
View was refreshed, currentValue is 2
View was refreshed, currentValue is 3
View was refreshed, currentValue is 4
If you just did _ = print(...), it won't work:
struct ContentView: View {
var body: some View {
_ = print("View was refreshed") /// error!
Text("Hi")
}
}
Type '()' cannot conform to 'View'; only struct/enum/class types can conform to protocols
You can user also #discardableResult in your own functions if sometimes you don't need the result.
#discardableResult
func someFunction() -> String {
}
someFunction() // Xcode will not complain in this case
if let _ = something {
...
}
is equal to
if something != nil {
...
}
If you replaced the underscore with a property name, the fix that Xcode would suggest is the second option (it would not suggest the first). And that's because the second option makes more programming sense. However—and this is something Apple themselves stress to developers—write the code that is the most readable. And I suspect the underscore option exists because in some cases it may read better than the other.

initializing class properties before use in Swift/iOS

I'm having trouble grasping the proper way of instantiating variables that always need to be set before an object is fully functional but may need to be instantiated after the constructor. Based on Swift's other conventions and restrictions it seems like there is a design pattern I'm unaware of.
Here is my use case:
I have a class that inherits from UIViewController and will programmatically create views based on user actions
I need to attach these views to this class, but to do so I need to retrieve their content based on configuration data supplied by another controller
I don't care if this configuration data is passed to the constructor (in which case it would always be required) or supplied by a secondary call to this object before it is used
My problem seems to be that both of the approaches in bullet 3 seem flawed.
In the first case, there is only one legitimate constructor this class can be called with, yet I'm forced to override other constructors and initialize member variables with fake values even if the other constructors are never intended to be used (I'm also trying to keep these variables as let types based on Swift's best practices).
In the second case, I'm effectively splitting my constructor into two parts and introduce an additional point of failure in case the second part fails to be called prior to class being used. I also can't move this second part to a method that's guaranteed to be called prior to usage (such as viewDidLoad) because I still need to pass in additional arguments from the config. While I can make sure to call the initPartTwo manually, I'd prefer to have a mechanism that better groups it with the actual constructor. I can't be the first one to run into this and it seems like there is a pattern I'm not seeing to make this cleaner.
UPDATE:
I ended up going with a modified version of the pattern matt suggested:
struct Thing {
let item1: String
let item2: String
struct Config {
let item3: String
let item4: String
}
var config:Config! {
willSet {
if self.config != nil {
fatalError("tried to initialize config twice")
}
}
}
init() {
self.item1 = ...
self.item2 = ...
...
}
public func phaseTwoInit(item3: String, item4: String) {
self.item3 = item3
self.item4 = item4
...
}
}
var t = Thing()
...
t.phaseTwoInit(...)
...
// start using t
If an initial instance variable property value can't be supplied at object initialization time, the usual thing is to declare it as an Optional. That way it doesn't need to be initialized by the class's initializers (it has a value - it is nil automatically), plus your code subsequently can distinguished uninitialized (nil) from initialized (not nil).
If the Optional if an implicitly unwrapped Optional, this arrangement need have no particular effect on your code (i.e. it won't have to be peppered with unwrappings).
If your objection is that you are forced to open the door to multiple settings of this instance variable because now it must be declared with var, then close the door with a setter observer:
struct Thing {
var name:String! {
willSet {
if self.name != nil {
fatalError("tried to set name twice")
}
}
}
}
var t = Thing()
t.name = "Matt" // no problem
t.name = "Rumplestiltskin" // crash

Resources