Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 23 additions & 51 deletions Documentation/DebuggingTechniques.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,66 +15,37 @@ Below is an example of type-error scenario:

```swift
SignalProducer<Int, NoError>(value:42)
.on(next: { answer in
.on(value: { answer in
return _
})
.startWithCompleted {
print("Completed.")
}
```

The code above will not compile with the following error on a `print` call `error: ambiguous reference to member 'print'
print("Completed.")`. To find the actual compile error, the chain needs to be broken apart. Add explicit definitions of closure types on each of the steps:
The code above will not compile with the following error on the `.startWithCompleted` call `error: cannot convert value of type 'Disposable' to closure result type '()'. To find the actual compile error, the chain needs to be broken apart. Add explicit definitions of closure types on each of the steps:

```swift
let initialProducer = SignalProducer<Int, NoError>.init(value:42)
let sideEffectProducer = initialProducer.on(next: { (answer: Int) in
let sideEffectProducer = initialProducer.on(value: { (answer: Int) in
return _
})
let disposable = sideEffectProducer.startWithCompleted {
print("Completed.")
}
```

The code above will not compile too, but with the error `error: cannot convert value of type '(_) -> _' to expected argument type '(Int -> ())?'` on definition of `on` closure. This gives enough of information to locate unexpected `return _` since `on` closure should not have any return value.

#### Binding `DynamicProperty` with `<~` operator

Using the `<~` operator to bind a `Signal` or a `SignalProducer` to a `DynamicProperty` can result in unexpected compiler errors.

Below is an example of this scenario:

```swift
let label = UILabel()
let property = MutableProperty<String>("")

DynamicProperty(object: label, keyPath: "text") <~ property.producer
```

This will often result in a compiler error:

> error: binary operator '<~' cannot be applied to operands of type 'DynamicProperty' and 'SignalProducer<String, NoError>'
DynamicProperty(object: label, keyPath: "text") <~ property.producer

The reason is a limitation in the swift type checker - A `DynamicProperty` always has a type of `AnyObject?`, but the `<~` operator requires the values of both sides to have the same type, so the right side value would have to be `AnyObject?` as well, but usually a more concrete type is used (in this example `String`).

Usually, the fix is as easy as adding a `.map{ $0 }`.

```swift
DynamicProperty(object: label, keyPath: "text") <~ property.producer.map { $0 }
```

This allows the type checker to infer that `String` can be converted to `AnyProperty?` and thus, the binding succeeds.
The code above will not compile too, but with the error `error: cannot convert value of type '(Int) -> _' to expected argument type '((Int) -> Void)?'` on definition of `on` closure. This gives enough of information to locate unexpected `return _` since `on` closure should not have any return value.

#### Debugging event streams

As mentioned in the README, stream debugging can be quite difficut and tedious, so we provide the `logEvents` operator. In its simplest form:

```swift
let searchString = textField.rac_textSignal()
.toSignalProducer()
.map { text in text as! String }
.throttle(0.5, onScheduler: QueueScheduler.mainQueueScheduler)
let property = MutableProperty<String>("")
...
let searchString = property.producer
.throttle(0.5, on: QueueScheduler.main)
.logEvents()
```

Expand All @@ -98,31 +69,32 @@ func debugLog(identifier: String, event: String, fileName: String, functionName:
You would then:

```swift
let searchString = textField.rac_textSignal()
.toSignalProducer()
.map { text in text as! String }
.throttle(0.5, onScheduler: QueueScheduler.mainQueueScheduler)
let property = MutableProperty<String>("")
...
let searchString = property.producer
.throttle(0.5, on: QueueScheduler.main)
.logEvents(logger: debugLog)
```

We also provide the `identifier` parameter. This is useful when you are debugging multiple streams and you don't want to get lost:

```swift
let searchString = textField.rac_textSignal()
.toSignalProducer()
.map { text in text as! String }
.throttle(0.5, onScheduler: QueueScheduler.mainQueueScheduler)
let property = MutableProperty<String>("")
...
let searchString = property.producer
.throttle(0.5, on: QueueScheduler.main)
.logEvents(identifier: "✨My awesome stream ✨")
```

There also cases, specially with [hot signals][[Signals]], when there is simply too much output. For those, you can specify which events you are interested in:
There also cases, specially with [hot signals][Signal], when there is simply too much output. For those, you can specify which events you are interested in:

```swift
let searchString = textField.rac_textSignal()
.toSignalProducer()
.map { text in text as! String }
.throttle(0.5, onScheduler: QueueScheduler.mainQueueScheduler)
.logEvents(events:[.Disposed]) // This will happen when the `UITextField` is released
let property = MutableProperty<String>("")
...
let searchString = property.producer
.throttle(0.5, on: QueueScheduler.main)
.logEvents(events: [.disposed]) // This will happen when `property` is released
```

[Signal]: ../Sources/Signal.swift