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

Last update: Aug 7, 2022

Queuer Banner

Build Status Coverage Status Maintainability Codebeat Badge Codacy Badge
Documentation Swift Package Manager Compatible Carthage Compatible
Version Language Platforms License


FeaturesRequirementsInstallingUsageDocumentationChangelogCommunicationContributingAuthorLicense


Features

Queuer is a queue manager, built on top of OperationQueue and 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:

  • Works on all Swift compatible platforms (even Linux)
  • Easy to use
  • Well documented (100% documented)
  • Well tested (100% of code coverage)
  • Create an operation block
  • Create a single operation
  • Create chained operations
  • Manage a centralized queue
  • Create unlimited queue
  • Declare how many concurrent operation a queue can handle
  • Create semaphores
  • Create and handle schedules
  • Automatically or manually retry an operation
  • 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 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:

    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:

    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:

    /usr/local/bin/carthage copy-frameworks

    Add the paths to the Queuer framework under Input Files

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

    Add the paths to the copied frameworks to the Output Files

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

    This script works around an App Store submission bug triggered by universal binaries and ensures that necessary bitcode-related files are copied when archiving

  • (Optional) Add Build Phase with the following contents

    /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-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

Queuer.shared.addOperation(operation)

Custom Queue

let queue = Queuer(name: "MyCustomQueue")

You can even create a queue by defining the maxConcurrentOperationCount and the qualityOfService properties:

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):

    queue.addOperation {
        /// Your task here
    }
  • Creating a ConcurrentOperation with a block:

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

    let synchronousOperation = SynchronousOperation { _ in
        /// Your task here
    }
    queue.addOperation(synchronousOperation)

We will see how ConcurrentOperation and SynchronousOperation works later.

Chained Operations

Chained Operations are Operations that add a dependency each other.
They follow the given array order, for example: [A, B, C] = A -> B -> C -> completionBlock.

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:

queue.addCompletionHandler {
    /// Your completion task here
}

Queue States

  • Cancel all Operations in queue:

    queue.cancelAll()
  • Pause queue:

    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:

    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 Operations are finished:

    queue.waitUntilAllOperationsAreFinished()

    This function means that the queue will blocks the current thread until all Operations 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:

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:

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.

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.

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.

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

You can even create a Scheduler without the handler and set it later:

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():

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.

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

You can even set a custom timeout, default is .distantFuture:

semaphore.wait(DispatchTime(uptimeNanoseconds: 1_000_000_000))

It's more useful if used inside an asynchronous task:

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 (OperationStates) 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 OperationStates.
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 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 section.

Contributing

See CONTRIBUTING.md file.

Author

Fabrizio Brancati

Website: https://www.fabriziobrancati.com
Email: [email protected]

License

Queuer is available under the MIT license. See the LICENSE file for more info.

GitHub

