Skip to content

Commit ef2926b

Browse files
committed
feat: Fixes for expose-observe-function
- Fix Xcode26 warnings related to redundant use of @_inheritActorContext - Fix NSObject.observe
1 parent c058a10 commit ef2926b

File tree

2 files changed

+90
-81
lines changed

2 files changed

+90
-81
lines changed

Sources/SwiftNavigation/NSObject+Observe.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,14 @@
121121
/// - Returns: A cancellation token.
122122
@discardableResult
123123
public func observe(
124-
_ tracking: @escaping @MainActor @Sendable () -> Void,
124+
_ context: @escaping @MainActor @Sendable () -> Void,
125125
onChange apply: @escaping @MainActor @Sendable () -> Void
126126
) -> ObserveToken {
127-
observe { _ in apply() }
127+
observe { _ in
128+
context()
129+
} onChange: { _ in
130+
apply()
131+
}
128132
}
129133

130134
/// Observe access to properties of an observable (or perceptible) object.
@@ -136,12 +140,12 @@
136140
/// - Returns: A cancellation token.
137141
@discardableResult
138142
public func observe(
139-
_ tracking: @escaping @MainActor @Sendable (_ transaction: UITransaction) -> Void,
143+
_ context: @escaping @MainActor @Sendable (_ transaction: UITransaction) -> Void,
140144
onChange apply: @escaping @MainActor @Sendable (_ transaction: UITransaction) -> Void
141145
) -> ObserveToken {
142146
let token = SwiftNavigation.observe { transaction in
143147
MainActor._assumeIsolated {
144-
tracking(transaction)
148+
context(transaction)
145149
}
146150
} onChange: { transaction in
147151
MainActor._assumeIsolated {

Sources/SwiftNavigation/Observe.swift

Lines changed: 82 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,10 @@ import ConcurrencyExtras
5555
/// - apply: A closure that contains properties to track.
5656
/// - Returns: A token that keeps the subscription alive. Observation is cancelled when the token
5757
/// is deallocated.
58+
@inlinable
5859
public func observe(
5960
isolation: (any Actor)? = #isolation,
60-
@_inheritActorContext _ apply: @escaping @Sendable () -> Void
61+
_ apply: @escaping @Sendable () -> Void
6162
) -> ObserveToken {
6263
observe(isolation: isolation) { _ in apply() }
6364
}
@@ -116,10 +117,11 @@ import ConcurrencyExtras
116117
/// - onChange: A closure that is triggered after some tracked property has changed
117118
/// - Returns: A token that keeps the subscription alive. Observation is cancelled when the token
118119
/// is deallocated.
120+
@inlinable
119121
public func observe(
120122
isolation: (any Actor)? = #isolation,
121-
@_inheritActorContext _ tracking: @escaping @Sendable () -> Void,
122-
@_inheritActorContext onChange apply: @escaping @Sendable () -> Void
123+
_ tracking: @escaping @Sendable () -> Void,
124+
onChange apply: @escaping @Sendable () -> Void
123125
) -> ObserveToken {
124126
observe(
125127
isolation: isolation,
@@ -137,43 +139,63 @@ import ConcurrencyExtras
137139
/// - apply: A closure that contains properties to track.
138140
/// - Returns: A token that keeps the subscription alive. Observation is cancelled when the token
139141
/// is deallocated.
142+
@inlinable
140143
public func observe(
141144
isolation: (any Actor)? = #isolation,
142-
@_inheritActorContext _ apply: @escaping @Sendable (_ transaction: UITransaction) -> Void
145+
_ apply: @escaping @Sendable (_ transaction: UITransaction) -> Void
143146
) -> ObserveToken {
144-
let actor = ActorProxy(base: isolation)
145147
return observe(
148+
isolation: isolation,
146149
apply,
147-
task: { transaction, operation in
148-
Task {
149-
await actor.perform {
150-
operation()
151-
}
152-
}
153-
}
150+
onChange: apply
154151
)
155152
}
156153

157154

158-
/// Tracks access to properties of an observable model.
159-
///
160-
/// A version of ``observe(isolation:_:)`` that is handed the current ``UITransaction``.
161-
///
162-
/// - Parameters:
163-
/// - isolation: The isolation of the observation.
164-
/// - tracking: A closure that contains properties to track.
165-
/// - onChange: A closure that is triggered after some tracked property has changed
166-
/// - Returns: A token that keeps the subscription alive. Observation is cancelled when the token
167-
/// is deallocated.
155+
/// Tracks access to properties of an observable model.
156+
///
157+
/// A version of ``observe(isolation:_:)`` that is handed the current ``UITransaction``.
158+
///
159+
/// - Parameters:
160+
/// - isolation: The isolation of the observation.
161+
/// - tracking: A closure that contains properties to track.
162+
/// - onChange: A closure that is triggered after some tracked property has changed
163+
/// - Returns: A token that keeps the subscription alive. Observation is cancelled when the token
164+
/// is deallocated.
168165
public func observe(
169166
isolation: (any Actor)? = #isolation,
170-
@_inheritActorContext _ tracking: @escaping @Sendable (UITransaction) -> Void,
171-
@_inheritActorContext onChange apply: @escaping @Sendable (_ transaction: UITransaction) -> Void
167+
_ context: @escaping @Sendable (UITransaction) -> Void,
168+
onChange apply: @escaping @Sendable (_ transaction: UITransaction) -> Void
169+
) -> ObserveToken {
170+
apply(.current)
171+
172+
return onChange(
173+
isolation: isolation,
174+
of: context,
175+
perform: apply
176+
)
177+
}
178+
179+
/// Tracks access to properties of an observable model.
180+
///
181+
/// A version of ``observe(isolation:_:onChange:)`` that is handed the current ``UITransaction``
182+
/// that doesn't have initial application of the operation. Operation block is only called on observed context change.
183+
///
184+
/// - Parameters:
185+
/// - isolation: The isolation of the observation.
186+
/// - tracking: A closure that contains properties to track.
187+
/// - onChange: A closure that is triggered after some tracked property has changed
188+
/// - Returns: A token that keeps the subscription alive. Observation is cancelled when the token
189+
/// is deallocated.
190+
public func onChange(
191+
isolation: (any Actor)? = #isolation,
192+
of context: @escaping @Sendable (UITransaction) -> Void,
193+
perform operation: @escaping @Sendable (_ transaction: UITransaction) -> Void
172194
) -> ObserveToken {
173195
let actor = ActorProxy(base: isolation)
174-
return observe(
175-
tracking,
176-
onChange: apply,
196+
return onChange(
197+
of: context,
198+
perform: operation,
177199
task: { transaction, operation in
178200
Task {
179201
await actor.perform {
@@ -207,57 +229,55 @@ func observe(
207229
Task(operation: $1)
208230
}
209231
) -> ObserveToken {
210-
let token = ObserveToken()
211-
onChange(
212-
{ [weak token] transaction in
213-
guard
214-
let token,
215-
!token.isCancelled
216-
else { return }
217-
218-
var perform: @Sendable () -> Void = { apply(transaction) }
219-
for key in transaction.storage.keys {
220-
guard let keyType = key.keyType as? any _UICustomTransactionKey.Type
221-
else { continue }
222-
func open<K: _UICustomTransactionKey>(_: K.Type) {
223-
perform = { [perform] in
224-
K.perform(value: transaction[K.self]) {
225-
perform()
226-
}
227-
}
228-
}
229-
open(keyType)
230-
}
231-
perform()
232-
},
232+
observe(
233+
apply,
234+
onChange: apply,
233235
task: task
234236
)
235-
return token
236237
}
237238

238239
func observe(
239-
_ tracking: @escaping @Sendable (_ transaction: UITransaction) -> Void,
240+
_ context: @escaping @Sendable (_ transaction: UITransaction) -> Void,
240241
onChange apply: @escaping @Sendable (_ transaction: UITransaction) -> Void,
241242
task: @escaping @Sendable (
242243
_ transaction: UITransaction,
243244
_ operation: @escaping @Sendable () -> Void
244245
) -> Void = {
245246
Task(operation: $1)
246247
}
248+
) -> ObserveToken {
249+
apply(.current)
250+
251+
return SwiftNavigation.onChange(
252+
of: context,
253+
perform: apply,
254+
task: task
255+
)
256+
}
257+
258+
func onChange(
259+
of context: @escaping @Sendable (_ transaction: UITransaction) -> Void,
260+
perform operation: @escaping @Sendable (_ transaction: UITransaction) -> Void,
261+
task: @escaping @Sendable (
262+
_ transaction: UITransaction,
263+
_ operation: @escaping @Sendable () -> Void
264+
) -> Void = {
265+
Task(operation: $1)
266+
}
247267
) -> ObserveToken {
248268
let token = ObserveToken()
249-
SwiftNavigation.onChange(
269+
SwiftNavigation.withRecursivePerceptionTracking(
250270
of: { [weak token] transaction in
251271
guard let token, !token.isCancelled else { return }
252-
tracking(transaction)
272+
context(transaction)
253273
},
254274
perform: { [weak token] transaction in
255275
guard
256276
let token,
257277
!token.isCancelled
258278
else { return }
259279

260-
var perform: @Sendable () -> Void = { apply(transaction) }
280+
var perform: @Sendable () -> Void = { operation(transaction) }
261281
for key in transaction.storage.keys {
262282
guard let keyType = key.keyType as? any _UICustomTransactionKey.Type
263283
else { continue }
@@ -277,37 +297,22 @@ func observe(
277297
return token
278298
}
279299

280-
private func onChange(
281-
_ apply: @escaping @Sendable (_ transaction: UITransaction) -> Void,
282-
task: @escaping @Sendable (
283-
_ transaction: UITransaction, _ operation: @escaping @Sendable () -> Void
284-
) -> Void
285-
) {
286-
withPerceptionTracking {
287-
apply(.current)
288-
} onChange: {
289-
task(.current) {
290-
onChange(apply, task: task)
291-
}
292-
}
293-
}
294-
295-
private func onChange(
296-
of tracking: @escaping @Sendable (_ transaction: UITransaction) -> Void,
300+
private func withRecursivePerceptionTracking(
301+
of context: @escaping @Sendable (_ transaction: UITransaction) -> Void,
297302
perform operation: @escaping @Sendable (_ transaction: UITransaction) -> Void,
298303
task: @escaping @Sendable (
299304
_ transaction: UITransaction,
300305
_ operation: @escaping @Sendable () -> Void
301306
) -> Void
302307
) {
303-
operation(.current)
304-
305308
withPerceptionTracking {
306-
tracking(.current)
309+
context(.current)
307310
} onChange: {
308311
task(.current) {
309-
onChange(
310-
of: tracking,
312+
operation(.current)
313+
314+
withRecursivePerceptionTracking(
315+
of: context,
311316
perform: operation,
312317
task: task
313318
)

0 commit comments

Comments
 (0)