Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

https://github.com/FabrizioBrancati/Queuer

Queuer is a queue manager, built on top of OperationQueue and Dispatch (aka GCD).
https://github.com/FabrizioBrancati/Queuer

asynchronous asynchronous-tasks carthage dispatch foundation gcd grand-central-dispatch linux macos operation operationqueue queuer scheduler semaphore swift swift-package-manager synchronous-tasks task tvos watchos

Last synced: about 2 months ago
JSON representation

Queuer is a queue manager, built on top of OperationQueue and Dispatch (aka GCD).

Lists

README

        


Queuer Banner

[![Build Status](https://travis-ci.com/FabrizioBrancati/Queuer.svg?branch=master)](https://travis-ci.com/FabrizioBrancati/Queuer)
[![Coverage Status](https://coveralls.io/repos/github/FabrizioBrancati/Queuer/badge.svg?branch=master)](https://coveralls.io/github/FabrizioBrancati/Queuer?branch=master)
[![Maintainability](https://api.codeclimate.com/v1/badges/ce03faaf6abe697458ed/maintainability)](https://codeclimate.com/github/FabrizioBrancati/Queuer/maintainability)
[![Codebeat Badge](https://codebeat.co/badges/50844e60-f4f2-4f9f-a688-5ccc976b7c8c)](https://codebeat.co/projects/github-com-fabriziobrancati-queuer-master-9833cda0-af64-433d-a08a-cd0d50d6b579)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/dc5a0e93970b42308ddd333c3bcc22ce)](https://www.codacy.com/manual/FabrizioBrancati/Queuer)


[![Documentation](https://github.fabriziobrancati.com/documentation/Queuer/badge.svg)](https://github.fabriziobrancati.com/documentation/Queuer/)
[![Swift Package Manager Compatible](https://img.shields.io/badge/SPM-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager)
[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)


[![Version](https://img.shields.io/cocoapods/v/Queuer.svg?style=flat)][Documentation]
[![Language](https://img.shields.io/badge/language-Swift%205.0%20%7C%205.1-orange.svg)](https://swift.org/)
[![Platforms](https://img.shields.io/badge/platforms-iOS%20%7C%20macOS%20%7C%20tvOS%20%7C%20watchOS%20%7C%20Linux-cc9c00.svg)][Documentation]
[![License](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://github.com/FabrizioBrancati/Queuer/blob/master/LICENSE)

---


Features
Requirements
Installing
Usage
Documentation
Changelog
Communication
Contributing
Author
License

---

Features
========

Queuer is a queue manager, built on top of [OperationQueue](https://developer.apple.com/documentation/foundation/operationqueue) and [Dispatch](https://developer.apple.com/documentation/dispatch) (aka GCD).

It allows you to create any asynchronous and synchronous task easily, all managed by a queue, with just a few lines.

Here is the list of all the features:
- [x] Works on all Swift compatible platforms (even Linux)
- [x] Easy to use
- [x] Well documented (100% documented)
- [x] Well tested (100% of code coverage)
- [x] Create an operation block
- [x] Create a single operation
- [x] Create chained operations
- [x] Manage a centralized queue
- [x] Create unlimited queue
- [x] Declare how many concurrent operation a queue can handle
- [x] Create semaphores
- [x] Create and handle schedules
- [x] Automatically or manually retry an operation
- [x] Ability to restore uncompleted operations
- [ ] Improve the state restoration feature
- [ ] Throttling between each automatic operation retry
- [ ] Data layer that every operation inside an `OperationQueue` can access

Requirements
============

| **Swift** | **Xcode** | **Queuer** | **iOS** | **macOS** | **tvOS** | **watchOS** | **Linux** |
|-----------|-------------|---------------|---------|------------|-----------|-------------|-----------|
| 3.1...3.2 | 8.3...9.0 | 1.0.0...1.1.0 | 8.0+ | 10.10+ | 9.0+ | 2.0+ | ![✓] |
| 4.0 | 9.0...9.2 | 1.3.0 | 8.0+ | 10.10+ | 9.0+ | 2.0+ | ![✓] |
| 4.1 | 9.3...9.4 | 1.3.1...1.3.2 | 8.0+ | 10.10+ | 9.0+ | 2.0+ | ![✓] |
| 4.2 | 10.0...10.1 | 2.0.0...2.0.1 | 8.0+ | 10.10+ | 9.0+ | 3.0+ | ![✓] |
| 5.0...5.1 | 10.2...11.2 | 2.1.0...2.1.1 | 8.0+ | 10.10+ | 9.0+ | 3.0+ | ![✓] |

Installing
==========

See [Requirements](https://github.com/FabrizioBrancati/Queuer#requirements) section to check Swift, Xcode, Queuer and OS versions.

### Manual
- Open and build the framework from the project (**Queuer.xcodeproj**)
- Import Queuer.framework into your project
- Import the framework with ```import Queuer```
- Enjoy!

### CocoaPods
- Create a **Podfile** in your **project directory** and write into:

```ruby
platform :ios, '8.0'
xcodeproj 'Project.xcodeproj'
use_frameworks!

pod 'Queuer'
```
- Change **"Project"** with your **real project name**
- Open **Terminal**, go to your **project directory** and type: ```pod install```
- Import the framework with ```import Queuer```
- Enjoy!

### Carthage
- Create a **Cartfile** in your **project directory** and write into:

```ruby
github "FabrizioBrancati/Queuer"
```
- Open **Terminal**, go to **project directory** and type: ```carthage update```
- **Include the created Framework** in your project
- **Add Build Phase** with the following contents:

```sh
/usr/local/bin/carthage copy-frameworks
```

Add the paths to the Queuer framework under **Input Files**

```sh
$(SRCROOT)/Carthage/Build/iOS/Queuer.framework
```

Add the paths to the copied frameworks to the **Output Files**

```sh
$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/Queuer.framework
```

This script works around an [App Store submission bug](http://www.openradar.me/radar?id=6409498411401216) triggered by universal binaries and ensures that necessary bitcode-related files are copied when archiving
- **(Optional)** Add Build Phase with the following contents

```sh
/usr/local/bin/carthage outdated --xcode-warnings
```

To automatically warn you when one of your dependencies is out of date
- Import the framework with ```import Queuer```
- Enjoy!

### Swift Package Manager
- Create a **Package.swift** file in your **project directory** and write into:

```swift
// swift-tools-version:5.1
import PackageDescription

let package = Package(
name: "Project",
products: [
.executable(name: "Project", targets: ["Project"])
],
dependencies: [
.package(url: "https://github.com/FabrizioBrancati/Queuer.git", .upToNextMajor(from: "2.0.0"))
],
targets: [
.target(name: "Project", dependencies: ["Queuer"])
]
)
```
- Change **"Project"** with your **real project name**
- Open **Terminal**, go to **project directory** and type: ```swift build```
- Import the framework with ```import Queuer```
- Enjoy!

Usage
=====

- [Shared Queuer](https://github.com/FabrizioBrancati/Queuer#shared-queuer)
- [Custom Queue](https://github.com/FabrizioBrancati/Queuer#custom-queue)
- [Create an Operation Block](https://github.com/FabrizioBrancati/Queuer#create-an-operation-block)
- [Chained Operations](https://github.com/FabrizioBrancati/Queuer#chained-operations)
- [Queue States](https://github.com/FabrizioBrancati/Queuer#queue-states)
- [Asynchronous Operation](https://github.com/FabrizioBrancati/Queuer#asynchronous-operation)
- [Synchronous Operation](https://github.com/FabrizioBrancati/Queuer#synchronous-operation)
- [Automatically Retry an Operation](https://github.com/FabrizioBrancati/Queuer#automatically-retry-an-operation)
- [Manually Retry an Operation](https://github.com/FabrizioBrancati/Queuer#manually-retry-an-operation)
- [Scheduler](https://github.com/FabrizioBrancati/Queuer#scheduler)
- [Semaphore](https://github.com/FabrizioBrancati/Queuer#semaphore)
- [Queue State Restoration (Beta)](https://github.com/FabrizioBrancati/Queuer#queue-state-restoration-beta)

### Shared Queuer

```swift
Queuer.shared.addOperation(operation)
```

### Custom Queue

```swift
let queue = Queuer(name: "MyCustomQueue")
```

You can even create a queue by defining the `maxConcurrentOperationCount` and the `qualityOfService` properties:
```swift
let queue = Queuer(name: "MyCustomQueue", maxConcurrentOperationCount: Int.max, qualityOfService: .default)
```

### Create an Operation Block
You have three methods to add an `Operation` block:

- Directly on the `queue`(or `Queuer.shared`):
```swift
queue.addOperation {
/// Your task here
}
```

- Creating a `ConcurrentOperation` with a block:
```swift
let concurrentOperation = ConcurrentOperation { _ in
/// Your task here
}
queue.addOperation(concurrentOperation)
```

- Creating a `SynchronousOperation` with a block:
```swift
let synchronousOperation = SynchronousOperation { _ in
/// Your task here
}
queue.addOperation(synchronousOperation)
```

> We will see how `ConcurrentOperation` and `SynchronousOperation` works later.

### Chained Operations
Chained Operations are `Operation`s that add a dependency each other.

They follow the given array order, for example: `[A, B, C] = A -> B -> C -> completionBlock`.
```swift
let concurrentOperationA = ConcurrentOperation { _ in
/// Your task A here
}
let concurrentOperationB = ConcurrentOperation { _ in
/// Your task B here
}
queue.addChainedOperations([concurrentOperationA, concurrentOperationB]) {
/// Your completion task here
}
```

You can also add a `completionHandler` after the queue creation with:
```swift
queue.addCompletionHandler {
/// Your completion task here
}
```

### Queue States
- Cancel all `Operation`s in queue:
```swift
queue.cancelAll()
```
- Pause queue:
```swift
queue.pause()
```
> By calling `pause()` you will not be sure that every `Operation` will be paused.

If the `Operation` is already started it will not be on pause until it's a custom `Operation` that overrides `pause()` function.

- Resume queue:
```swift
queue.resume()
```
> To have a complete `pause` and `resume` states you must create a custom `Operation` that overrides `pause()` and `resume()` function.

- Wait until all `Operation`s are finished:
```swift
queue.waitUntilAllOperationsAreFinished()
```
> This function means that the queue will blocks the current thread until all `Operation`s are finished.

### Asynchronous Operation
`ConcurrentOperation` is a class created to be subclassed.
It allows synchronous and asynchronous tasks, has a pause and resume states, can be easily added to a queue and can be created with a block.

You can create your custom `ConcurrentOperation` by subclassing it.

You must override `execute()` function and call the `finish()` function inside it, when the task has finished its job to notify the queue.

For convenience it has an `init` function with a completion block:
```swift
let concurrentOperation = ConcurrentOperation { _ in
/// Your task here
}
concurrentOperation.addToQueue(queue)
```

### Synchronous Operation
There are three methods to create synchronous tasks or even queue:
- Setting `maxConcurrentOperationCount` of the queue to `1`.

By setting that property to `1` you will be sure that only one task at time will be executed.
- Using a `Semaphore` and waiting until a task has finished its job.
- Using a `SynchronousOperation`.

It's a subclass of `ConcurrentOperation` that handles synchronous tasks.

It's not awesome as it seems to be and is always better to create an asynchronous task, but some times it may be useful.

For convenience it has an `init` function with a completion block:
```swift
let synchronousOperation = SynchronousOperation { _ in
/// Your task here
}
synchronousOperation.addToQueue(queue)
```

### Automatically Retry an Operation
An `Operation` is passed to every closure, with it you can set and handle the retry feature.

By default the retry feature is disabled, to enable it simply set the `success` property to `false`. With `success` to `false` the `Operation` will retry until reaches `maximumRetries` property value. To let the `Operation` know when everything is ok, you must set `success` to `true`.

With `currentAttempt` you can know at which attempt the `Operation` is.
```swift
let concurrentOperation = ConcurrentOperation { operation in
/// Your task here
if /* Successful */ {
operation.success = true
} else {
operation.success = false
}
}
```

### Manually Retry an Operation
You can manually retry an `Operation` when you think that the execution will be successful.

An `Operation` is passed to every closure, with it you can set and handle the retry feature.

By default the manual retry feature is disabled, to enable it simply set the `manualRetry` property to `true`, you must do this outside of the execution closure. You must also set `success` to `true` or `false` to let the `Operation` know when is everything ok, like the automatic retry feature.

To let the `Operation` retry your execution closure, you have to call the `retry()` function. If the `retry()` is not called, you may block the entire queue. Be sure to call it at least `maximumRetries` times, it is not a problem if you call `retry()` more times than is needed, your execution closure will not be executed more times than the `maximumRetries` value.
```swift
let concurrentOperation = ConcurrentOperation { operation in
/// Your task here
if /* Successful */ {
operation.success = true
} else {
operation.success = false
}
}
concurrentOperation.manualRetry = true
/// Later on your code
concurrentOperation.retry()
```

### Scheduler
A `Scheduler` is a struct that uses the GDC's `DispatchSourceTimer` to create a timer that can execute functions with a specified interval and quality of service.

```swift
let schedule = Scheduler(deadline: .now(), repeating: .seconds(1)) {
/// Your task here
}
```

You can even create a `Scheduler` without the handler and set it later:
```swift
var schedule = Scheduler(deadline: .now(), repeating: .seconds(1))
schedule.setHandler {
/// Your task here.
}
```

With `timer` property you can access to all `DispatchSourceTimer` properties and functions, like `cancel()`:
```swift
schedule.timer.cancel()
```

### Semaphore
A `Semaphore` is a struct that uses the GCD's `DispatchSemaphore` to create a semaphore on the function and wait until it finish its job.

I recommend you to use a `defer { semaphore.continue() }` right after the `Semaphore` creation and `wait()` call.

```swift
let semaphore = Semaphore()
semaphore.wait()
defer { semaphore.continue() }
/// Your task here
```

You can even set a custom timeout, default is `.distantFuture`:
```swift
semaphore.wait(DispatchTime(uptimeNanoseconds: 1_000_000_000))
```

It's more useful if used inside an asynchronous task:
```swift
let concurrentOperation = ConcurrentOperation {
/// Your task here
semaphore.continue()
}
concurrentOperation.addToQueue(queue)
semaphore.wait()
```

### Queue State Restoration (Beta)
To enable the Queue Restoration feature you must use `ConcurrentOperation` with a unique (non-nil) `name` property.
Currently this feature allows you to save the current state (`OperationState`s) of your queue, like: `name`, `progress` and `dependencies`.

The `progress` property allows to save the current state of the `Operation` progress. Update it constantly during the `Operation` execution.

Call `Queuer.state(of: OperationQueue)` or `operationQueue.state()` to get the `QueueStateList` aka: Array of `OperationState`s.

It's up to you save and retrieve this list, and create the queue correctly.

Documentation
=============

Jazzy Generated [Documentation] - 100% Documented

Changelog
=========

To see what has changed in recent versions of Queuer, see the **[CHANGELOG.md](https://github.com/FabrizioBrancati/Queuer/blob/master/CHANGELOG.md)** file.

Communication
=============

- If you need help, open an issue.
- If you found a bug, open an issue.
- If you have a feature request, open an issue.
- If you want to contribute, see [Contributing](https://github.com/FabrizioBrancati/Queuer#contributing) section.

Contributing
============

See [CONTRIBUTING.md](https://github.com/FabrizioBrancati/Queuer/blob/master/.github/CONTRIBUTING.md) file.

Author
======

**Fabrizio Brancati**

[Website: https://www.fabriziobrancati.com](https://www.fabriziobrancati.com)


[Email: [email protected]](mailto:[email protected])

License
=======

Queuer is available under the MIT license. See the **[LICENSE](https://github.com/FabrizioBrancati/Queuer/blob/master/LICENSE)** file for more info.

[Documentation]: https://github.fabriziobrancati.com/documentation/Queuer/
[✓]: Resources/Check.png