https://github.com/FabrizioBrancati/Queuer
Comments
  • 1. How to run sync operations then concurrent operations in a queue?

    I have to run task A, B, C and D. Task B needs A. Tasks C and D need B.

    I would like to run a queue in this order

    1. Task A
    2. Task B (needs A)
    3. Task C and D in parallel (both need B)

    What would be the easiest way to achieve it with Queuer?

    Reviewed by Kalzem at 2019-01-23 13:42
  • 2. Queuer by CocoaPod

    Hi Fabrizio,

    I've imported your Queuer framework by CocoaPod (pod Queuer) in my own Framework that it's used in my app. When I try to do the Archive Xcode shows me this error: bitcode bundle could not be generated because '/MyFramework../Build/Products/Release-iphoneos/Queuer/Queuer.framework/Queuer' was built without full bitcode. All frameworks and dylibs for bitcode must be generated from Xcode Archive or Install build file '/MyFramework.../Build/Products/Release-iphoneos/Queuer/Queuer.framework/Queuer' for architecture armv7.

    I've tried several options searched by google but all have failed. I've downloaded the Queuer framework project and I've added to my framework and it works (without changing any parameters).

    Am I doing something wrong? It's a shame I can't use it with CocoaPods.

    Regards,

    Reviewed by nachoindenova at 2017-11-13 11:17
  • 3. How to bring persistence?

    I have scenario where i need to resume or retry certain operation after killing and reopening app.

    Ultimately how to bring persistence with (SQLite, Realm or UserDefaults) in this.

    Reviewed by SureshSc at 2018-05-21 09:59
  • 4. Feature/add group operation

    #14

    This PR introduces the class GroupOperation which can include a few ConcurrentOperations that will run in parallel. Each ConcurrentOperation is responsible for it failures and retries. Doing so doesn't change Queuer's logic at all.

    Allows for such flows: 1 -> [2A || 2B || 2C] -> 3

    Reviewed by Kalzem at 2019-02-08 12:56
  • 5. It is support NSURLUploadTask?

    Is it work in NSURLUploadTask in background/suspended state of app.

    I want to upload task execute in synchronize manner.

    Let's say 10 task in queue. It should execute in queue one by one like... 1, 2,3..etc

    Need to upload thousands of images in background.

    Please advise and suggestions are greatly appreciated.

    Reviewed by BhaveshKumbhani at 2018-12-08 03:22
  • 6. Unable to subclass due to Public Access Control restrictions

    Installed with CococaPods and running Xcode 8.3 / Swift 3.1.

    Subclassing ConcurrentOperation in my own project with import Queuer as per the README.

    On compile, getting the error:

    Cannot inherit from non-open class ConcurrentOperation outside of its defining module.

    and

    Overriding non-open instance method outside of its defining module.

    First error fixed by changing ConcurrentOperation from public to open. Must also change the overrides in the class to open to match Access Controls of Operation.

    Second error fixed by making -ConcurrentOperation.execute() open as well.

    Reviewed by rchapmanRP at 2017-08-10 21:49
  • 7. Let's muse about the semantics of the new `func finish(_ hasFailed: Bool)` call of v2.0 of Queuer

    I really love simplicity of this framework and the way it is heading. However, I'm not entirely sure I like the semantics of the new func finish(_ hasFailed: Bool) call of version 2.0 of Queuer.

    At the call side a successful finish of an operation now looks like this:

    operation.finish(false)
    

    Since the parameter name is not exposed, my mind reads "operation not finished". If I'm the only person seeing a potential pitfall here, please close the issue and let's forget about it. Otherwise, I suggest to either exposed the parameter name to make obvious what is happening:

    operation.finish(hasFailed: false)
    

    Or, what I think would be a more natural approach, change the semantics to func finish(success: Bool):

    operation.finish(success: true)
    

    Or maybe event to func finish(success: Bool = true) :

    operation.finish()
    

    I think the last option could have some benefits:

    • The most common task of just finishing an operation is the simplest option: operation.finish()
    • If a user needs a more fine grained behavior, she can add details: operation.finish(success: false)
    • A user new to the framework needs not to know, that a retry-logic event exists
    • This approach is compatible to the previous version of the framework
    Reviewed by zykloman at 2018-11-01 16:52
  • 8. The Queuer watch framework deployment target should be 3.0 or later

    When uploading an iOS / watchOS app bundle via fastlane to iTunes Connect I get the following error message from iTunes Connect:

    [17:30:23]: ERROR ITMS-90081: "This bundle is invalid. Applications built for more than one architecture require an iOS Deployment Target of 3.0 or later."
    Return status of iTunes Transporter was 1: ERROR ITMS-90081: "This bundle is invalid. Applications built for more than one architecture require an iOS Deployment Target of 3.0 or later."
    The call to the iTMSTransporter completed with a non-zero exit status: 1. This indicates a failure.
    

    Changing the WATCHOS_DEPLOYMENT_TARGET of the watch target of the Queuer framework from 2.0 to 3.0 solves this issue.

    Reviewed by zykloman at 2018-10-19 16:07
  • 9. Chained operation results

    Hello,

    I am discovering this project which looks super interesting. However, I have a question about the Queuer class and the addChainedOperations method. Is it possible for the queued operations to pass the result of their process to the next one?

    For example: Operation1 -> Copies a file in a temporary directory and returns an URL. Operation2 -> Takes an URL as input and uploads the corresponding file on a server.

    Is it possible for Operation2 to get the input from Operation1 if they are queued with addChainedOperations?

    Best regards, Nicolas

    Reviewed by LebonNic at 2021-02-04 10:21
  • 10. finish(success: Bool) does nothing to success

    Finishing a ConcurrentOperation with operation.finish(success: false) does nothing to its success property. I would expect success to be false if I finish the operation that way 🤔

    Reviewed by mradzinski at 2020-07-31 15:38
  • 11. Swift 4.2 Support

    This PR aims to:

    • [x] Add support to Xcode 10 and Swift 4.2
    • [x] Update package tools version to 4.2
    • [x] Wait for Xcode 10 GM before merging this PR
    • [x] Add a retry feature #10
    • [x] Improve Semaphore class
    • [x] Bring persistence to queues #7
    • [x] Update watchOS deplyment target to 3.0 #11
    Reviewed by FabrizioBrancati at 2018-07-14 14:37
  • 12. Operations are `isFinished=true` without me explicitly calling `operation.finish()`

    Steps to reproduce

    1. Add some ConcurrentOperations to a queue
    2. Add a completion handler to the same queue.

    Expected behavior: Completion handler gets called after the last operation in queue calls finish().

    Actual behavior: Completion handler gets called regardless if the queue operations are still running (finish() hasn't been called yet)

    Reviewed by mradzinski at 2021-04-06 02:54
  • 13. Possible to create a unviersal queue for alamofire requests?

    Just wondered if it was possible to use this Queuer to push all alamofire requests into it with different priority scales? I think i read somewhere in an issue it wasn't possible with Alamofire or something along those lines but saw a new version was brought out.

    Reviewed by liamcharmer at 2021-03-26 12:38
  • 14. Serial task

    The call of interface A returns the result and interface B is called, interface B finishes calling C, and finally synthesizes and renders. This kind of blocking doesn't seem to work.

    Reviewed by dajiangjun887 at 2021-03-16 09:25
  • 15. Verify queue length when adding multiple operations

    This PR adds extra assertions to verify the queue length when testing adding multiple operations to the same queue.

    On Linux, there seem to be a bug (#19) that does not allow the queue to become longer than one, making everything serial.

    Reviewed by kradalby at 2020-07-20 20:00
A Task Queue Class developed in Swift (by Marin Todorov)
A Task Queue Class developed in Swift (by Marin Todorov)

TaskQueue Table of Contents Intro Installation CocoaPods Carthage Simple Examples Synchronous tasks Asynchronous tasks Serial and Concurrent Tasks GCD

Jul 23, 2022
Syntactic sugar in Swift for asynchronous dispatches in Grand Central Dispatch (iOS7+ and OS X 10.9+ compatible)

Async.legacy Syntactic sugar in Swift for asynchronous dispatches in Grand Central Dispatch (GCD) Async rewritten for iOS7 and OS X 10.9 Compatibility

Jul 1, 2019
Syntactic sugar in Swift for asynchronous dispatches in Grand Central Dispatch

Async Now more than syntactic sugar for asynchronous dispatches in Grand Central Dispatch (GCD) in Swift Async sugar looks like this: Async.userInitia

Aug 6, 2022
Grand Central Dispatch simplified with swift.

GCDKit GCDKit is Grand Central Dispatch simplified with Swift. for Swift 1.2: Use version 1.0.1 for Swift 2.1 / 2.2: Use the master branch Introductio

Jul 27, 2022
Chronos is a collection of useful Grand Central Dispatch utilities
Chronos is a collection of useful Grand Central Dispatch utilities

Chronos is a collection of useful Grand Central Dispatch utilities. If you have any specific requests or ideas for new utilities, don't hesitate to create a new issue.

Jun 29, 2022
A wrapper of Grand Central Dispatch written in Swift

GCD A wrapper of Grand Central Dispatch written in Swift. Examples gcd // submit your code for asynchronous execution on a global queue with high prio

May 19, 2022
A general purpose embedded hierarchical lock manager used to build highly concurrent applications of all types. Same type of locker used in many of the large and small DBMSs in existence today.

StickyLocking StickyLocking is a general purpose embedded lock manager which allows for locking any resource hierarchy. Installable Lock modes allow f

Jun 15, 2021
Venice - Coroutines, structured concurrency and CSP for Swift on macOS and Linux.
Venice - Coroutines, structured concurrency and CSP for Swift on macOS and Linux.

Venice provides structured concurrency and CSP for Swift. Features Coroutines Coroutine cancelation Coroutine groups Channels Receive-only chan

Aug 6, 2022
Make your logic flow and data flow clean and human readable

Flow What's Flow Flow is an utility/ design pattern that help developers to write simple and readable code. There are two main concerns: Flow of opera

Jun 17, 2022
Async and concurrent versions of Swift’s forEach, map, flatMap, and compactMap APIs.

CollectionConcurrencyKit Welcome to CollectionConcurrencyKit, a lightweight Swift package that adds asynchronous and concurrent versions of the standa

Jul 23, 2022
Extensions and additions to AsyncSequence, AsyncStream and AsyncThrowingStream.

Asynchone Extensions and additions to AsyncSequence, AsyncStream and AsyncThrowingStream. Requirements iOS 15.0+ macOS 12.0+ Installation Swift Packag

Jul 22, 2022
straightforward networking and error handling with async-await and URLSession

AsyncAwaitNetworkingPlayground How To Run Just clone the project, open it and run. Some notes about AsyncAwaitNetworkingPlayground It's a straightforw

Jul 26, 2022
Slack message generator and API client, written in Swift with Result Builders and Concurrency

Slack Message Client This package provides a Swift object model for a Slack Block Kit message, as well as a Result Builder convenience interface for e

Jul 30, 2022
Automatically generate GraphQL queries and decode results into Swift objects, and also interact with arbitrary GitHub API endpoints

GitHub API and GraphQL Client This package provides a generic GitHub API client (GithubApiClient) as well as Codable-like GitHub GraphQL querying and

Aug 2, 2022
A complete set of primitives for concurrency and reactive programming on Swift
A complete set of primitives for concurrency and reactive programming on Swift

A complete set of primitives for concurrency and reactive programming on Swift 1.4.0 is the latest and greatest, but only for Swift 4.2 and 5.0 use 1.

Jan 29, 2022
GroupWork is an easy to use Swift framework that helps you orchestrate your concurrent, asynchronous functions in a clean and organized way
GroupWork is an easy to use Swift framework that helps you orchestrate your concurrent, asynchronous functions in a clean and organized way

GroupWork is an easy to use Swift framework that helps you orchestrate your concurrent, asynchronous functions in a clean and organized way. This help

Jan 29, 2022
SwiftCoroutine - Swift coroutines for iOS, macOS and Linux.
SwiftCoroutine - Swift coroutines for iOS, macOS and Linux.

Many languages, such as Kotlin, Go, JavaScript, Python, Rust, C#, C++ and others, already have coroutines support that makes the async/await pattern i

Aug 6, 2022
SwiftyTimer allows you to instantly schedule delays and repeating timers using convenient closure syntax. It's time to get rid of Objective-C cruft.

SwiftyTimer Modern Swifty API for NSTimer SwiftyTimer allows you to instantly schedule delays and repeating timers using convenient closure syntax. It

Aug 4, 2022
A demonstration for bridging between Combine and your new async functions

CombineAsyncually This is a DEMONSTRATION of how you can bridge the new async / await functionality in Swift 5.5 with Combine. There is NO WARRANTY. T

Jul 21, 2022