@@ -282,11 +282,15 @@ import SwiftUI
282
282
/// - Parameter keyPath: A key path to a specific bindable state.
283
283
/// - Returns: A new binding.
284
284
public func binding< Value: Equatable > (
285
- _ keyPath: WritableKeyPath < State , BindableState < Value > >
285
+ _ keyPath: WritableKeyPath < State , BindableState < Value > > ,
286
+ file: StaticString = #fileID,
287
+ line: UInt = #line
286
288
) -> Binding < Value > {
287
289
self . binding (
288
290
get: { $0 [ keyPath: keyPath] . wrappedValue } ,
289
- send: { . binding( . set( keyPath, $0) ) }
291
+ send: {
292
+ . binding( . set( keyPath, $0, bindableActionType: Action . self, file: file, line: line) )
293
+ }
290
294
)
291
295
}
292
296
}
@@ -325,11 +329,25 @@ public struct BindingAction<Root>: Equatable {
325
329
/// path.
326
330
public static func set< Value: Equatable > (
327
331
_ keyPath: WritableKeyPath < Root , BindableState < Value > > ,
328
- _ value: Value
332
+ _ value: Value ,
333
+ bindableActionType: Any . Type ? = nil ,
334
+ file: StaticString = #fileID,
335
+ line: UInt = #line
329
336
) -> Self {
330
- . init(
337
+ #if DEBUG
338
+ let debugger = Debugger (
339
+ value: value, bindableActionType: bindableActionType, file: file, line: line
340
+ )
341
+ let set : ( inout Root ) -> Void = {
342
+ $0 [ keyPath: keyPath] . wrappedValue = value
343
+ debugger. wasCalled = true
344
+ }
345
+ #else
346
+ let set : ( inout Root ) -> Void = { $0 [ keyPath: keyPath] . wrappedValue = value }
347
+ #endif
348
+ return . init(
331
349
keyPath: keyPath,
332
- set: { $0 [ keyPath : keyPath ] . wrappedValue = value } ,
350
+ set: set ,
333
351
value: value,
334
352
valueIsEqualTo: { $0 as? Value == value }
335
353
)
@@ -353,6 +371,49 @@ public struct BindingAction<Root>: Equatable {
353
371
) -> Bool {
354
372
keyPath == bindingAction. keyPath
355
373
}
374
+
375
+ #if DEBUG
376
+ private class Debugger < Value> {
377
+ let value : Value
378
+ let bindableActionType : Any . Type ?
379
+ let file : StaticString
380
+ let line : UInt
381
+ var wasCalled = false
382
+
383
+ init ( value: Value , bindableActionType: Any . Type ? , file: StaticString , line: UInt ) {
384
+ self . value = value
385
+ self . bindableActionType = bindableActionType
386
+ self . file = file
387
+ self . line = line
388
+ }
389
+
390
+ deinit {
391
+ guard self . wasCalled else {
392
+ let action = """
393
+ \( bindableActionType. map { " \( $0) .binding( " } ?? " \( BindingAction . self) " ) \
394
+ .set(_, \( self . value) ) \
395
+ \( bindableActionType != nil ? " ) " : " " )
396
+ """
397
+ runtimeWarning (
398
+ """
399
+ A binding action created at " %@:%d " was not handled:
400
+
401
+ Action:
402
+ %@
403
+
404
+ To fix this, invoke the " binding() " method on your feature's reducer.
405
+ """ ,
406
+ [
407
+ " \( self . file) " ,
408
+ self . line,
409
+ action,
410
+ ]
411
+ )
412
+ return
413
+ }
414
+ }
415
+ }
416
+ #endif
356
417
}
357
418
#endif
358
419
0 commit comments