Skip to content

Commit bd2e2ad

Browse files
author
Clément Le Provost
authored
[feat] Add KVO support to query classes (#339)
Added at the `AbstractQuery` level, so it’s available for both `Query` and `PlacesQuery`.
1 parent 91565f2 commit bd2e2ad

File tree

2 files changed

+65
-0
lines changed

2 files changed

+65
-0
lines changed

Source/AbstractQuery.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ import Foundation
172172
///
173173
/// + Warning: This class is not meant to be used directly. Please see `Query` or `PlacesQuery` instead.
174174
///
175+
/// ## KVO
176+
///
177+
/// Every parameter is observable via KVO under its own name.
178+
///
175179
@objc
176180
open class AbstractQuery : NSObject, NSCopying {
177181

@@ -196,11 +200,18 @@ open class AbstractQuery : NSObject, NSCopying {
196200
/// - parameter value: The parameter's value, or nill to remove it.
197201
///
198202
@objc public func setParameter(withName name: String, to value: String?) {
203+
let oldValue = parameters[name]
204+
if value != oldValue {
205+
self.willChangeValue(forKey: name)
206+
}
199207
if value == nil {
200208
parameters.removeValue(forKey: name)
201209
} else {
202210
parameters[name] = value!
203211
}
212+
if value != oldValue {
213+
self.didChangeValue(forKey: name)
214+
}
204215
}
205216

206217
/// Convenience shortcut to `parameter(withName:)` and `setParameter(withName:to:)`.

Tests/QueryTests.swift

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,60 @@ class QueryTests: XCTestCase {
9898
query["b"] = nil
9999
XCTAssertNil(query["b"])
100100
}
101+
102+
// MARK: KVO
103+
104+
func testKVO() {
105+
let query = Query()
106+
query.addObserver(self, forKeyPath: "hitsPerPage", options: [.new, .old], context: nil)
107+
query.addObserver(self, forKeyPath: "attributesToRetrieve", options: [.new, .old], context: nil)
108+
defer {
109+
query.removeObserver(self, forKeyPath: "hitsPerPage")
110+
query.removeObserver(self, forKeyPath: "attributesToRetrieve")
111+
}
112+
query.hitsPerPage = 666
113+
query.hitsPerPage = 666 // setting the same value again should not trigger any call
114+
query.hitsPerPage = nil
115+
query.attributesToRetrieve = ["abc", "xyz"]
116+
query.attributesToRetrieve = ["abc", "xyz"] // setting the same value again should not trigger any call
117+
query.attributesToRetrieve = nil
118+
XCTAssertEqual(4, iteration)
119+
}
120+
121+
/// Tracks the number of calls to `observeValue(forKeyPath:of:change:context:)`.
122+
var iteration: Int = 0
123+
124+
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
125+
iteration += 1
126+
switch iteration {
127+
case 1:
128+
XCTAssertEqual(keyPath, "hitsPerPage")
129+
let old = change![.oldKey] as? Int
130+
let new = change![.newKey] as? Int
131+
XCTAssert(nil == old)
132+
XCTAssertEqual(666, new)
133+
case 2:
134+
XCTAssertEqual(keyPath, "hitsPerPage")
135+
let old = change![.oldKey] as? Int
136+
let new = change![.newKey] as? Int
137+
XCTAssertEqual(666, old)
138+
XCTAssert(nil == new)
139+
case 3:
140+
XCTAssertEqual(keyPath, "attributesToRetrieve")
141+
let old = change![.oldKey] as? [String]
142+
let new = change![.newKey] as? [String]
143+
XCTAssert(nil == old)
144+
XCTAssertEqual(["abc", "xyz"], new!)
145+
case 4:
146+
XCTAssertEqual(keyPath, "attributesToRetrieve")
147+
let old = change![.oldKey] as? [String]
148+
let new = change![.newKey] as? [String]
149+
XCTAssertEqual(["abc", "xyz"], old!)
150+
XCTAssert(nil == new)
151+
default:
152+
XCTFail("Unexpected call to `observeValue(forKeyPath:of:change:context:)`")
153+
}
154+
}
101155

102156
// MARK: High-level
103157

0 commit comments

Comments
 (0)