# ![qloop](icon.png) QLoop
**QLoop** /'kyoo•loop/ - *n* - Declarative asynchronous operation loops
## Introduction
Here is an introduction to the [QLoop](https://github.com/quickthyme/qloop)
library, and what it does. It assumes that you already have a basic
understanding of Swift. Although, knowledge of Xcode or any other IDE will
be irrelevant to the topics covered here.
At a high level, features it provides include:
- compose asynchronous operation paths as reusable "loop" constructs
- *test-friendly* observer-pattern module favoring declarative composition
- built-in error propagation
- swiftPM compatible package
- universal module; Swift 4.2+, 5 (default)
#### More promising
If you're familiar with "promise chains", then you will no doubt feel right at
home, here. In many ways, QLoop works very much like promises: link together
asynchronous operations, propagate output and/or errors, handle exceptions
safely, allow for synchronous testing, and expose observable results.
The differences, however, are far less subtle. There's obvious stuff, such
as iteration control and operation grouping. (it is a loop, after all)
But what really makes QLoop stand out, is how well one can statically compose,
thoroughly test, inspect, and reason over complex operations with ease.
QLoop enables *declarative-reactive* development **without obfuscation** or
*callback hell*. And when compared to some similar frameworks, it is extremely
light-weight, non-intrusive, and universally cross-platform.
#### Less mocking
Testing, composition, and inspection are the three top priorities of QLoop.
Rather than make setting up the test environment more difficult, it actually
simplifies it a great deal.
Loops essentially mock themselves, given that they are naturally
empty and void of personality until both of its anchors are bound (which they
are not, by default).
Pretty much everything you need to simulate reactions for testing
purposes comes entirely built-in, saving you the trouble of writing mocks and
other such foolery.
Then there's `describeOperationPath()`, which produces human-readable,
as well as reliably-parsable, "snapshots" that can be used for diagnostic
purposes and/or for test comparisons. You can see exactly which operations
are to be called and in what order.
### Loops
![loops](loops.png)
Compose `paths` of asynchronous operation `segments`, then bind them to anchors
or wrap them up into *observable* loops. Simply decorate an entity with empty `loops`
and/or `anchors`, and implement the `onChange` and/or `onError` events.
- Observation & Delegation
Loops are circular, providing both `Input` (**delegation**) and `Output` (**observation**)
capabilities.
- Flavorless
By default, a QLoop has no bound functionality on creation. You must bestow
its behavior by binding it to paths and/or segments.
- Iteration
Loops provide **iteration**, should it be desired. By default, a loop will
run once per input set, but they can be made to run however you like, simply
by swapping out its `iterator`. There are several included out-of-the-box,
but you can also create your own in order to extend the loop's functionality.
Any iterator you wish to use with QLoop must conform to `QLoopIterating`.
- Chaining
Loops can also be connected to other `loops`, `paths`, or `segments`;
basically anything that can bind to an `anchor`.
### Paths
The default segment constructors allow them to be linked explicitly,
in a type-safe manner, but they can quickly become difficult to read
or muck around with for any practical use of chaining.
`QLPath` addresses this, by allowing you to compose a series
of segments together, in a less-violent, more readable way.
This is accomplished using *type-erasure*, but we don't have to give up
type safety completely. The *failable initializer* will return `nil` if
any segment in the chain fails to link to one of its neighboring segments.
When using paths, **you always have a way to ensure everything is correct.**
Besides, it's trivial to verify the operation chains in our unit-tests,
thanks to their ability to consistently describe themselves.
Just like with a loop, a path behaves as a bundle for its segments. They
are typically bound to a loop at some point, or combined with other paths
and/or segments to form more complex chains.
### Segments
Operation `segments` are the vehicles that drive your custom `operations`.
Segments can be run independently, or linked together in order to form any
number of complex sequences.
![segments](segments.png)
You do not subclass segments. Instead, you simply attach your asynchronous
operations to them using a contract enforced by a simple swift closure.
There are currently two types of segments to choose from:
- `QLSerialSegment` - (or `QLss`) performs a **single operation** and then moves on
- `QLParallelSegment` - (or `QLps`) performs **multiple concurrent operations**,
waiting for them all to complete before moving on.
##### Operations
Operations are your application's workers, interactors, etc. In order to attach
an operation to a segment, it must be compatible (either inately or wrapped)
with this signature:
```
( _ input: Input?,
_ completion: (_ output: Output?) -> () ) throws -> ()
```
That is to say, it must take in an `Input` of whatever type, perform whatever
operation(s), then either call the completion handler with appropriate
`Output`, or throw an error.
##### ErrorHandlers
Error handlers are optional things you can add whenever you suspect errors might
get thrown. The error handler signature looks like this:
```
( _ error: Error,
_ completion: @escaping (_ output: Output?) -> (),
_ errCompletion: @escaping (_ error: Swift.Error?) -> () ) -> ()
```
Whenever the operation throws an error, or if an error is passed via the input
anchor, then the error handler (if set) will try and handle the error. If it is
successful, then it may choose to call the `outputCompletion`. Otherwise, it
should forward the error (or generate a new one, depending) down the line by
calling `errCompletion` instead.
The error handler should *not* throw an error.
The default behavior is to just forward the error.
### Anchors
An `anchor` is what facilitates the **contract** binding segments. To bind
to an anchor essentially means to respond to its `onChange(_)` and/or
`onError(_)` events.
---
For more, please refer to the **[API Reference](reference.md)**.
There's also the **[Getting Started](getting-started.md)** guide
and a **[demo app](https://github.com/quickthyme/qloop-demo)**!