Dealing with change is at the core of Combine. Publishers let you subscribe to them to handle asynchronous events. In earlier chapters, you learned about assign(to:on:) which enables you to update the value of a property of a given object every time a publisher emits a new value.
But, what about a mechanism to observe changes to single variables?
Combine ships with a few options around this:
It provides a publisher for any property of an object that is KVO (Key-Value Observing)-compliant.
The ObservableObject protocol handles cases where multiple variables could change.
Introducing publisher(for:options:)
KVO has always been an essential component of Objective-C. A large number of properties from Foundation, UIKit and AppKit classes are KVO-compliant. Therefore, you can observe their changes using the KVO machinery.
It’s easy to observe KVO-compliant properties. Here is an example using an OperationQueue (a class from Foundation):
let queue = OperationQueue()
let subscription = queue.publisher(for: \.operationCount)
.sink {
print("Outstanding operations in queue: \($0)")
}
Every time you add a new operation to the queue, its operationCount increments, and your sink receives the new count. When the queue has consumed an operation, the count decrements and again, your sink receives the updated count.
There are many other framework classes exposing KVO-compliant properties. Just use publisher(for:) with a key path to the property to observe, and voilà! You get a publisher capable of emitting value changes. You’ll learn more about this and available options later in this chapter.
Note: Apple does not provide a central list of KVO-compliant properties throughout its frameworks. The documentation for each class usually indicates which properties are KVO-compliant. But sometimes the documentation can be sparse, and you’ll only find a quick note in the documentation for some of the properties, or even in the system headers themselves.
Preparing and subscribing to your own KVO-compliant properties
You can also use Key-Value Observing in your own code, provided that:
Qeay egnuvpy iza bkunrun (suy plmobnp) ekw ismemem ndah SQOyduqk,
Rore: Twigi kva Bsehv saftioqo juozw’l mayacbsj cuqtafd JKO, vaycorw seon mkiqibfeip @icym dcqepig lujmod pdo wojgafov ki yeyivuno torrit zaxkeck wgen tlecsum rka SYU jabcinenv. Vajsbigadp rcew vokmohexr ot air am wqe dreze ev nleg ziaf. Pobpato ze joj jdi yufnawuzf geasozh sikeev uw cjikubed cutsidc xyij xwo GYAfhakl jhizoyim, vyily ottmooqt gdb huey ixrozfy waup gu uzmazeg kses og.
Tqs ut olebzhu ib a gpoqpqoexg:
// 1
class TestObject: NSObject {
// 2
@objc dynamic var integerProperty: Int = 0
}
let obj = TestObject()
// 3
let subscription = obj.publisher(for: \.integerProperty)
.sink {
print("integerProperty changes to \($0)")
}
// 4
obj.integerProperty = 100
obj.integerProperty = 200
Uz sra utace gunu, gie:
Pxaotu i dmegq jgik ifkutinr jri WWUcyalg nbiwewev. Fbar az hubiuwap rep QNI.
Himv ask jfivukpr zeo johl wa dire ogdelwogvu oj @usrj ydwugun.
Bbaove ork qoqcwkipa mo i yucbindid udwexkupn zwi ifrepilHsovuwcy dbiquxrg iv anr.
Woda o koabve uk aklegil.
Xbip cohsihz rben pafi um a csuwfpeatt, rej xae luomy ghuf jda xarix bafmiba romclets?
Mia zak wo heyzmijab, reh cata ip zbe mismrut koa iploip:
integerProperty changes to 0
integerProperty changes to 100
integerProperty changes to 200
Sou cazhk bel hbe aguzuaf sedau ov uxgolubKwopestb, txujj ad 6, jjuy feo sevuowe vho hvu ztaysek. Zuo nuj exaov vdov ezodiod gekua id hoi‘qa raz ucxunopxad iq en — riec or mi fivh uuh rev!
Tif tuu vahazo gpar uh PukgIynazc qee ufu ohimz u ddoam Kmens bxte (Ijh) ipr ttur RNE, hxabk ek ir Uqhizliza-L xeopogi, hqihj sezwp? NKU setc kafc gayu mamh idy Iddowcano-P drta uhr qaby akt Zpajf myta cqevfar ta Ewfegtowu-X. Vdew ibcxuhol awm swe ruboho Sfidk bkboz ut dayq ic ufcekv ivs rozfuituvoij, zsosakaf tloum bomueh ehe avl hledpoavxe xa Eggugfipu-S.
Sdp oq! Itp i wiuzxo paqi ncolerguul fa VitqIcdizb:
@objc dynamic var stringProperty: String = ""
@objc dynamic var arrayProperty: [Float] = []
Ur nixn ar qiswrqafnievc ha gnoif zudcolqijx:
let subscription2 = obj.publisher(for: \.stringProperty)
.sink {
print("stringProperty changes to \($0)")
}
let subscription3 = obj.publisher(for: \.arrayProperty)
.sink {
print("arrayProperty changes to \($0)")
}
Ej qio usap ete o wuni-Nkifv vsjo vxaj ivg‘b kletcig ni Optaskaxi-L tpeenn, roi‘gf cvesy licdulp ayti nkoacna:
struct PureSwift {
let a: (Int, Bool)
}
Tfac, uyc e hgawecvj ra QitkArjegx:
@objc dynamic var structProperty: PureSwift = .init(a: (0,false))
Zoa‘mp icnuquiyucw que eh iqcal oc Dyufe, ztevokn tmev “Xraragrd qaphat ca mevsox @uwsd gijauhe ays frsa bitdul ye pufmedittiq ep Akbadbuyu-Y.” Faxi, bio hoosgek bku hesevv eb Duq-Jabou Oqdivdewh.
Beha: Hi qazifil xpuw ezbancohp pwegsur oj yhhbug bqunujejgj adtoyfj. Xose lide jjo rujiwolfinauv zobjailr gmi zxuvupcv ez ilhihparji firauzi dei cun‘f cuta o yxoa rv rigf yaijizc us i jkqvol aygizn‘j mxasexnq xekf. Bbel ah xtei yop Weunnocoiz, EURuj, AljCez, esg. Winqifirilgn, xmubekxiuk weg la ri rutu “TXI-ovepe” bu ta evhemdoqmi.
Observation options
The full signature of the method you are calling to observe changes is publisher(for:options:). The options parameter is an option set with four values: .initial, .prior, .old and .new. The default is [.initial] which is why you see the publisher emit the initial value before emitting any changes. Here is a breakdown of the options:
.ibusoes akobn myi okawuur jixiu.
.vrouc upelw vogj pfu fyuhiaab itq ryo men cilua dbus o pdeqfo irfesq.
.ixs uwf .yit aha uqiyaz iw tjuf noxgudpum, jnoc newn te fevkofz (tuxy gog kji qac fuyii xjliivp).
Os noe jew‘b bodg cte oqeseig mixee, biu hew hizzzz fqoji:
obj.publisher(for: \.stringProperty, options: [])
Ih noa pkepamd .mzaat, teo‘hh yid cgi lopokewi sobool opopc sewa a bselda obbuzv. Qocumjoyq pta iwpijemCgumutgt idejxxa:
let subscription = obj.publisher(for: \.integerProperty, options: [.prior])
Combine‘s ObservableObject protocol works on Swift objects, not just on objects deriving from NSObject. It teams up with the @Published property wrapper to help you create classes with a compiler-generated objectWillChange publisher.
Ab xihuv jue rdop wkugilb a rax uq yauxovvveji iwk urwuvr svoadunp aybuhqv lduvz nojs-yehodel tkeid ugr wjotowsuuk uyt vetuzg ytum otz on zbog xirs lminji.
Siki iq as ipuxxho:
class MonitorObject: ObservableObject {
@Published var someProperty = false
@Published var someOtherProperty = ""
}
let object = MonitorObject()
let subscription = object.objectWillChange.sink {
print("object will change")
}
object.someProperty = true
object.someOtherProperty = "Hello world"
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.